Skip to content

fix(server): configurable exit-time reap (PTY_REAP_ON_EXIT) + un-break parity + wire vitest CI - #114

Merged
myobie merged 1 commit into
mainfrom
fix/preserve-finished-sessions
Jul 21, 2026
Merged

fix(server): configurable exit-time reap (PTY_REAP_ON_EXIT) + un-break parity + wire vitest CI#114
myobie merged 1 commit into
mainfrom
fix/preserve-finished-sessions

Conversation

@myobie

@myobie myobie commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Configurable exit-time cleanup + un-break the parity contract + wire vitest CI

Fixes the parity-contract break #111 (exit-reap) caused on main, per Nathan's
call: make reap-vs-preserve-on-exit configurable rather than a flat default.

The setting

  • PTY_REAP_ON_EXIT env var — a network/global knob the daemon inherits
    (spawn.ts forwards process.env), so it can be set fleet-wide. false/0/
    no/offpreserve; unset/else → reap (shipped default; keeps feat: exit-time cleanup of dead non-permanent sessions #111's
    behavior + its author's rationale, least delta).
  • Per-session overrides (already existed, win over the config default):
    keep=true tag → force preserve (+ gc-exempt); --ephemeral → force reap.
  • Precedence in shouldReapAtExit: keep > --ephemeral > strategy=permanent

    config default. The new defaultReap param is optional (defaults to
    reapOnExitDefault()) so the re-exported 2-arg API (relay/layout/supervisors)
    keeps working and still gets the correct env-driven answer.

Un-breaks main

  • parity #1 now asserts both reference modes: preserve → final screen kept
    (idempotent peek); reap → session reaps itself, peek reports it gone.
  • exit-reap.test.ts keeps the reap-default suite and adds a preserve-mode
    (PTY_REAP_ON_EXIT=false) block. Global test setup (isolate-env.ts) scrubs
    PTY_REAP_ON_EXIT so the default is deterministic.
  • completions help one-liner (authorized): completions added to
    NON_COMMAND_CASES — it ships its own lightweight usage:, not the strict
    per-subcommand format. Fixes the pre-existing help.test.ts drift from Add pty completions <shell> generator (fish/bash/zsh, SSOT, dynamic sessions) #106.

Wires the vitest CI gate (folds in / supersedes #113)

.github/workflows/test.yml: npm ci → build → npm test on PR + push-to-main,
Node 22 (matches flake.nix). The push CI was Nix-build-only — how both the
parity break and the completions drift reached main invisibly. Installs the
test-only binaries ubuntu-latest lacks: zsh/fish (shells.test.ts) and a
no-op claude stub (restart-guardrail spawns a claude --resume session; its
guard keys purely on the argv). Closing #113 as folded in here.

Docs

README "Session lifecycle and cleanup" + CHANGELOG rewritten to describe the
configurable setting (was #111's reap-default BREAKING note).

Rust

Node keeps shouldReapAtExit semantics; rust mirrors the same PTY_REAP_ON_EXIT
knob + default. I'll relay the exact env-var name + precedence once this lands.

…k parity + wire vitest CI

Un-breaks the post-exit parity contract that #111 (exit-reap) broke on main, per
Nathan's call: make reap-vs-preserve-on-exit CONFIGURABLE rather than a fixed
default.

- PTY_REAP_ON_EXIT env knob (network/global; the daemon inherits it via
  spawn.ts) sets the default: false/0/no/off -> preserve, unset/else -> reap
  (shipped default). Per-session keep=true (force preserve) / --ephemeral
  (force reap) override it. shouldReapAtExit gains an OPTIONAL defaultReap
  param (defaults to reapOnExitDefault()) so the re-exported 2-arg API
  (relay/layout/supervisors) stays backward-compatible.
- parity #1 now asserts BOTH modes (preserve keeps the final screen; reap
  reaps). exit-reap.test.ts keeps the reap-default suite + adds a preserve-mode
  block. isolate-env.ts scrubs PTY_REAP_ON_EXIT for a deterministic default.
- help.test.ts: completions -> NON_COMMAND_CASES (fixes pre-existing #106 help
  drift; completions ships its own lightweight usage, not the strict
  per-subcommand format).
- CI: add .github/workflows/test.yml gating the vitest suite (Node 22, matches
  flake.nix) on PR + push-to-main. Installs the test-only binaries
  ubuntu-latest lacks (zsh/fish for shells.test.ts; a no-op claude stub for
  restart-guardrail). The push CI was Nix-build-only, which is how both breaks
  reached main invisibly. Supersedes #113.
- Docs: README "Session lifecycle and cleanup" + CHANGELOG rewritten for the
  configurable setting.

Full suite green: 1408 passed, 21 pre-existing env-gated skips.
@myobie
myobie merged commit 79c1267 into main Jul 21, 2026
2 checks passed
@myobie
myobie deleted the fix/preserve-finished-sessions branch July 21, 2026 10:31
myobie added a commit that referenced this pull request Jul 21, 2026
…modes) + node harness

Round-2 of the parity drive: a single language-neutral fixtures file both the
node suite and pty-rust assert against, so the two implementations cannot
silently drift. Node owns tests/fixtures/parity/screens.json; pty-rust vendors a
byte-identical mirror.

screens.json v2 covers the plain-screen seeds AND both exit-time modes now that
post-exit behavior is configurable (PTY_REAP_ON_EXIT, #114): a per-fixture env
overlay pins the mode. Seeds: idle-prompt-plain (live prompt, exact bytes),
post-exit-final-screen (PTY_REAP_ON_EXIT=false → preserved final screen,
idempotent peek, exit status), post-exit-reaped (default reap → session removes
itself, peek fails + ls omits). Harness (tests/parity-fixtures.test.ts) loads
the JSON, spawns with the fixture env, and asserts per kind. README documents
the schema + assertion rules.

JSON-shape seeds (ls-json-shape, client-count-during-peek) land next in a
sibling shapes.json.
myobie added a commit that referenced this pull request Jul 21, 2026
…hapes.json (json-shape) (#115)

* test(parity): R2 shared fixtures — canonical screens.json (both exit modes) + node harness

Round-2 of the parity drive: a single language-neutral fixtures file both the
node suite and pty-rust assert against, so the two implementations cannot
silently drift. Node owns tests/fixtures/parity/screens.json; pty-rust vendors a
byte-identical mirror.

screens.json v2 covers the plain-screen seeds AND both exit-time modes now that
post-exit behavior is configurable (PTY_REAP_ON_EXIT, #114): a per-fixture env
overlay pins the mode. Seeds: idle-prompt-plain (live prompt, exact bytes),
post-exit-final-screen (PTY_REAP_ON_EXIT=false → preserved final screen,
idempotent peek, exit status), post-exit-reaped (default reap → session removes
itself, peek fails + ls omits). Harness (tests/parity-fixtures.test.ts) loads
the JSON, spawns with the fixture env, and asserts per kind. README documents
the schema + assertion rules.

JSON-shape seeds (ls-json-shape, client-count-during-peek) land next in a
sibling shapes.json.

* test(parity): R2 json-shape fixtures — shapes.json (ls-json-shape + client-count)

Companion to screens.json for the machine-readable surfaces both suites assert.
shapes.json fixtures run a scenario via the real pty CLI then assert output
FIELD-BY-FIELD per policy ({exact} | {type} | {omitWhenUnset}), not raw bytes.

- ls-json-shape: pty list --json entry shape, running vs exited (preserve mode
  so the exited entry stays listed). status enum running|exited|vanished. pid
  policy documents the daemon-pid-vs-child-pid distinction (ls pid = daemon:
  number running / null exited; stats.process.pid = child).
- client-count-during-peek: after a transient peek, stats --json
  clients.attached === 0 (peek/stats are not attached streaming clients).

Harness tests/parity-shapes.test.ts interprets the per-field policy. All values
captured empirically against the real dist. Node owns; rust mirrors.
myobie added a commit that referenced this pull request Jul 21, 2026
…t gate (#116)

The vitest CI gate flaked once on main (#114 merge, 79c1267): screenshot.test.ts
'preserves true color (24-bit) codes' timed out at the default 5000ms under CI
load (it passed on re-run). Every test in this file spawns a real pty session
and polls the rendered screen (createSession + waitForText), so 5s is too tight
on a loaded runner — and the siblings still on the default were equally at risk.
Raise the whole file to 15000ms (matching this file's existing explicit
timeouts) so a loaded runner can't false-red a real-pty screenshot test. A flaky
required gate false-reds future PRs and erodes trust in the gate.
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.

1 participant