feat: wave-A restart-resilience — identity durability, freshclaude parity, hidden-pane rebind (6 branches, integrated)#536
Merged
Conversation
Load-bearing validation (10 verified, 2 falsified) drove these fixes: - Task 2 Step 1: invariants capture harness is nested inside cfg(test) mod tests, not a direct child -- rewritten as a mechanical hoist. - Cross-lane coordination: sibling lanes verifiably edit terminal.rs, server_messages.rs, invariants.rs, tabs_persist_tests.rs; one adds a ServerMessage variant without contract regen -- added a constraint note and a mandatory pre-push drift check (rebase + re-regen) in Task 8. - Verified findings folded in: targeted test:vitest bypasses the coordinator gate; worktree npm needs no install (resolution falls through to the parent checkout); oracle corpora carry no tabs.sync traffic; live server confirmed Rust (fixes latent until redeploy); Task 6 line-budget contingency. Ledger: .worktrees/.the-usual-logs/tabs-snapshot-defects/load-bearing-ledger.md 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…ind plan Validation pass (11 verified / 2 falsified; ledger in .the-usual-logs): - A2 falsified: hydration queue has no reconnect pump; Task 5 now clears the stale slot, resets hydrationRegisteredRef, registers with queueIfStarted:true, and adds a real-queue unit test. - A5 partially falsified: freshAgent.attach not unconditionally cheap; architecture wording corrected (attach pacing is load-bearing). - A3 verified: dead-session attach contract proven; Task 6 contingency replaced with the INVALID_SESSION_ID facts. - A4 edge: new Task 3 Step 4b hardens markSessionLost against missing session entries (empirically reproduced throw). - A9/A10/A11/A12 refinements: create-serialization rationale, #534 merge notes, e2e evidence-validity notes, requestId in create job keys.
…-eyes r1) Independent cross-model review (fresh eyes, iteration 1) found two blocking plan defects; both fixed: - Task 5 Step 1/2: the tabs.sync.ack roundtrip test was written in constructed-value style, but the real helper is server_roundtrip(wire: &str, type_name: &str) -> ServerMessage. Rewritten in the file's wire-string style (frames for both ack shapes, type_name "tabs.sync.ack"), with Step 2's expected RED state restated as the second frame's schema-conformance failure. - Task 1 Step 1 + Global Constraints: baseline expectation 'git log -1 => c491aee' contradicted the branch HEAD (plan docs commits sit atop the base). Replaced with a merge-base ancestry check, and the Base constraint restated as merge-base c491aee with origin/main allowed to advance (Task 8 drift check covers it). Minor/nit findings from the review are logged, not fixed: .worktrees/.the-usual-logs/tabs-snapshot-defects/fresheyes-plan.md 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…alidation Seven falsified assumptions fixed: CLAUDE_CONFIG_DIR-first candidate store roots (CLI ignores CLAUDE_HOME), cwd-scoped resume (original cwd from the transcript + .jsonl-path fallback), real prompt shape + meta-line skips in the parser fixtures, identity-guarded consumer-exit eviction, single shared env lock for tests, unconditional tempfile dev-dep, corrected direct-read rationale. Verified facts and accepted residuals recorded in the plan's self-review and the validation ledger.
…ed assumptions Load-bearing validation findings applied: - Task 1: inheritance is now hydrate-scoped via opt-in flag (updatePaneContent resume/repair paths rely on mint-when-absent rotation); adds rotation lock test and fresh-agent string type guard - Task 3: covers respawn_pane as third spawn_terminal_pane caller; documents rotation-on-respawn as intentional legacy parity; makes probe_create_request_id pub (documented fence exception); named-arg comments for Lane A5 rebase - Task 4: rebuilt as tests-only tolerance locks in a new test file (strict read-path type check would render all device snapshots unreadable on one wrong-typed field; Lane A6 co-edits tabs_persist_tests.rs) - Task 7: sibling-lane drift re-check + EDEV-08 port-parity bookkeeping step - Non-goals: documented design-intent inferences (reopen/restore fresh identity, fresh-agent persist exemption rationale)
…call signatures (fresh-eyes iteration 1)
- Task 6 test 1: require streamId non-null and changed (only terminal.attach.ready
writes it; terminal.created resets it) so the poll proves the hidden background
attach, not just the pre-existing census re-create path
- Task 6 test 2: require createRequestId changed (minted only by the .lost recovery,
which needs the server's INVALID_SESSION_ID round trip) so stale pre-restart Redux
state can no longer satisfy the recovery poll; drop the sessionId-equality escape
hatch (fake sidecar resumes with the same sessionId)
- Task 6 test 2: pass required cwd arg to createFreshclaudePane(page, harness, cwd)
- Task 5 queue pin tests: call onActiveTabReady('tab-1', ['tab-1']) matching the real
two-argument API so the STOP gate cannot trip spuriously
- Update evidence-validity note, Step 2 expectations, and add fresh-eyes self-review
addendum
…restart-parity plan
Fresh-eyes independent review (iteration 1) found 4 blocking executable-plan
defects; all fixed:
- Task 3 golden fixture: add the missing turn for the object-with-string-content
prompt (sample line 5, 'cli string content question') so the golden matches
the 9-line sample and the parser: 6 turns, tool_use at :4, tool_result at :5,
latestTurnId ':5' (was 5 turns / ':4', making Step 5 'all PASS' unachievable).
- Task 8: replace the wall's leafDurableIdentity (first arm content.sessionId =
create-time nanoid, never rekeyed for claude) with a local liveDurableIdentity
reading sessionRef.sessionId ?? resumeSessionId -- the fields the
sdk.session.init merge actually writes -- in both the pre-restart poll and the
final same-identity assertion; drop leafDurableIdentity from the copy list.
- Task 9 Step 3: correct the composed-ruler probe grep to the actual test title
('THE RULER: all pane types live, one SIGKILL') -- the old pattern matched
zero tests, making the probe/decision step unexecutable.
- Tasks 1/6: make the spawn_consumer contract consistent -- Task 1's sketch now
shows the 4-arg signature (new sidecar_session_id eviction identity guard)
plus the post-loop identity-guarded eviction of the sessions-map entry and
cli_index entries (ledger A9); Task 6's resume_for_attach call updated to
pass sidecar_session_id.clone() as the 4th argument. Self-review section 3
updated to record the now-true consistency claim.
- Task 3 split-test snippet: bind through msg (msg["payload"]["newContent"] ["createRequestId"]) instead of unbound 'payload' variable, matching the actual test's binding in pane_ops.rs - Task 4 Step 3: pass both test filters after '--' (cargo test -p freshell-ws --lib -- semantically_corrupt corrupt_generation_file); cargo accepts only one positional TESTNAME
… module Pure move: tabs_persist.rs sits at the 1,000-line repo limit and the upcoming defect fixes need room. No behavior change. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…drop alarms loudly Defect 2 (storage half, campaign P2.17): an oversize generation was WARN + Ok(()) — indistinguishable from success. Now it is an ERROR-class invariant alarm (tabs_snapshot_dropped_oversize) and a Skipped outcome. The 1 MiB cap itself is kept: records are ~1-2KB/tab metadata and the store's 2.5 GiB hard bound depends on it. Honesty is the fix. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
… launchRetry gate (fresh-eyes iteration 2) - Task 1 Step 3: expect HEAD = latest docs(plan) commit and verify baseline via 'git merge-base HEAD origin/main' = c491aee (the old 'HEAD at c491aee' expectation could never be satisfied once plan commits landed); apply 'git -C <worktree>' to every chained git invocation - Task 5 Step 5: correct launchRetry path to test/unit/client/components/ and require positive confirmation the suite executed (vitest silently drops non-matching positional path filters, so the #532 gate passed vacuously) - Append self-review addendum (iteration 2) covering the edited steps
…e spec skeleton executable The independent reviewer flagged the Task 8 spec skeleton as non-executable: - __dirname used in an ESM project; added the donor's fileURLToPath(import.meta.url) shim (restore-contract-wall-rust.spec.ts:36-40 convention) plus the node:url import - helper imports gained the .js extensions the donor uses - getPaneLayout() called without the required tabId; both tests now read tabId via harness.getActiveTabId() after the picker settles and pass it to every getPaneLayout call and to sendFreshAgentTurn's 4-arg donor signature - adaptation notes updated to match the corrected skeleton
Defect 1 (campaign P2.17): .ok()?-swallowed scoring gave an all-corrupt device dir capturedAt=0 and evicted it FIRST — destroying exactly the evidence most in need of forensic recovery. Corrupt dirs are now exempt and loud; eviction only ever removes cleanly-parseable dirs; when that makes the cap unenforceable the incoming write fails loudly instead.
…pped tabs_persist.rs Independent review (fresheyes iteration 2) found that appending the 3-line #[cfg(test)] registration to tabs_persist.rs (999 lines) would push it past the port/AGENTS.md:81 <=1,000-line cap the plan itself treats as binding. Fix: register tabs_persist_validation_tests.rs from the end of tabs_persist_validation.rs (563 -> 566 lines) instead. That file is a #[path] child of tabs_persist (mod validation, tabs_persist.rs:96-97), so the test module stays a descendant of tabs_persist and pub(crate)/private items remain reachable; the test file's import becomes 'use super::super::*;'. Precedent: tabs_snapshots_tests.rs:987 registers a sibling test file the same way for the same cap reason. Updated scope fence, File Structure table, and Task 4 Files/Interfaces/Steps 1-2/commit command accordingly. tabs_persist.rs is now explicitly Do-NOT-modify.
…sistReason) Defect 2 (wire half, campaign P2.17): the push ack said accepted:true even when the generation was silently dropped. The ack now carries persisted:false + reason on any failed/skipped persistence attempt; fields are omitted on success so pre-change acks stay byte-identical. accepted semantics unchanged. Verified: the frozen SPA has no tabs.sync.ack handler and no runtime validation of server messages. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…w agrees with the golden fixture The dictated summarize() sketch returned the first text-OR-thinking item's text, which for golden turn 1 (items [thinking "pondering", text "first answer"]) yields "pondering" — contradicting the golden's "summary": "first answer" and making Task 3 Step 5's 'Expected: all PASS' unachievable. Fix: summarize() now prefers the first non-empty text item, falling back to the first non-empty thinking item, then tool labels. The golden (the hand-authored cross-language contract pin consumed by Task 4's zod test) is unchanged; turn 1 now summarizes to "first answer". Doc comment states the preference rule and cites golden turn 1 as the pinned example.
Task 1 of the freshclaude restart-parity plan (§2.8 item 2), entirely in crates/freshell-freshagent/src/claude.rs: - FreshClaudeState.cli_index: durable Claude UUID (cliSessionId from sdk.session.init) -> sessions-map key, recorded by the stdout consumer. - ClaudeSession.sidecar_session_id: the id the sidecar keys the session by; handle_send/handle_interrupt now address the sidecar with it (a no-op for created sessions, load-bearing for Task 6's resumed-on-attach sessions). - ClaudeSession.cli_session_id: best-effort copy of the durable id. - spawn_consumer gains a 4th param (sidecar_session_id) and an identity- guarded consumer-exit eviction: when a sidecar dies, its session AND its cli_index entries are evicted, but a stale consumer can never evict a newer session re-registered under the same map key (ledger A9). - handle_kill evicts the killed session's cli_index entries; shutdown clears the index. - Fake sidecar now emits sdk.session.init + sdk.status after created, logs the whole create request per spawn, and exits on send text "__exit__" so tests can exercise the eviction path through the public API. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
The module declaration at crates/freshell-freshagent/src/lib.rs:40 was changed from 'pub mod claude_snapshot' to 'pub(crate) mod claude_snapshot' to align with the plan specification that enforces internal-only visibility. All 4 claude_snapshot tests pass. Clippy and fmt check pass with no violations. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…on deferred to reveal
…th golden fixture Extends claude_snapshot.rs with the JSONL->turns parser (legacy extractChatMessagesFromJsonl contract + ledger A5 real-store fixes: object-with-string-content prompts kept, isMeta/isSidechain/ isCompactSummary/isVisibleInTranscriptOnly skipped, malformed lines never fatal), build_claude_snapshot_json (FreshAgentSnapshotSchema-exact, zod .strict()), ClaudeSnapshotError, and get_claude_snapshot (locate + read + build; revision = transcript mtime ms, fallback turn count). Golden fixture claude-snapshot-golden.json is the contract artifact Task 4 zod-parses; shared sample transcript covers all parse shapes. Stale #[allow(dead_code)] removed from the now-called locators; a single allow remains on get_claude_snapshot (wired by Task 5) and the Io message field (read by Task 5's 500 body). 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
Pins the Rust claude snapshot adapter's output against the FROZEN client's strict zod contract (FreshAgentSnapshotSchema). If the Rust builder drifts from the schema, this test fails before any e2e does, catching cross-language contract violations early. Golden fixture (generated by crates/freshell-freshagent/src/claude_snapshot.rs) contains a representative sample with all item types (text, thinking, tool_use, tool_result) and verifies: - Parse succeeds under strict schema - First turn is user with expected text item - All turnIds are unique Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…ure time, create never blocked 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…es the pending marker Adds the shared async ledger_resolve_identity resolution hook (awaited spawn_blocking around resolve_pending + surface_write_failure) and wires it into the codex adoption bind block after set_meta and BEFORE the associated broadcast (durable-before-answer). handle_codex_candidate_persisted becomes async; its dispatch call site gains .await. Rejected candidates still write nothing (all guards return before the hook). 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…n P1.10 restore re-arm Wire the shared ledger_resolve_identity hook (Task 8) into the opencode and amplifier locator bind blocks: after set_meta, awaited BEFORE the associated broadcast — binding row first, then the spawn-time pending marker is deleted (pinned order). Closes triggers (c)/(d) so every MARKER_MODES resolver also resolves its marker. Pins P1.10: a restore-created opencode pane lacking identity still arms the locator (resume None), carries a durable pending marker across the identity-in-flight window, and resolution lands the binding + deletes the marker. The amplifier resolve-path test gains the mirror-image ledger assertions via a real-ledger fixture. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…down transcripts yield loud dead_session (P1.8 read 2) 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…ead 3) resolve_claude_restore_session_id's rungs 1-2 (in-process, PR #530) now fall THROUGH to the durable pane ledger when the in-process homes have no lineage (a fresh boot), while the A13 live-guard stays a HARD stop. The new rung answers only bound rows and gc_expired tombstones (closed/ superseded rows are never resurrected) and applies an A13-equivalent live-guard against BOTH identity homes: a live identity-registry owner and a REST-shaped live registry row (mode claude + resume_session_id + Running -- invisible to identity and createRequestId lineage). 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
Two Playwright walls (Rust-only, RustServer.restartAbrupt()): - Wall 1 (SIGKILL-within-5s-of-pane-creation): claude binding row and codex pending marker are on disk within 5s of pane creation, the claude row carries the same --session-id the fake CLI received, and everything survives an abrupt SIGKILL + boot scan (marker preserved, nothing quarantined). - Wall 2 (SIGKILL-inside-locator-window): an opencode pane killed mid identity-establishment (row gate never satisfied) leaves a durable pending marker that survives the restart boot scan with zero binding rows -- fresh-by-race stays distinguishable from fresh-by-intent. Registered in playwright.config.ts in BOTH RUST_ONLY_SPECS and the rust-chromium project's testMatch. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
… doc comments 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
gc_locked held the index Mutex across the entire pass, so a large GC batch blocked async readers (handshake stamping, restore rung, ever_bound) for N x ~15-64ms fsyncs. The periodic gc path now snapshots the work list under the guard, then drops and re-acquires the guard per item; each per-item helper (gc_marker_locked / gc_row_locked) re-reads current index state under the re-acquired guard and skips items that no longer qualify. The write-through invariant is preserved: every file mutation and its index update still happen under ONE guard acquisition. boot_scan keeps its single-guard shape (pre-serve, no concurrent readers) and drives the same shared helpers, so semantics, logs, and reports are unchanged on both paths. Also: the 6h GC loop no longer swallows the spawn_blocking JoinError; a panicked or cancelled GC task now logs pane_ledger_gc_join_failed. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…r raises a false durability alarm resolve_pending writes the binding row FIRST (the durable identity), then deletes the pending marker. A marker-delete failure used to propagate as the fn's Err, so ledger_resolve_identity emitted a durability.degraded frame + ERROR claiming the identity "could not be durably recorded" -- factually wrong: it WAS recorded, only cleanup failed, and the boot/GC sweep repairs the stale marker. The failure is now logged at WARN (pane_ledger_marker_delete_failed_on_resolve, with terminal_id + error) and the fn returns Ok(()). A binding-write failure still returns Err -- that is the real durability alarm. New unit test pins the corrected semantics. Also: surface_write_failure's doc comment (and the matching invariants log line) said the frame goes "to attached clients" -- state.broadcast_tx fans to ALL connected clients; the wording now says so. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
attach.ready travels the direct conn_rx channel while replay/live output travels the TERM-09 ConnectionOutputQueue; the connection loop's unbiased select! could deliver already-queued replay frames BEFORE the attach.ready enqueued ahead of them, inverting the documented 'attach.ready, then replay, then live' order. The client arms its pendingReplay window only on ready (src/lib/terminal-attach-seq-state.ts:143), so pre-ready replay frames are mis-sequenced and restored scrollback is intermittently lost. Drain pending direct frames (try_recv) before draining queued output. Reproduced ~4/10 runs by the seeded-replay ordering assertions landing in the next commit (tests/scrollback_restore_seed.rs).
… contract Additive, optional, server->client-only contract change directed by campaign P2.17 defect 2 (honest oversize-drop signal). Client tolerance verified: the SPA has no tabs.sync.ack handler and performs no runtime validation of server messages (shared/ws-protocol.ts design contract). A new roundtrip test pins schema conformance of both ack shapes. DEVIATION (recorded): the plan called for a full 'npm run contract:generate' regen, but shared/ws-protocol.ts on main has PRE-EXISTING drift the frozen contract never absorbed (amplifier.activity.* + terminal.idle added without regen; pane.reconcile.* present in the inventory but absent from the TS), so the port freeze suite is already red on main (8/13) and a full regen breaks freshell-protocol's inventory tests (they pin the Rust port surface). This commit therefore applies ONLY the generator's tabs.sync.ack delta, byte- identical to the generator's output for that entry (verified against an actual regen). Reconciling the wider TS<->contract drift belongs to the lanes that introduced it and is reported as follow-up.
Defect 3 (campaign P2.17): device identity came from whatever *.json read_dir returned first — nondeterministic for half-migrated or hand-edited dirs. Every generation in a dir must now agree on deviceId; disagreement is a loud InvalidData error + invariant alarm, never a guess.
Sweep from campaign P2.17: the orphan-.tmp reaper already has three tests; PERSIST_LOCK poison tolerance and the empty-push ack shape had none. No semantic changes (empty-push design belongs to kata h9vt).
…fields Wire-compat pin for the frozen SPA (campaign P2.17 defect 2): the client has no tabs.sync.ack handler and no runtime validation of server messages, so the new optional ack fields must be inert — no throw, no dispatch, no re-push. Test-only; client src/ untouched.
…ight # Conflicts: # test/e2e-browser/playwright.config.ts
…-preflight # Conflicts: # test/e2e-browser/playwright.config.ts
# Conflicts: # test/e2e-browser/playwright.config.ts
…clippy) Lane A6 (tabs-snapshot-defects) made persist_generation return #[must_use] PersistOutcome; Lane A1's mirrored put() helper in tabs_persist_validation_tests.rs predates that. Same 'let _ =' shape A6's own tabs_persist_tests.rs:32 put() helper uses. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
Cross-lane interaction with main: PR #534 (landed after wave-A lanes forked) added a layout-membership gate to TerminalView.attachTerminal — collectAllTerminalIds(panes.layouts) must reference the terminal id or the attach is suppressed. A4's hidden-rebind test store used empty layouts, so the gate silently swallowed the background-rebind attach. Production is unaffected: hidden panes are always present in panes.layouts, and terminal.created writes the terminalId into the leaf via updatePaneContent before the hydration queue grants the trigger. The test now mirrors that reality by seeding a leaf layout for tab-1/pane-1. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…A4xA2) Cross-lane interaction: freshclaude-restart-parity (A2) adds a restart-parity attach arm (claude.rs handle_attach decision table) that resumes an untracked session IN PLACE when the attach carries a durable UUID and the transcript exists. On the A4 lane alone (pre-parity fallback #529) the server round-tripped INVALID_SESSION_ID and recovery was proven by a CHANGED createRequestId via the client's .lost re-create; in the merged tree that discriminator can never fire for a claude pane with durable identity. New combined-tree evidence, verified against a live run: - pre-restart: poll for the durable UUID on the pane (deterministic attach identity) - post-restart WITHOUT reveal: usable status AND server-side resume proof from the sidecar request log (FAKE_CLAUDE_SIDECAR_LOG create with resumeSessionId = durable UUID or its transcript .jsonl path -- the log previously pointed at FAKE_CLAUDE_ARGV_LOG, which the sidecar never writes) - createRequestId asserted STABLE: the .lost re-create fallback must not fire (no duplicate-create storm) 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…herence, A2xA3 dual claude identity stores) Two seams no single lane's spec covers: - A1xA3: reload + abrupt restart keeps the pane<->ledger createRequestId join coherent. Reload must NOT re-mint (A1's contract). The census re-create after restart MAY re-mint (BindingRow doc: advisory, latest-observed) -- the pinned contract is CONVERGENCE: the ledger row's advisory key equals the pane's current key with no stale row left behind to strand lookup_by_create_request_id. - A2xA3: one abrupt restart restores BOTH claude identity stores -- the terminal pane's ledger binding (terminal.rs create path) and the freshclaude pane's sidecar cliSessionId index (claude.rs resume arm) -- with no cross-talk: the fresh-agent UUID never appears in the pane ledger, the terminal's session stays bound, nothing quarantined. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
… the honest ack Wave-A cross-lane pin: a tabs.sync.push whose pane payloads carry Lane A1's createRequestId (both mint shapes: 32-hex server mint and 21-char client nanoid) must ride through Lane A6's honest-persist ack path unchanged -- accepted, persisted (success ack OMITS persisted/ persistReason on the wire), and the key round-trips into the persisted generation verbatim. 🤖 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 A of the restart-resilience campaign — five lanes merged and verified as a combined tree plus integration fixes.
Lanes (preserved as merge parents):
Plus: fix/ws-attach-ready-ordering (attach.ready delivered before queued output frames — salvaged from the withdrawn scrollback lane; scrollback persistence itself was withdrawn by user ruling — provider resume covers it).
Integration commits: A1×A6 clippy fix, A4×#534 test seed, A4×A2 resume-in-place e2e contract, new cross-lane interaction spec (A1×A3, A2×A3), A6×A1 honest-ack seam pin.
Verification: cargo workspace 1841 tests green, clippy -D warnings green, coordinated JS suites green (4050 client), 36/36 e2e twice (zero flakes) incl. restore contract wall (6 expected-fail pins unchanged, re-validated), port-contract drift unchanged vs main (pre-existing, not worsened).