fix(acp): harden reviewer session lifecycle - #604
Conversation
Codex ReviewVerdict: needs changes [P1] Identity reservations survive runtime teardownsrc/main/acp/runtime.ts:643 Impact: disconnect() and unexpected connection-close cleanup clear active sessions but not the new pending reviewer/primary reservations. A disconnect racing an in-flight startup can leave the next startup rejected for the same ID, potentially indefinitely if the original operation hangs. Recommendation: Invalidate in-flight startups and clear both pending reservation collections during every connection teardown before allowing new sessions. [P2] Collision handling leaks provisioned session resourcessrc/main/acp/runtime.ts:1418 Impact: On a primary ID collision, the new code only disposes the local ActiveSession. MCP HTTP routes created before startup are not yet recorded in routing maps, so they cannot be unregistered; the same leak exists in the resume/adoption collision branches at lines 2058 and 2155. The failed protocol session and its scoped resources remain until full runtime disconnect. Recommendation: Centralize collision abort cleanup to cancel/close the protocol session and unregister all MCP routing IDs created for that attempt before throwing. Summary: Static inspection found two lifecycle defects in the new identity-reservation and collision cleanup paths. No project commands were run. |
3dd3289 to
01bffe9
Compare
Codex ReviewVerdict: needs changes [P1] Invalidate pending identity reservations during connection teardownsrc/main/acp/runtime.ts:643 Impact: Recommendation: Treat pending startups as blocking activity, clear or cancel their reservations during teardown, and guard the pending-to-active transition against a stale connection generation before registering bridge authority. Summary: The new pending session identity state is not integrated with connection teardown or reconnect gating, creating a lifecycle and routing race during interrupted startup. |
01bffe9 to
2337f47
Compare
|
Rebased onto current main (bbbd1ac) and force-updated to 2337f47. The latest review fixes add generation-owned reconnect/teardown, owner-token Reviewer disposal, staged Primary Specialist/Permission/model projections, and best-effort startup cleanup that preserves the primary error while revoking Notebook capabilities. Focused ACP/Reviewer tests pass 334/334, typecheck and zero-error lint pass, and two independent fixed-SHA closure reviews report 0 findings. The two unrelated full-suite timing failures both passed on isolated rerun; merge remains gated on fresh CI and AI review. |
Codex ReviewVerdict: needs changes [P2] Clean up provisional MCP routes on startup collisionsrc/main/acp/runtime.ts:2280 Impact: Recommendation: Track provisional routing IDs and unregister them on every startup failure, including identity-collision branches, before propagating the collision error. Summary: Static inspection found a stale MCP-route leak on newly added session-collision paths. |
2337f47 to
d882005
Compare
|
Force-updated to This closes the prior provisional-route finding and the deeper same-ID capability lifecycle cases:
Exact-head evidence: focused 357/357, full 650 files / 9,668 tests, typecheck passed, lint 0 errors, |
Codex ReviewVerdict: mergeable No actionable findings. Summary: Static inspection found no concrete merge-blocking defects in the requested commit range. |
d882005 to
7c47428
Compare
|
Rebased the branch onto current The latest-main overlap with #611 exposed one real composition regression: the Claude/OpenCode-ID fresh-adoption fast path did not pass A3's existing Primary identity reservation. Latest-main Specialist immediate handoff/prompt identity and Compute Skill/control-capability behavior remain intact. No IPC payload/channel, persisted schema, UI, Electron/Web/CLI/Task availability, Permission policy, or Issue #458 orchestration contract changed. Local exact-head evidence:
Waiting for CI and GitHub AI review on |
Codex ReviewVerdict: needs changes [P1] Prevent stale resume cleanup from revoking a successor capabilitysrc/main/acp/runtime.ts:2341 Impact: A superseded resume can revoke Notebook/compute access belonging to a successor using the same app session ID. Recommendation: Check reservation ownership before broad cleanup and fresh adoption; stale startups should release only their concrete provisional lease. [P1] Always clean up provisional HTTP MCP routessrc/main/acp/runtime.ts:4886 Impact: A framework-switch race can leave failed-startup artifact, notebook, or skill routes registered with authenticated capabilities. Recommendation: Remove the mutable framework check or capture the startup transport mode for cleanup. Summary: Static inspection found two concrete lifecycle defects. Branch and PR title prechecks are valid. |
|
Updated PR #604 to exact head
New/strengthened tests cover the stale resume publishing a same-ID successor before the old not-found arrives, exact-vs-legacy capability release, and an HTTP startup failing after the framework pointer switches. Exact-head evidence: focused 418/418; full 670 files / 9,814 tests passed (15 files / 184 tests skipped); typecheck passed; lint 0 errors / 19 baseline warnings; Spec P0-P3 all zero; Standards no P0/P1/P2. No public IPC, persistence, UI, Specialist/Permission/Compute surface, or Issue #458 change. Waiting for CI and GitHub AI review on |
Codex ReviewVerdict: needs changes [P2] Do not skip session-wide cleanup after releasing a provisional Notebook capabilitysrc/main/acp/runtime.ts:4943 Impact: When an owner-scoped release exists, this return skips app-session cleanup and the wired onSessionReleased hook after startup failure, leaving stale completion-handoff authority and session metadata. Recommendation: Release the concrete capability, then perform broad cleanup when ownsStableIdentity is true; skip broad cleanup only for superseded startups. Summary: Static inspection found one lifecycle regression. Branch and PR metadata prechecks are valid; no project commands were run. |
c2e2d09 to
f1c319e
Compare
|
Updated PR #604 to exact head The cleanup rule is now explicit:
A new regression forces a post-session-start permission failure and verifies that the current owner performs both exact release and Exact-head evidence: focused 419/419; full 670 files / 9,815 tests passed (15 files / 184 tests skipped); typecheck passed; lint 0 errors / 19 baseline warnings; Spec P0-P3 all zero; Standards no P0/P1/P2. No public IPC, persistence, UI, Specialist/Permission/Compute surface, or Issue #458 change. Waiting for CI and GitHub AI review on |
Codex ReviewVerdict: needs changes [P1] Do not let an invalidated context reset adopt a successor sessionsrc/main/acp/runtime.ts:1903 Impact: After a reset is invalidated by disconnect/reconnect, a newer resume can publish the same app ID. The stale reset then accepts that successor, disposes it, and replaces its provider session, losing newer context and routing state. Recommendation: Tie renewal to the reset’s original session/generation, or reject renewal after explicit teardown invalidation. Never treat a successor’s current app ID as the reset’s published identity. Summary: Static inspection found one lifecycle race in context-reset identity renewal. Branch and pull-request title prechecks are valid. |
|
Updated PR #604 locally to exact head
The new regression stalls reset during Local exact-head evidence:
No public IPC, persistence, UI, Electron/Web/CLI/Task availability, Specialist/Permission/Compute policy, or Issue #458 orchestration contract changed. I will now push and wait for fresh CI plus GitHub Codex review. Merge remains squash-only. |
Codex ReviewVerdict: needs changes [P1] Prevent stale startup permission setup from mutating a successor sessionsrc/main/acp/runtime.ts:1638 Impact: An invalidated startup can continue using mutable Recommendation: Capture the startup’s connection/generation and pass it through permission, model, and effort setup; assert it is still current immediately before each protocol request and abort when superseded. Summary: Static inspection found one merge-blocking ACP lifecycle race: startup reservations prevent stale publication but do not prevent stale setup requests from mutating a same-ID successor session after teardown/reconnect. |
Invalidate pending Reviewer and Primary identity reservations before teardown can fail, and renew stable identities after an already-planned reconnect.
Invalidate pending session startups across connection replacement and teardown, complete armed reconnect barriers on explicit shutdown paths, and clean provisional sessions and reviewer resources without masking the primary lifecycle result.
Detach failed teardown resources before awaiting cleanup, scope reconnect completion and Reviewer disposal to their owners, and stage Primary projections until the startup reservation is committed.
Keep provisional session cleanup best-effort so disposal errors cannot replace the startup failure or skip Notebook capability revocation.
Unregister failed HTTP-only startup routes while their identity reservation still owns them, and release Notebook bearer capabilities through token-scoped leases so stale cleanup cannot revoke a same-ID successor.
ecfdce8 to
5585aef
Compare
|
Rebased A3.0 onto latest
Exact-head local evidence for
No public IPC, persistence, UI, Electron/Web/CLI/Task availability, Specialist/Permission/Compute policy, or Issue #458 orchestration contract changed. I will wait for fresh exact-head CI and GitHub Codex review before squash merging. |
Codex ReviewVerdict: needs changes [P1] Guard MCP host shutdown against a newer connectionsrc/main/acp/runtime.ts:2725 Impact: An older disconnect can close the shared HTTP MCP host after a replacement connection starts, stopping routes used by the replacement session and breaking notebook, artifact, and skill MCP calls. Recommendation: Make HTTP-host cleanup generation-aware, or use generation-specific hosts so an older teardown cannot close a successor’s host. Summary: Static inspection found one lifecycle race in concurrent disconnect/reconnect handling. |
|
Fixed the exact-head Codex P1 about an older disconnect closing a successor's shared HTTP MCP host.
Exact candidate
No public IPC, persistence, UI, Electron/Web/CLI/Task availability, Specialist/Permission/Compute policy, or Issue #458 orchestration contract changed. Merge remains gated on fresh CI and exact-head GitHub Codex review, then squash only. |
Codex ReviewVerdict: mergeable No actionable findings. Summary: Static inspection found no concrete merge-blocking defects in the pull request changes. |
Problem
Reviewer Session IDs were registered only after asynchronous startup. A Reviewer could alias an active or in-flight Primary Session, temporarily narrow Primary permissions/tools, and delete shared metadata during disposal. Reviewer or Primary teardown failures could also leave stale startup continuations, bridge authority, reconnect barriers, or backend resources alive across a replacement connection.
Proposed change
Harden ACP session identity and teardown ownership without changing normal Reviewer behavior or public contracts.
ActiveSession, so a late dispose from an old same-ID Reviewer cannot remove its successor's route, bridge scope, metadata, or temporary directory.onSessionReleased. Provisional HTTP cleanup uses the transport mode captured by that startup rather than the later mutable framework.ActiveSessionbefore its first await. Only a startup that began without a usable connection (or behind an already-armed reconnect barrier) receives a one-shot reservation renewal across expected connection setup; an explicit teardown invalidates an older reset instead of letting it replace a same-ID successor.ensureConnectedbefore using it. Per-session deletion epochs supersede older reset/resume reservations, an in-flight deletion rejects later same-ID startups, and owner refcounts retain each epoch until every stale continuation has unwound.Scope and non-goals
Validation
npm run typecheck: passed.npm run lint: passed with 0 errors; 19 pre-existing warnings remain outside this diff.npm test -- --run: 669 files passed, 15 skipped; 9,837 tests passed, 184 skipped.git diff --check: passed.a842397, headcd63636, 16 commits, ahead/behind16/0.a842397..cd63636reports P0/P1/P2/P3 = 0/0/0/0. Standards reports P0/P1/P2 = 0/0/0 and three non-blocking P3 notes: prerequisite diff size, lifecycle-test discoverability, and a missing direct Notebook durable-binding combination test.Coverage includes Reviewer/Primary same-ID races; create/resume/context-reset/fresh-adoption app/provider reservations; invalidated context reset racing a same-ID successor; reset/resume across delete, failed delete, and a second disconnect after connection setup; public connect, disconnect, shutdown, deferred reconnect, and unexpected close; same-ID successor permission setup; reentrant initialized/failure event disconnects; initialize/authentication intent failure cleanup; stale HTTP host close racing a successor route; real disposal failure followed by a fresh backend/bridge; stale Reviewer disposal after replacement; staged Specialist/Permission/model projection commits; partial/throwing HTTP route cleanup; published-session observer/alias failures; exact Notebook capability release; and disposal failure preserving required-model/startup errors while reservations are revoked.
Review focus
Please focus on generation ownership at teardown boundaries, concrete ActiveSession ownership for Reviewer disposal and context reset, the one-shot connection-setup renewal permit, captured connection ownership through bootstrap/session setup, and the no-await assertion-to-publication sections for Primary create/resume/adoption.
Real external ACP providers were not run; provider boundaries use the project's existing fakes. Merge by squash only after all required CI and AI review checks pass.