feat: wave-C restart-resilience — fresh-agent reconcile completion, sidebar contract, frozen WS contract (3 lanes, integrated)#541
Merged
Conversation
Decision (a) UPDATE: regenerate port/contract/* to the true protocol
surface (29/56, v7), reconcile the freeze suite (ZOD list 8->12) and the
Rust T0 inventory (29/56/85, extension buffer shrunk to
durability.degraded), refresh stale docs, and add a port-contract CI
workflow (freeze suite + regen idempotency + cargo test) so the guard
can never silently rot again. B2 residual located but deliberately not
applied (live legacy-Node consumer = behavior change); stash@{0}
obsolescence verified read-only.
🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)
Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
Lane C2 of the restart-resilience campaign final wave: fresh-agent verdict client folding, fresh-agent per-sessionRef create/resume lease (D8), and the reload-path pre-verdict create race. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…idation findings Pre-execution validation of six load-bearing assumptions (ledger in .the-usual-logs/port-contract-reconcile/load-bearing-ledger.md). Verified: generator determinism (fresh-npm-ci regen x2 byte-identical, zero env-dependent sources), Rust base green (34 tests) with post-regen red confined to tests/inventory.rs, --locked clean with a pure-Rust dep tree, and Actions policy allowed_actions=all with the rust-clippy.yml precedent succeeding on push+PR. Falsified and fixed: the regen diff is NOT line-level clean (enum appends + anyOf diff-anchoring artifacts - Task 2 Steps 1-2 now carry the exact validated generator output and removed-line tolerance list with sharper STOP criteria), and the oracle suite is not green at base (4 pre-existing env/machine failures, regen-neutral - noted; follow-up recorded in Task 6). Task 5 now mirrors rust-clippy.yml's deliberate 1.96.0 toolchain pin and documents that the workflow first runs at PR open, not on the branch push. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…atim format Fresh-eyes review (iteration 1) found the Expected block claimed the generator 'prints exactly' a condensed 4-line transcript that generate-ws-contract.ts cannot emit, inside a STOP-on-mismatch gate. Reworded to gate only on the validated values (7 / 66 / 29 / 56 / 56-56 / 12 / 0) and corrected the provenance note to say the values, not the formatting, were validated by clean-clone execution.
…falsified, 1 accepted) Load-bearing assumption pass over the P1.14 plan (ledger: .worktrees/.the-usual-logs/sidebar-registry-sync/load-bearing-ledger.md): - FALSIFIED rung-2 ledger fallback premise: no production window where a live identity lacks session_id while a Bound row covers its current terminal id. Task 2 replaced with the validated real gap: stamp the GET /api/terminals sidebar projection from the identity registry so the client Phase-E terminal:<id> ghost never materializes. - FALSIFIED raw codex REST resume acceptance: Tasks 6/7 codex legs now send the canonical sessionRef body shape (raw resume is deliberately 400-rejected at HEAD). - FALSIFIED Task 5 RED symptom: pre-fix failure is the data-has-tab timeout (no arming -> no adoption), not a server-minted duplicate row; red->green attribution corrected. - FALSIFIED Task 8 lost-client simulation: must also clear sessionStorage (clientInstanceId) or the recovery self-pollution filter drops the client's own generations. - VERIFIED: client adoption fold -> hasTab green (no client changes), note_submit fires for REST-created terminals, restart respawn re-upserts identity (incl. codex), P1.6/P1.11/P1.13 landed, e2e binary override + 1s index TTL semantics. - ACCEPTED (recorded): Task 4 freshagent scope interpretation (lane-spec exception text not persisted anywhere findable); Verification Report must surface it for the campaign owner.
The step expected 'git log --oneline -1' to print bf6242a, but the branch carries docs(plan) commits atop that base, so the gate could never pass as written. Gate now checks 'git merge-base HEAD origin/main' and notes that docs(plan) commits on top of the base are expected and not lane contamination. (Fresh Eyes iteration 2 blocking fix.)
- Tasks 5-8: REST creates now send x-auth-token (server rejects Bearer; all four scenarios would have 401-failed as written) - Task 5 Step 2: replace no-op git-stash RED procedure with a bf6242a throwaway-worktree build (fixes are committed, nothing to stash) - Task 5 case-c: scope .xterm visibility check with .last() to avoid Playwright strict-mode violation (multiple mounted terminals) - Task 6: dedupe gate uses window.__FRESHELL_TEST_HARNESS__ with no sentinel fallback so it fails loud instead of passing vacuously - Task 7: respawn proof snapshots --resume count before restart and asserts an increase (absolute >=2 was satisfied pre-restart) - Task 9: Verification Report template now describes Task 2 as identity-registry stamping of /api/terminals, not the falsified ledger-backed join
Load-bearing assumption review (5 verified, 11 falsified, 2 accepted; ledger in .the-usual-logs/reconcile-completion/load-bearing-ledger.md): - A1/A7: claude sessions are placeholder-keyed; add durable->live resolution + attach ack (new Task 10b) and has-live adopt arms with codex eviction guard (Task 12) - A2: Redux-map-on-ready gate is provably too late; authoritative pre-verdict gate moves to a ws-client sender-level hold (new Task 6b) - A5: no legacy auto-retry exists; frozen-client invariant formally amended with the SESSION_RESERVED carve-out (owner-ratifiable) - A6: lease primitive gains binding map + BoundLive + under-lock re-check; synchronous cli_index insert on claude create-resume - A8: kill discipline reworked to set_kill_handle + ownership sweep confirmed empty before release; hold closed on non-Linux - A9/A11/A12: opencode get_session timeout; clearSessionLost reducer; hidden+pending composition test - A10: cross-kind terminal/fresh-agent liveness probes both directions (new Task 13b) - A14a/A16: harness spec knobs budgeted; process-kill suites wrapped in sandbox-test.sh - A15: P0.2 pin expectation inverted (expected to remain, narrowed) - Validated-decisions section added (A4 shape, A13 bound, A5 amendment) Co-authored-by: Amplifier <240884023+amplifier-github@users.noreply.github.com>
… count failures Fresh-eyes review (iteration 3) found the observed-RED gate predicted combined_surface_is_81 failing after Task 2's regen, but that test reads only crate constants (still 28+53=81) and stays green until Task 3 Step 4 renames it to combined_surface_is_85. Expected block now matches the plan's own validation evidence (2 failures: client/server count asserts).
The freeze suite (config/vitest/vitest.port.config.ts) was previously runnable only via a README-documented raw vitest invocation - no npm script, no coordinator entry, no CI. It is currently RED on main (8 failed / 30 passed): port/contract/* is stale vs shared/ws-protocol.ts (missing amplifier.activity.*, terminal.idle; outbound schema missing pane.reconcile.result). Subsequent commits reconcile the artifacts. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
… (29/56, v7) npm run contract:generate on bf6242a. Purely additive: picks up the amplifier activity family (PR #498 era, 5398c8a), terminal.idle (317e2ea), and closes eef9b34's partial regen (outbound schema was missing pane.reconcile.result; inbound bundle was missing AmplifierActivity*/TerminalIdle/PaneReconcile*/ReadyCapabilities schemas). Freeze suite green (38/38); ZOD_BACKED_SERVER_MESSAGES grown 8->12. Second regen produces no diff (idempotent). NOTE: cargo test -p freshell-protocol is intentionally red at this commit (inventory counts 28/53/81 vs regenerated 29/56); the next commit reconciles the Rust side. Not independently mergeable. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…mpletion plan - Task 6b: the existing pinned test ws-client.reconcile.test.ts:140-150 is the capability-ACKED pre-ready flush (not capability-less as previously described); add Step 1b to deliberately flip it to the new hold contract, correct the regression-test description, update the obsolete ws-client.ts:202-205 design comment in Step 3, and amend Step 4's expectation accordingly. - Task 1: the ReadyCapabilitiesSchema red test was vacuous (Zod non-strict objects strip unknown keys rather than rejecting, so .success is true on base); assert key SURVIVAL via parsed.data.paneReconcileFreshAgentV1, mark the getServerCapabilities test as green-on-base regression coverage excluded from the red gate, and correct Step 2's stated red rationale.
…tion 2
- Task 4 GREEN: with_codex_locator now takes Option<Arc<CodexLocator>>
mirroring with_amplifier_locator (lib.rs:362-368); RED test passes
Some(locator.clone()); Self::new default codex_locator: None added;
main.rs wiring clarified (clones the whole Option binding).
- Task 4 Step 5: REST codex note_submit now prescribed BEFORE the
registry.input PTY write (codex_association.rs:49-56 await-before-Enter
contract), gated on the handler's crate-local is_submit_input instead of
contains('\r'), and the out-of-scope mode binding removed.
- Task 6: dedupe gate uses the Node-side harness.getTabCount() from the
TestHarness returned by bootAndConnect (donor remote-tab-linkage:252-255)
instead of a nonexistent window-global method; tabsBefore > 0 pin keeps
it fail-loud.
- Task 7: persist flush uses the donor-verbatim
__FRESHELL_TEST_HARNESS__?.dispatch idiom (remote-tab-linkage:277-281)
instead of the phantom __freshellStore global, with a persisted-layout
truthiness assertion so a silent no-op fails loudly.
…ozen T0 surface (29/56/85) Promote amplifier.activity.list / amplifier.activity.list.response / amplifier.activity.updated / terminal.idle from EXTENSION_*_MESSAGE_TYPES into CLIENT/SERVER_MESSAGE_TYPES, matching the regenerated port/contract/ws-message-inventory.json. The extension carve-out (0736afe) existed only to keep tests/inventory.rs green against a stale artifact; that artifact is now current, so the buffer shrinks to the one genuinely Rust-only frame (durability.degraded, documented promotion path). Inventory tests pin 29/56/85; TS freeze suite and Rust T0 tests now assert the SAME surface. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…, 12 zod-backed) 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…t T0 tests The freeze suite previously ran nowhere (no npm script, excluded from the default vitest config, unknown to the coordinator, absent from CI) and sat red on main unnoticed; cargo test was likewise not in CI. This workflow pins both sides of the contract seam on every PR, plus a contract:generate + git diff --exit-code idempotency gate, following the rust-clippy.yml precedent for cheap targeted CI. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…mpletion plan - Task 2: retarget red gate at the real base gap (stripStaleIds restoreLayout strip); mark persistence test as green-on-base regression coverage (stripTransientSessionFields already strips the trio for fresh-agent); drop the unnecessary persistMiddleware change; fix red rationale and anchors - Task 14: gate the GLOBAL createFailed projection in fresh-agent-ws.ts for SESSION_RESERVED+retryable (no error card / Retry race / stale entry after attach auto-resolve); dual-path test delivery; assert no pendingCreateFailures entry and no error card; add fresh-agent-ws unit test; place pane-level early-return after releasePendingRebind()
…isposition 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…ty registry (P1.14)
… timeout Add missing hardening blocks to match sibling workflows (rust-clippy, typecheck-client): - permissions: contents: read (caps GITHUB_TOKEN to read-only during npm ci) - concurrency with cancel-in-progress: true (supersede stale runs) - timeout-minutes: 20 (covers npm ci + test:port + contract:generate + cold cargo test for freshell-protocol) Mirrored from rust-clippy.yml (lines 9-10, 12-14, 19) and typecheck-client.yml (lines 9-10, 12-14, 19). 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…mpletion plan - Task 2: red gate was inverted (restore-strip test is green on base because normalizePaneContent's fresh-agent branch already drops the fold trio). Retarget the red gate at trio SURVIVAL through initLayout/updatePaneContent normalization, and add the missing load-bearing change: preserve reconcileEpoch/pendingReconcile/reconcileNotice in BOTH fresh-agent returned literals of normalizePaneContent (mirroring the terminal branch at :98-102), with stripStaleIds then the sole strip point on the restore path. - Task 9: record the harness seeding dependency on Task 2's normalize change (initLayout normalizes at :933) and correct Step 2's red-gate expectation (six tests, with per-test red rationale and a halt-if-green guard).
…ReconcileFreshAgentV1 [C3-NOTE: minimal ws-protocol widening — kind enum + ready/hello capability key]
…on; stripped on restore + persist 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
Task 3 of the reconcile-completion plan:
- applyFreshAgentReconcileAttach: folds an attach/duplicate verdict into a
fresh-agent pane by writing the durable sessionRef as the live handle
(valid for all providers; claude durable->live resolution lands in 10b).
- resetFreshAgentPaneForReconcileCreate: respawn/fresh verdict fold that
preserves createRequestId (council rule 2) and degrades loudly to fresh
on provider mismatch, mirroring the terminal guard.
- findReconcilePaneContent: terminal|fresh-agent finder; setPaneRestoreError,
setPaneReconcileNotice, clearPaneReconcileNotice now act on both kinds.
- DeadSessionEntry gains optional kind ('terminal'|'fresh-agent'; absent =
terminal, backwards compatible).
🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)
Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…n-fold
View-level pre-verdict wait state for panes named in an outgoing
pane.reconcile request: PanesState.reconcilePendingPanes (paneKey ->
startedAt ms, ephemeral, {} initial). Actions setReconcilePendingPanes
(replace), clearReconcilePendingPane, clearAllReconcilePendingPanes.
All five fold-target reducers (applyReconcileAttach,
resetPaneForReconcileCreate, applyFreshAgentReconcileAttach,
resetFreshAgentPaneForReconcileCreate, setPaneRestoreError) clear their
own pane's pending flag so a folded verdict always releases the wait
(never a silent wedge). Stripped at the persistMiddleware slice level
and reset on hydration. Tasks 7/8/9 consume; the authoritative
sender-level hold is Task 6b.
🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)
Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…oses the reload create race) When ready acks paneReconcileV1, the sender holds pane creates (terminal.create/freshAgent.create) per pane until the verdict folds: - pre-ready queued creates move to heldCreates instead of the wire - post-ready creates are held too (all before setReconcilePendingCreates narrows the set; only pending requestIds after) - setReconcilePendingCreates releases creates outside the set immediately - cancelCreate retracts a held create (the fold's retraction hook) - clearReconcileCreateHold flushes still-held creates (cardinality-gap fallback); a RECONCILE_VERDICT_WAIT_MS (4s) wall-clock bound guarantees the hold degrades to today's eager behavior - never a silent wedge - disconnect mid-hold re-queues held creates via preReadyCreateQueue so they send exactly once on the next connection - never a duplicate; createRequestId is never re-minted - without the capability ack, sender behavior is byte-identical to today 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…hAgentV1 Baseline repair: Task 1 added paneReconcileFreshAgentV1 to the hello capabilities but the exact-match expectation in ws-client.test.ts was not updated, leaving test/unit/client/lib red at the branch baseline. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…ing-pane lifecycle On every ready, App latches both capabilities (paneReconcileV1 + paneReconcileFreshAgentV1), sends ONE reconcile request covering terminal panes plus (iff the fresh-agent capability is advertised) fresh-agent panes, marks exactly the requested paneKeys pending in Redux, and narrows the ws-client sender hold to exactly the requested createRequestIds. All pending state (Redux map AND sender hold) clears on fold completion, on a correlated error frame, on a malformed result, on cardinality violation, and on a capability-less ready (frozen-client invariant: no capability -> legacy path, no stale wedge). The fold retracts each folded pane's stale held create at the sender (ws.cancelCreate) BEFORE the hold clears. Sibling App test suites' ws-client mocks gain the three hold-API methods (cancelCreate/setReconcilePendingCreates/clearReconcileCreateHold) so the mocks keep mirroring the real WsClient surface. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…rdict on reload View-level pre-verdict deferral (defense-in-depth on top of the Task 6b sender hold): a hydrated pane that is named in reconcilePendingPanes defers its mount-time terminal.create until its verdict folds, bounded wall-clock by RECONCILE_VERDICT_WAIT_MS (imported from ws-client -- the one definition). On timeout the view dispatches clearReconcilePendingPane (this layer owns the per-pane release timer); the effect re-fires via the new reconcilePendingSince dep and the legacy eager create proceeds with the SAME createRequestId -- never re-minted, never a silent wedge. The attach branch is never gated. The ws.onReconnect re-drive (V3 caveat) now consults the pending map too, so a mid-window WS flap cannot fire the ungated create; the post-flap reconcile round-trip or the bounded timeout drives the pane instead. No pending entry (capability off / pane not in the request) means behavior byte-identical to today. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…ire, bounded pre-verdict wait, notice) Fresh-agent VIEW leg of pane reconcile (Task 9): - create effect re-arms on reconcileEpoch bumps (arm key createRequestId:epoch) so a verdict fold on a mounted pane re-fires the create with the SAME createRequestId (council rule 2 -- never re-minted) - pre-verdict create wait: a reconcile-pending pane defers its mount-time create until the verdict folds, bounded wall-clock by RECONCILE_VERDICT_WAIT_MS (imported from ws-client -- the one definition); on timeout the view releases the pending flag and the legacy eager create proceeds (never a silent wedge). Gate returns BEFORE the hidden rebind-queue enqueue, so a fold-driven create on a hidden pane still rides the paced queue - freshAgent.created consumes pendingReconcile (A19, fresh-agent leg) - reconcileNotice renders once as a role=status line above the transcript, timed one-shot (5s), then consumed from pane content - attach folds ride the EXISTING attach effect; the freshAgent.attach frame already carries resumeSessionId + sessionRef (claude's attach_durable_id reads ONLY those fields -- N-V1-2), now locked by test No pending entry / no fold (capability off) => behavior byte-identical to today; the 105-test FreshAgentView suite and the hidden-rebind pacing suite pass unchanged. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…SessionLost + capability-gated fallback retained) 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…k, send routes via cli_index 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…kill-before-release TTL) 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…pt arms, tree-kill-before-release, SESSION_RESERVED losers The tree-kill sweep is YAMA-aware: under restricted ptrace (ptrace_scope=1) /proc/<pid>/environ is readable only while the reader is an ancestor, so the tagged tree is captured BEFORE killing the sidecar and death is confirmed via world-readable /proc/<pid>/stat with (pid, starttime) pid-reuse guards. Red-gate honesty: winner_dies_mid_resume_releases_the_lease_for_the_loser is green on base (A's plain create failure never blocked B pre-lease); it pins the release-on-failure contract as regression coverage. The other four wire tests were red-first. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
Task 9 final consolidation: contract-case table (a/b/d PASS as-is, c FIXED), fixes list, red->green proof pointers, post-disk-fix gate results (fmt/clippy/cargo test/coordinated vitest/release build/11 e2e), scope interpretations for the campaign owner, and the honest residuals list including the pre-existing cross-lane pane_ledger flock-test flake observed during repeated gate runs. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…ared sidecar never killed, get_session bounded)
Task 13: claude attach-resume, codex attach untracked-resume + exited-respawn,
and opencode attach-resume all claim the per-sessionRef lease before spawning/
resuming; losers get freshAgent.error{SESSION_RESERVED} (retryable, never
lost). Opencode never records a kill handle -- the shared serve sidecar is
never killed by the lease -- and get_session is timeout-bounded so a wedged
serve reopens the key instead of reserving it forever.
Red tests: loser_attach_after_winner_binds_converges_to_the_live_session,
opencode_attach_resume_is_serialized_without_touching_the_shared_sidecar
(+ in-crate resume_durable_session_get_session_is_bounded_and_reopens_the_lease).
🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)
Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…sumes see each other (one-writer invariant) Task 13b (V7): the terminal and fresh-agent resume domains overlap via 'Reopen as freshclaude / Claude CLI'. Two injected guards close the two-writer hole: (1) every fresh-agent create/attach resume seam consults a terminal-liveness probe (built in main.rs over the same identity+registry join the D7 guard uses) and answers SESSION_RESERVED while a live PTY owns the session; (2) the terminal D7 create-rung guard now also joins the three fresh-agent runtimes' has_live_session, rejecting terminal.create with its existing RESTORE_UNAVAILABLE frame while a live sidecar owns the session. Probes default to always-false (behavior-preserving for unwired constructors). Red tests: freshagent_resume_is_refused_while_a_terminal_pty_owns_the_session, terminal_create_is_refused_while_a_live_sidecar_owns_the_session. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…ncile auto-resolve on exhaustion
Task 14: a transient reservation (the D8 lease loser answer) never lands
create-failed and never mints an error card. The GLOBAL create.failed
projection skips SESSION_RESERVED+retryable (no pendingCreateFailures entry,
create route stays alive); the pane-level handler re-drives the SAME create
after a 1s floor inside a 30s window (SAME createRequestId -- never
re-minted); attach losers (freshAgent.error{SESSION_RESERVED}) re-send the
attach the same way with the banner suppressed via lastErrorCode; window
exhaustion auto-resolves via the Task 10 single-pane reconcile (attach
verdict -> silent attach; dead -> the visible panel/fresh flow).
Red tests: create.failed SESSION_RESERVED re-drive, exhaustion auto-resolve,
attach-loser re-drive, and the fresh-agent-ws global-gate projection test.
🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)
Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…r lease, zero identity-less reload spawns Task 15: three proofs with the real SPA against the real Rust server: (1) fresh-agent restart recovers via reconcile verdicts (boot reconcile names the fresh-agent pane, durable identity converges, never lost, no restoreError); (2) two clients resuming one fresh-agent sessionRef spawn exactly one sidecar (STABLE-COUNT settle on the fake-sidecar request log); (3) a 3x page.reload storm never spawns an identity-less resume (terminal spawns zero -- attach verdicts -- and every fresh-agent create carries the resume identity). Registered in RUST_ONLY_SPECS + rust-chromium testMatch. Assertion bite verified via a temporary nonsense-identity run (failed, then reverted). 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…rtifact under ~/.codex The fake wrote rollouts to /tmp/fake-codex-home (outside the isolated HOME the Rust session-existence probe scans) with un-indexable content (no session_meta, no cwd -- the R10b gate skips such files). Harmless before, but the fresh-agent reconcile verdicts landed by this lane made the existence probe load-bearing: every fixture thread answered dead_session/durable_artifact_missing across restart, wiping the pane's durable identity (restore-contract-wall freshcodex red). Now mirrors the real CLI: CODEX_HOME || ~/.codex, session_meta first line with id + cwd. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…start REST convergence poll; prove double-restart 6x green Task 16: (1) the freshclaude identity pin HELD as the plan predicted (V1/A15) -- reason string narrowed to the proven residual (pre-kill content.sessionId is the sidecar-minted placeholder; SIGKILL yields a respawn verdict minting a new placeholder; closing needs claude to expose the durable id as the primary handle -- not in C2 scope). (2) The wall double-restart test's final /api/terminals convergence poll now resolves the pane's CURRENT terminalId inside the poll (a pre-poll capture raced the pane's final convergence round -- 1-in-6 flake). 6x proofs all green: reconcile-client-adoption double-restart 6/6, restore-contract-wall double-restart 6/6, double-restart-terminal-restore 6/6. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…rying respawn create HISTORY (2026-07-26, reconcile-completion): the spec pinned the attach-only mechanism (zero post-restart freshAgent.create frames). With fresh-agent reconcile verdicts live, a SIGKILL'd session folds a RESPAWN verdict that re-drives ONE create carrying the durable resumeSessionId + sessionRef (the D8 lease serializes it against the reconnect attach -- one sidecar either way). The guarded invariant is unchanged and now asserted directly: no post-restart create may ever be identity-less (the lost->triggerRecovery re-mint). All other assertions (attach proof, idle settle, server-side resume, history rehydration, same-conversation send) untouched. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
… homedir fallback) Follow-up to 5a242b0: getCodexHome()'s os.homedir() fallback referenced os without importing it -- the freshell-codex launch_lifecycle proxy test (which exercises the platform-info handler through the spawned fixture) crashed the app-server with a ReferenceError. Sandbox workspace run caught it; green after. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…le surface C2 (feat/reconcile-completion) extended the wire after the contract lane froze it: capability paneReconcileFreshAgentV1 (hello + ready) and the pane-reconcile kind widened from const 'terminal' to enum ['terminal','fresh-agent']. No new message types (inventory unchanged at 29/56). Regenerated via npm run contract:generate; freeze test proves byte-identical regen. Rust freshell-protocol types already carry the matching fields (5/5 inventory/schema tests pass against this surface). 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…nal yields clean respawn waveC integration interaction pin (C2 x PR #539): a detached terminal reaped by enforce_idle_kills while its pane is mid-reconcile must converge to a respawn verdict on the next round - never attach to the reaped id, never a wedge. Exercises the real seam: the sweep marks the terminal not-live, so the verdict scan falls through to the recovery rows. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…aveC integration Conflicts resolved: - crates/freshell-server/src/main.rs: #540 moved TerminalIdentityRegistry construction above the fresh-agent builder chain (with_session_identity); dropped C2's duplicate later construction, kept C2's Task 13b terminal_liveness probe capturing the shared early instance. - crates/freshell-ws/src/terminal.rs: adopted #540's shared TerminalRegistry::live_session_owner helper for the D7 terminal join, kept C2's Task 13b fresh-agent sidecar arm on top. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
PR #540 (ks38) 409-rejects a REST resume for a session whose terminal is still running (D7 one-writer guard) - invalidating case-a's validated assumption that a duplicate resume 'spawns unconditionally + ERROR-logs'. Kill the earlier serial cases' live terminals (WS terminal.kill via the harness socket) before re-establishing the resume tabs, and surface the response status/body in both POST assertions. Pre-adoption codex terminals hide sessionRef from the REST directory JSON, so the kill targets every running terminal, not just sessionRef matches. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
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.
Wave C — final wave of the restart-resilience campaign. Three lanes merged (contract → C2 → C1) and verified as a combined tree, plus a post-fork merge of #540 with resolutions. Lanes (preserved as merge parents):
Integration commits: frozen-contract reconciliation with C2's wire additions (capability + kind enum; inventory unchanged at 29/56/85), idle-reap × reconcile seam pin (#539 interaction), #540 merge resolutions (shared TerminalIdentityRegistry + live_session_owner helper), sidebar case-a fix for #540's REST resume live-guard.
Verification: cargo workspace green (77 suites, 0 failures), clippy -D warnings clean, coordinated JS suites green (4229/4548/350), test:port 38/38, e2e 38/38 at CI-parity workers, double-restart trio 6x each = 54/54, no contract-wall pin flips (P0.2 pin holds with verified narrowed reason), no deleted machinery resurrected. Documented load-sensitive flakes (not masked): wall double-restart 1-in-7 under load, sidebar case-a at unbounded parallelism (pre-existing), remote-proxy EADDRINUSE TOCTOU (pre-existing).