feat(gc): expire keep-tagged exited sessions after a max age - #173
Merged
Conversation
`keep=true` exempted a dead session from `pty gc`'s sweep forever. Agents set it on the session they are debugging right now and never come back to untag it, so the registry becomes append-only: 740 of 911 sessions on one host were exited-and-kept, and `pty gc --dry-run` could only offer to sweep 113. The exemption is now bounded. `pty gc` skips a `keep`-tagged exited/vanished session only while it has been dead for less than `--keep-max-age` (default 7d), then sweeps it and reports it under a separate `keepExpired` bucket so the reason stays visible. `0` sweeps the whole dead-keep backlog on that pass. Exit-time retention is untouched and still unconditional, and running sessions are never sweep candidates whatever their age. Age is anchored on `exitedAt`, falling back to `createdAt` for a `vanished` session that never wrote one — the same anchor precedence `pty list --older-than` uses. A record with neither timestamp never expires except under an explicit `0`. Refs schickling/dotfiles#2412, schickling/dotfiles#2408.
schickling-assistant
marked this pull request as ready for review
September 4, 2026 14:33
myobie
approved these changes
Sep 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
keep=trueexempted a dead session frompty gc's sweep forever. Agents set the tag on the session they are debugging right now (pty run --tag keep=true) and nobody ever comes back to untag it, so the registry becomes append-only. On dev3: 911 sessions, 65 live, 740 EXITED-and-kept — andpty gc --dry-runcould only offer to sweep 113. Disk is negligible; the cost is registry noise that everypty lsand every agent-facing listing pays.Implements decision Q7 (
keep=trueon an exited session is honored for N days, default 7, then swept). Refs schickling/dotfiles#2412, schickling/dotfiles#2408.Change
pty gc --keep-max-age <dur>(default7d): akeep-tagged dead session is exempt from step 3's sweep only while it has been dead for less than the window. Grammar is the existingparseDuration(Ns/Nm/Nh/Nd), plus bare0meaning "sweep every dead keep session now". A unit-less non-zero value is rejected (--keep-max-age 7is ambiguous between seconds and days) rather than guessed at.isGonegate) and still aren't, at any age or window — covered by a test.exitedAt, falling back tocreatedAtfor avanishedsession whose daemon never wrote an exit record — the same precedencepty list --older-thanuses. A record with neither timestamp never expires, except under an explicit0; retaining an unaged record is the recoverable failure.shouldReapAtExit) is untouched and still unconditional. Expiry happens only on a gc pass.GcResultgainskeepExpired: string[], disjoint fromremoved, so "the keep tag expired" is reportable apart from ordinary stale-session cleanup.--dry-runpreviews it and counts it separately (N keep-expired sessionsin the summary bar). New public exports:DEFAULT_KEEP_MAX_AGE_MS,isKeepExpired.Removed (keep expired after 7d): <name>/Would remove (keep expired after 7d): <name>; the retained line now names the countdown —Kept (keep tag): <name> — swept once dead for 7d, or remove the keep tag to reap it now.pty gc --help+ top-level usage, completions spec (src/completions.ts) with the three checked-in artifacts regenerated, README (lifecycle table, examples, sweep + auto-gc sections),docs/disk-layout.mdkeep-tag semantics, CHANGELOG under Unreleased.Gates + evidence
npm run typecheck— clean (re-run after rebase onto Make pty kill verify and finish the kill, read the process table in one place, and stop waiting to say a name is taken #170).npx vitest run tests/gc-keep-expiry.test.ts— 8/8 pass. Covers: young keep session retained; expired one swept and reported apart from an identical untagged session; custom window in both--keep-max-age 3hand--keep-max-age=1hspellings;0sweeping a just-exited session;createdAtanchoring for a record with no exit record; a running keep session surviving--keep-max-age 0(verified stillstatus=runningafterwards);--dry-run/-nnon-mutating in both default and zero windows; flag validation.npx vitest run(full suite, pre-rebase commit) — 1625 passed, 5 failed in 3 files, all environment-bound and none touching gc:tests/completions.test.ts > completes evidence modes and leaf-specific flags in bash— 5s test timeout; the bash completion probe takes ~40s on this host. Proven red at the same base with my changes stashed anddist/rebuilt: identical single failure, 41s.tests/shells.test.ts > zsh > starts up and accepts commands—execvp(3) failed.: No such file or directory;zshis not on PATH in this environment.tests/screenshot.test.tsvim + nano cases — vim reportsE1568: Terminal did not respond to DSR request for 'background' coloragainst the headless harness; fails identically when those two files run in isolation.npx vitest run tests/gc*.test.ts tests/help.test.ts tests/completions.test.ts tests/disk-layout-docs.test.ts tests/list-purity.test.ts— green except the pre-existing bash-completion timeout above. The "matches every checked-in completion artifact" parity test passes, so the regeneratedcompletions/pty.{bash,zsh,fish}are in sync.tests/gc-keep-expiry.test.ts tests/gc.test.ts tests/help.test.ts— green (plus the same pre-existing completions timeout).PTY_ROOT: 2-day-old and 30-day-old exited keep sessions → the default pass keeps the young one and reportsRemoved (keep expired after 7d): old;--keep-max-age 0sweeps both; dry runs left both files on disk;--keep-max-age 7exits 1 with the duration error.Intentionally left
pty gcrun on dev3 and no dotfiles pin bump — separate step after merge, per the work brief.keep.max-age=<dur>): nothing asks for per-session retention yet, and the global flag pluspty rmcover both directions.GcResult's doc header still says "Five buckets" (already inaccurate before this change, at 9 buckets) — left alone to keep the diff to one logical change.Posted on behalf of @schickling
agent_identitysessionagent_personaagent_supervisoragent_toolagent_tool_versionagent_runtimetooling_profile