v1.42.1.0 feat: gate terminal-agent teardown on ServerConfig.ownsTerminalAgent (unblocks gbrowser embedder)#1615
Open
garrytan wants to merge 4 commits into
Open
v1.42.1.0 feat: gate terminal-agent teardown on ServerConfig.ownsTerminalAgent (unblocks gbrowser embedder)#1615garrytan wants to merge 4 commits into
garrytan wants to merge 4 commits into
Conversation
Adds ownsTerminalAgent?: boolean to ServerConfig (default true). Wraps the three shutdown side effects (pkill -f terminal-agent\.ts + 2 safeUnlinkQuiet calls for terminal-port and terminal-internal-token) inside a single if (ownsTerminalAgent) block. Embedders (gbrowser phoenix overlay) pass false to keep their own PTY lifecycle intact across gstack's teardown. CLI start() call site passes ownsTerminalAgent: true explicitly; static-grep test in the new test file catches a refactor that drops it. Strict opt-out: only explicit false flips the gate (cfg.ownsTerminalAgent === false ? false : true). Defends against JS callers passing truthy non-bool values. Adds __resetShuttingDown test-only export mirroring __resetRegistry. The module-scoped isShuttingDown latch otherwise silently no-ops a second shutdown() in the same process. Drops dead try/catch wrappers around safeUnlinkQuiet inside the new gate — safeUnlinkQuiet already swallows all errors internally. New test file (4 cases) stubs both process.exit AND child_process.spawnSync so a real pkill -f terminal-agent\.ts never fires on the developer machine. beforeAll/afterAll save and restore real-daemon file contents in the state dir so the test cannot clobber a running gstack session.
…ion gap, ownership-object trigger) Three P3 followups surfaced by /autoplan + /plan-eng-review while reviewing the ownsTerminalAgent gate: - Identity-based terminal-agent kill: pkill -f terminal-agent\.ts is a latent CLI footgun (regex match kills sibling gstack sessions, editor processes, etc.). Replace with PID-tracked process.kill at both cli.ts:1047 and server.ts:1281. - shutdown() reads module-level config, not cfg.config (pre-existing composition gap). Same gap applies to cleanSingletonLocks(resolveChromiumProfile()) at server.ts:1298 (should be cfg.chromiumProfile). Both are followup work for the embedder-composition story. - 4th caller-owned teardown gate trigger: today ServerConfig has 3 (xvfb?, proxyBridge?, ownsTerminalAgent). If a 4th appears, collapse to cfg.callerOwns?: Set<...> ownership object.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a one-paragraph reference for the v1.42.1.0 embedder teardown gate right after the Sidebar architecture block. Covers default semantics, when embedders must pass `false`, polarity inversion vs xvfb?/proxyBridge?, and the static-grep CI test that pins the CLI call site. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
E2E Evals: ✅ PASS6/6 tests passed | $1.27 total cost | 12 parallel runners
12x ubicloud-standard-8 (Docker: pre-baked toolchain + deps) | wall clock ≈ slowest suite |
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.
Summary
Adds
ServerConfig.ownsTerminalAgent?: boolean(defaulttrue) so embedders that run their own PTY server can opt out of gstack's terminal-agent teardown. Fixes the gbrowser phoenix overlay's/health.terminalPort: nullissue without changing CLI behavior bit-for-bit.The fix:
browse/src/server.ts:1312wraps three teardown side effects inif (ownsTerminalAgent): thepkill -f terminal-agent\.tsspawnSync, plussafeUnlinkQuietforterminal-portandterminal-internal-token. Default istrueso the CLI path is preserved bit-for-bit; gbrowser passesfalse.cfg.ownsTerminalAgent === false ? false : true— only explicitfalseflips the gate. Defends against JS callers bypassing TS.start()call site explicitly passesownsTerminalAgent: truewith a comment; static-grep test pins it.__resetShuttingDown()test-only export atbrowse/src/server.ts:2628mirrors__resetRegistryprecedent. Module-scopedisShuttingDownotherwise silently no-ops a secondshutdown()in the same test process.try { safeUnlinkQuiet } catch {}wrappers inside the new gate (slop-scan flagged).Bug fix downstream: gbrowser PR #21's mitigation (re-writing
terminal-porton every overlay spawn) becomes redundant. Confirmed by reading that diff — purely client-side, no gstack-side coordination needed.Test Coverage
Tests:
browse/test/server-factory.test.ts(28 pass, unchanged) + newbrowse/test/server-embedder-terminal-port.test.ts(4 pass) = 32 tests on the impacted surface, all green. Per-test stub ofchild_process.spawnSyncensures realpkill -f terminal-agent\.tsnever runs on the developer's machine (would kill sibling gstack sessions).beforeAll/afterAllsave and restore real-daemonterminal-port/terminal-internal-tokenso the test is safe to run while gstack is alive.Pre-Landing Review
0 issues found. Code follows the existing
xvfb?/proxyBridge?precedent; JSDoc enumerates all three gated actions + polarity inversion warning + pkill regex breadth caveat.Pre-existing test failures (NOT introduced by this PR — verified via
git stash+ re-run on origin/main): 8 test files have failures:server-auth.test.ts(4),sidebar-integration.test.ts(11),sidebar-security.test.ts(1),sidebar-ux.test.ts(73),security-sidepanel-dom.test.ts(6),security-source-contracts.test.ts(1),gstack-config.test.ts(3),snapshot.test.ts(1). These predate this PR and live in sidebar/security/snapshot/config code untouched by these commits. Identical failure counts confirmed on clean origin/main.Eval Results
No prompt-related files changed — evals skipped.
Scope Drift
Scope Check: CLEAN. Intent (user attachment): gate terminal-port deletion behind a
ServerConfigflag. Delivered: the gate, plus hardening (3 case tests + static-grep + JSDoc +__resetShuttingDown+ adversarial-flagged strict-bool + real-daemon save/restore).Plan Completion
10/10 plan items DONE:
: true), T5 (__resetShuttingDownexport), T6 (buildFetchHandler JSDoc), T7 (test file with 4 cases including static-grep), T8/T9/T10 (3 followup TODOs filed).=== false ? false : true) and testbeforeAll/afterAllsave+restore for real-daemon files.TODOS
3 P3 followups filed in
TODOS.md:process.kill, replacespkill -fregex)shutdown()reads module-levelconfig(composition gap; parallelchromiumProfilegap)Documentation
ServerConfig.ownsTerminalAgent?: booleangate (defaulttrue), the three side effects it controls (pkill -f terminal-agent\.tsplus twosafeUnlinkQuietcalls), polarity inversion vsxvfb?/proxyBridge?(explicit bool vs presence), and the static-grep CI test that fails if a refactor drops the explicit: trueat the CLIstart()call site. Embedders (e.g. gbrowser phoenix overlay) must passfalseto preserve their PTY discovery files.Skipped (no update needed): README.md, ARCHITECTURE.md, CONTRIBUTING.md, VERSION (bumped), CHANGELOG.md (entry written).
PR body notes for gbrowser team
chromiumProfileis probably the next embedder-clobber.cleanSingletonLocks(resolveChromiumProfile())atbrowse/src/server.ts:1298runs unconditionally — if you passcfg.chromiumProfile, gstack's shutdown will clean lock files in your profile dir on every teardown. Not fixed here; file a ticket if it bites.Test plan
bun test browse/test/server-factory.test.ts(28 pass)bun test browse/test/server-embedder-terminal-port.test.ts(4 pass)bun test browse/test/dual-listener.test.ts(25 pass)safeUnlinkQuiet.*terminal-(port|internal-token)|pkill.*terminal-agent— all 3 matches insideif (ownsTerminalAgent)blockownsTerminalAgent: trueat CLI start() — matches at server.ts:2466 with comment🤖 Generated with Claude Code