Skip to content

feat(gc): expire keep-tagged exited sessions after a max age (parity with pty#173) - #12

Merged
schickling merged 1 commit into
mainfrom
schickling-assistant/2026-09-04-gc-keep-expiry
Sep 5, 2026
Merged

feat(gc): expire keep-tagged exited sessions after a max age (parity with pty#173)#12
schickling merged 1 commit into
mainfrom
schickling-assistant/2026-09-04-gc-keep-expiry

Conversation

@schickling-assistant

Copy link
Copy Markdown
Contributor

Problem

keep=true exempted a dead session from pty gc's sweep for ever. Agents tag the session they are debugging now and never come back to untag it, so the exemption turns the registry into an append-only log — 740 of 911 sessions on one host, which is what prompted the upstream fix (compoundingtech/pty#173, merged 86dcc5eb7).

Change

Ports #173 exactly: same flag name, default, duration grammar, and output wording, so a shell user cannot tell the two binaries apart.

  • pty gc --keep-max-age <dur> (Ns|Nm|Nh|Nd, default 7d; bare 0 sweeps every dead keep session on this pass). Both spellings, --flag V and --flag=V. A unit-less non-zero value is rejected — --keep-max-age 7 is ambiguous between seconds and days.
  • registry::is_keep_expired + DEFAULT_KEEP_MAX_AGE_MS in pty-core (crates/pty-core/src/registry/tags.rs), beside is_keep_requested. Age anchors on exitedAt, falling back to createdAt for a vanished session that never wrote one — the anchor precedence pty list --older-than already uses. A record with neither timestamp, or an unparseable one, never expires except under 0: retaining an unaged record is the recoverable failure, deleting it is not.
  • Only dead sessions are candidates. The sweep's is_gone() gate is unchanged, so a running keep session survives --keep-max-age 0. Exit-time retention in the daemon is untouched and still unconditional.
  • Two buckets, reported apart. GcResult.keep_expired is disjoint from removed, so the CLI prints Removed (keep expired after 7d): <name> (Would remove … under --dry-run) plus an N keep-expired sessions term in the footer, and the retained line now names its window: Kept (keep tag): <name> — swept once dead for 7d, or remove the keep tag to reap it now.
  • Help text (crates/pty/tests/fixtures/help/{gc,usage}.txt — vendored byte for byte from Node) and all three completion scripts carry #173's wording.
  • docs/parity.md §12 records the flag as kept, §13 records #173 as merged-and-ported; docs/conformance.md regenerated.

Gates and evidence

  • cargo test -p pty-core --test registry_tags — 6 pass, including the new keep_expiry_window (anchor precedence, unaged record, inclusive boundary, 0 sweeps everything).
  • PTY_TEST_BIN=target/debug/pty cargo test -p pty-conformance --test gc_keep_expiry8 pass, mirroring #173's eight cases 1:1 with /// node: line refs: young kept; expired swept alongside a same-age untagged session with the two footer terms distinct; both flag spellings; 0 on a just-exited session; createdAt anchoring; a running keep session surviving 0; two non-mutating dry runs then the real removal; unit-less flag rejected on stderr with exit 1.
  • cargo test -p pty --test cli_gc --test help --test completions --test cli_dispatch, and -p pty-conformance --test gc --test gc_parent_child --test exit_reap — all green (exact-stdout expectations updated for the new retained-session line).
  • End-to-end smoke on a fabricated root (young/old keep + untagged stale): Removed: stale, Removed (keep expired after 7d): old, Kept (keep tag): young — swept once dead for 7d, …, Cleaned up 1 stale session, 1 keep-expired session.; --dry-run identical with Would … and nothing removed; --keep-max-age 0 then sweeps young; --keep-max-age 7pty gc: --keep-max-age expects a duration like 12h, 7d, or 0 (got "7"), exit 1.
  • cargo test --workspace --no-fail-fast: 6 red binaries — daemon_geometry (3), daemon_lifecycle (1), daemon_roles (2), interactive_theme (1), fixtures_protocol (1) — proven pre-existing by re-running each with this branch's changes stashed: identical failures (pty daemon: the listener failed: Invalid argument (os error 22) on this host). registry_list failed once under full-suite load and passes alone both with and without the change — the load-sensitive class README.md §"Reading a single failure from a full run" describes. Everything gc/keep-related is green.
  • cargo clippy -p pty-core -p pty --all-targets names nothing in the changed code (the one gc.rs hit, == false at :572, is pre-existing and outside this diff).

Intentionally left

  • No cargo fmt. The tree has 1,080 pre-existing rustfmt deviations and no rustfmt.toml; formatting would touch ~180 unrelated files. New code follows its neighbours and rustfmt where the two agree.
  • Node cross-run not possible here. crates/pty-conformance/tests/gc_keep_expiry.rs is written to run against either binary, but the installed Node pty is 0.12.0, which predates #173. Noted in docs/parity.md §13.
  • docs/conformance.md regeneration also picks up 4 pre-existing unmapped tests the committed copy predated (648 → 660 tests, of which 8 are this PR's).
  • The dropped gc steps (permanent respawn, flapping, abandoned reap) and their --idle-days / --fast-fail-* flags stay dropped and accepted-and-ignored, per docs/parity.md §12.

Refs compoundingtech/pty#173, schickling/dotfiles#2412.

Posted on behalf of @schickling
field value
agent_identity dev3.direct.omp.v6c4mkm2
session dev3.v6c4mkm2
agent_persona generalist
agent_supervisor unavailable
agent_tool OMP
agent_tool_version 18.1.2
agent_runtime OMP 18.1.2
tooling_profile dotfiles@7534055

`keep=true` bought a dead session immortality against `pty gc`'s sweep.
Agents tag the session they are debugging right now and never come back
to untag it, so the exemption turned the registry into an append-only
log (740 of 911 sessions on one host, upstream).

`pty gc --keep-max-age <dur>` (default `7d`, `0` sweeps the dead keep
backlog on this pass) now bounds it. Only exited/vanished sessions are
candidates — a running session is never swept whatever its age. Age
anchors on `exitedAt`, falling back to `createdAt` for a `vanished`
session that never wrote one, the same precedence `pty list
--older-than` uses; a record with neither never expires except under
`0`. A unit-less non-zero value is rejected rather than guessed at,
since `--keep-max-age 7` is ambiguous between seconds and days.

Swept keep sessions are reported apart from the plain sweep — `Removed
(keep expired after 7d): <name>` and an `N keep-expired sessions` term
in the footer — so the reason is visible instead of looking like the
tag was ignored, and the retained line now names the window it is
counting down.

Port of compoundingtech/pty#173 (merged 86dcc5eb7): same flag name,
default, duration grammar, and output wording, with the help text and
completions vendored from it byte for byte.

agent-identity: dev3.direct.omp.v6c4mkm2
agent-persona: generalist
agent-supervisor: unavailable
agent-tool: OMP
agent-tool-version: 18.1.2
agent-runtime: OMP 18.1.2
tooling-profile: dotfiles@7534055
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants