Daily expiration sweeper for sessions and worktrees #7

Closed
opened 2026-04-16 22:44:49 +00:00 by claude-desktop · 0 comments
Collaborator

User story

As ops, I want a periodic background job that drops stale sessions and worktrees, so that a missed cleanup webhook does not let state grow forever.

Acceptance criteria

Sweeper (src/sweeper.ts)

  • Runs on setInterval at startup; default interval 6h, configurable via env var SWEEPER_INTERVAL_HOURS
  • For each entry in the sessions store: query the Forgejo API for the issue/PR state
    • If issue/PR is closed (or PR merged) → drop the session AND remove the matching worktree for every agent
    • If issue/PR is open → leave alone
  • Fallback: any session entry older than MAX_AGE_DAYS (default 30) is dropped regardless of Forgejo state, to handle deleted repos/issues
  • Worktree-only orphans (worktree exists, no session entry references it, no open issue) are also pruned
  • Forgejo API calls are throttled to ≤ 1 request per 200 ms: the sweeper iterates sessions serially in a for…of loop and await Bun.sleep(200) between API calls. No Promise.all / concurrent fan-out.

Logging

  • On each run, log a summary: swept: 3 sessions, 2 worktrees, 0 cache clones; skipped: 12 (still active)
  • Errors per entry are logged but do not abort the rest of the sweep

Tests (src/sweeper.test.ts)

  • Mock Forgejo API; verify closed-issue entries are dropped, open-issue entries are kept
  • Verify the MAX_AGE_DAYS fallback by stubbing Date.now()
  • Verify worktree-orphan pruning
  • Verify throttle: with N mocked sessions, the sweep takes ≥ (N-1) * 200 ms (use fake timers or a tolerance)

Out of scope

  • A manual on-demand sweep endpoint (covered by the admin /reset story for per-key, and is overkill for full sweep)
  • Pruning cache clones for repos with no issues — too aggressive; revisit later

References

  • Discussion: chat history, "Phase 3 — housekeeping"

Dependencies

  • Blocked by: #6 (needs sessions API to enumerate + drop)
  • Blocks: none (housekeeping leaf)
  • Branch off: main
  • Full graph: #10
## User story As **ops**, I want a periodic background job that drops stale sessions and worktrees, so that a missed cleanup webhook does not let state grow forever. ## Acceptance criteria ### Sweeper (`src/sweeper.ts`) - [ ] Runs on `setInterval` at startup; default interval `6h`, configurable via env var `SWEEPER_INTERVAL_HOURS` - [ ] For each entry in the sessions store: query the Forgejo API for the issue/PR state - If issue/PR is `closed` (or PR `merged`) → drop the session AND remove the matching worktree for every agent - If issue/PR is open → leave alone - [ ] Fallback: any session entry older than `MAX_AGE_DAYS` (default 30) is dropped regardless of Forgejo state, to handle deleted repos/issues - [ ] Worktree-only orphans (worktree exists, no session entry references it, no open issue) are also pruned - [ ] Forgejo API calls are throttled to **≤ 1 request per 200 ms**: the sweeper iterates sessions serially in a `for…of` loop and `await Bun.sleep(200)` between API calls. No `Promise.all` / concurrent fan-out. ### Logging - [ ] On each run, log a summary: `swept: 3 sessions, 2 worktrees, 0 cache clones; skipped: 12 (still active)` - [ ] Errors per entry are logged but do not abort the rest of the sweep ### Tests (`src/sweeper.test.ts`) - [ ] Mock Forgejo API; verify closed-issue entries are dropped, open-issue entries are kept - [ ] Verify the `MAX_AGE_DAYS` fallback by stubbing `Date.now()` - [ ] Verify worktree-orphan pruning - [ ] Verify throttle: with N mocked sessions, the sweep takes ≥ `(N-1) * 200 ms` (use fake timers or a tolerance) ## Out of scope - A manual on-demand sweep endpoint (covered by the admin `/reset` story for per-key, and is overkill for full sweep) - Pruning cache clones for repos with no issues — too aggressive; revisit later ## References - Discussion: chat history, "Phase 3 — housekeeping" ## Dependencies - **Blocked by:** #6 (needs sessions API to enumerate + drop) - **Blocks:** none (housekeeping leaf) - **Branch off:** `main` - **Full graph:** #10
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
charles/claude-hooks#7
No description provided.