fix(server): configurable exit-time reap (PTY_REAP_ON_EXIT) + un-break parity + wire vitest CI - #114
Merged
Merged
Conversation
…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
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.
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.
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'scall: make reap-vs-preserve-on-exit configurable rather than a flat default.
The setting
PTY_REAP_ON_EXITenv var — a network/global knob the daemon inherits(
spawn.tsforwardsprocess.env), so it can be set fleet-wide.false/0/no/off→ preserve; unset/else → reap (shipped default; keeps feat: exit-time cleanup of dead non-permanent sessions #111'sbehavior + its author's rationale, least delta).
keep=truetag → force preserve (+ gc-exempt);--ephemeral→ force reap.shouldReapAtExit:keep>--ephemeral>strategy=permanentUn-breaks main
parity #1now asserts both reference modes: preserve → final screen kept(idempotent peek); reap → session reaps itself, peek reports it gone.
exit-reap.test.tskeeps the reap-default suite and adds a preserve-mode(
PTY_REAP_ON_EXIT=false) block. Global test setup (isolate-env.ts) scrubsPTY_REAP_ON_EXITso the default is deterministic.completionsadded toNON_COMMAND_CASES— it ships its own lightweightusage:, not the strictper-subcommand format. Fixes the pre-existing
help.test.tsdrift from Addpty 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 teston PR + push-to-main,Node 22 (matches
flake.nix). The push CI was Nix-build-only — how both theparity break and the
completionsdrift reached main invisibly. Installs thetest-only binaries ubuntu-latest lacks:
zsh/fish(shells.test.ts) and ano-op
claudestub (restart-guardrail spawns aclaude --resumesession; itsguard 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
shouldReapAtExitsemantics; rust mirrors the samePTY_REAP_ON_EXITknob + default. I'll relay the exact env-var name + precedence once this lands.