Skip to content

fix(coding-agent): keep snapshot catch-up failures isolated #11

Description

@rynfar

Problem

The daemon can reuse a positional snapshot ID (session-generation-event cursor) after snapshot bytes change. Catch-up then detects different bytes under the same ID, closes the worker control channel, and aborts every resident session.

This has recurred in the Pylon-installed fork under large transcripts and high RLM child-update fan-in. Prime upstream issue PrimeIntellect-ai#1229 described the same invariant but closed without a fix.

Required outcome

  • Snapshot transfer identity uniquely identifies immutable bytes while event cursors keep their ordering meaning.
  • A bad snapshot generation is retired and retried without recycling an otherwise healthy worker.
  • Worker recovery waits before session reuse and preserves session ownership.
  • Child snapshot fan-in is reduced without dropping terminal child state.
  • Regression tests cover same-cursor byte changes, mismatch isolation, worker liveness, and recovery.

Compatibility

Keep the existing daemon protocol backward-compatible unless a capability-gated change is required. Preserve stock Prime behavior outside the Pylon integration boundary.

Activity

  1. rynfar commented on Aug 30, 2026

    @rynfar
    Author

    Coordination hold after PR #12

    PR #12 is merged into pylon as f728316dabbaa85aa561e6d6b08550ed337574be. The documented dependency order now advances to this issue.

    An existing implementation worktree is present and must be treated as protected:

    • branch: fix/snapshot-recovery-integrity
    • worktree: /Users/rynfar/.prime/worktrees/prime-snapshot-recovery-integrity
    • committed head: 2e189d2ac951a92631847ffb21151f19c6b38d71, based on the former pylon@8551520f...
    • current state: one committed implementation plus 16 modified files and one untracked test

    This comment is a collision-avoidance hold, not a takeover claim. Do not edit, rebase, merge, reset, clean, or otherwise mutate that worktree until its owner checkpoints the current work and records the required issue claim.

    The owner claim must name the branch/worktree, complete owned file inventory, contract and compatibility boundary, dependency on pylon@f728316d, test plan, reviewers, and merge order. Only after the dirty state is checkpointed should the owner integrate the new origin/pylon; the committed overlap with PR #12 is limited to packages/coding-agent/src/core/agent-session.ts and a read-only merge-tree review found it automatically mergeable. Combined snapshot-integrity and correlated-lifecycle coverage is required afterward.

    The old #8 upstream candidate remains stale and must not be used as review evidence. It will be regenerated only after #11 merges.

  2. rynfar commented on Aug 30, 2026

    @rynfar
    Author

    Correction to the worktree count above: current git status --porcelain=v2 shows 15 modified tracked files plus one untracked test, not 16 modified files. The branch, head, worktree, and collision-avoidance hold are unchanged.

  3. rynfar commented on Aug 30, 2026

    @rynfar
    Author

    Recovery ownership claim

    No reachable active agent owns or knows the owner of the protected worktree; both active sibling sessions confirmed they have not touched it. I am taking over this issue only in a new isolated worktree. The original dirty worktree remains untouched and protected.

    Owned inventory

    • .pylon/features.yaml
    • .pylon/upstream-review.md
    • packages/coding-agent/.changes/11-snapshot-recovery-integrity.md
    • packages/coding-agent/src/core/agent-session.ts
    • packages/coding-agent/src/modes/agent-connection/daemon-agent-connection.ts
    • packages/coding-agent/src/modes/daemon/active-session-state.ts
    • packages/coding-agent/src/modes/daemon/daemon-mode.ts
    • packages/coding-agent/src/modes/daemon/daemon-protocol.ts
    • packages/coding-agent/src/modes/daemon/daemon-session-list.ts
    • packages/coding-agent/src/modes/daemon/daemon-supervisor.ts
    • packages/coding-agent/src/modes/daemon/daemon-worker-client.ts
    • packages/coding-agent/src/modes/daemon/snapshot-transcript-cache.ts
    • packages/coding-agent/src/modes/session-worker/private-framing.ts
    • packages/coding-agent/test/agent-connection-daemon.test.ts
    • packages/coding-agent/test/agent-session-recursion.test.ts
    • packages/coding-agent/test/daemon-mode.test.ts
    • packages/coding-agent/test/daemon-protocol.test.ts
    • packages/coding-agent/test/daemon-session-list.test.ts
    • packages/coding-agent/test/daemon-supervisor-lazy-subagents.test.ts
    • packages/coding-agent/test/daemon-supervisor-monitor.test.ts
    • packages/coding-agent/test/daemon-supervisor-process.test.ts
    • packages/coding-agent/test/daemon-version-compatibility.test.ts
    • packages/coding-agent/test/session-worker-private-framing.test.ts
    • packages/coding-agent/test/snapshot-transcript-cache.test.ts
    • packages/coding-agent/test/suite/regressions/4601-worker-snapshot-cache.test.ts
    • packages/coding-agent/test/suite/regressions/4602-snapshot-transfer-idempotency.test.ts
    • packages/coding-agent/test/suite/regressions/4677-snapshot-catchup-replacement.test.ts

    Contract

    • preserve protocol 7 and backward compatibility;
    • expose immutable_snapshot_transfer_v1 only through explicit daemon/worker capability negotiation;
    • keep ordering cursors separate from opaque transfer identities that name immutable bytes;
    • prepare bounded immutable memory/file-backed chunks before deferred streaming, with cancellation and private cleanup on all exits;
    • retire and retry only the bad generation without closing an otherwise healthy worker;
    • bound retries and prove worker/recovery ownership before session reuse;
    • reduce child snapshot fan-in without dropping terminal child state;
    • keep socket paths, session paths, raw snapshot payloads, worker tokens, and diagnostics host-private;
    • provide the public worker/descriptor integrity evidence needed by Comet chore(pylon): merge Prime upstream through d60fab8a #5 or document the exact remaining gap.

    Validation and review

    The recovery will first reproduce the protected tree without mutating it, then integrate f728316d, resolve/review the agent-session.ts overlap, and run focused snapshot/protocol/connection/supervisor/recursion tests, real-process isolation/recovery tests, pinned stock-v0.8.1 compatibility when the artifact is available, full checks, and independent Comet/Pylon-consumer rereviews. Any inherited test claim will be rerun rather than trusted.

    Merge order: #12 (done) → this issue → regenerated/reviewed #8 → #13 → Comet #5. No production claim will be made until the recovered implementation and its public integrity proof pass review.

  4. rynfar commented on Aug 30, 2026

    @rynfar
    Author

    Additional merge blocker: authoritative owned-session cleanup proof

    A focused read-only review of the recovered candidate found that the current client_owned_sessions behavior is not strong enough for the cross-process host contract. This is separate from the four snapshot/framing/authority P1 repairs now committed at ba86fb349ba43bf4179b58bde19b34eacce4d8f2.

    Blocking findings:

    • complete_owned_session awaits exact worker stop, but deleteWorkerDescriptor() swallows descriptor-removal failures after removing the in-memory registration. The command can therefore report success while a stale durable descriptor remains.
    • Owner disconnect cleanup is best-effort. An initial stop-tombstone persistence failure is only logged and is not rearmed, so a crashed owner can leave a client-owned worker live indefinitely until supervisor restart. Descriptor-removal failure can likewise leave a stale descriptor without a current-supervisor retry.
    • Ownership reconnect only works for the same DaemonClient object's private protocol ID. A replacement host process cannot impersonate the original owner safely.
    • A different client has no public absence proof. Owner-filtered list, global busy count, and asynchronous shutdown admission cannot prove that both the exact worker generation and descriptor are gone.

    Required before merge (exact names remain reviewable):

    1. Add a new server capability such as authoritative_owned_session_cleanup_v1; do not reinterpret stock 0.8.1's client_owned_sessions offer.
    2. Make durable cleanup fail closed: remove ancillary journals first and the registration descriptor last; retain the tombstoned in-memory worker and retry on tombstone/descriptor failures; never return successful completion until exact process-generation and descriptor absence are proved.
    3. Add a non-owning, privacy-safe query such as get_owned_session_cleanup { activeSessionId } -> active | stopping | settled. settled must mean no in-memory/durable registration and no exact worker process generation, without exposing PID, path, owner ID, or raw descriptor.
    4. Add deterministic descriptor-unlink and tombstone-write failure regressions, direct completion proof, real socket-crash polling from a different client, and supervisor-replacement coverage. Stock 0.8.1 must lack the new capability and the client must reject the query locally.

    The compatibility test now waits for natural supervisor and worker exit in both stock/current directions and asserts zero worker descriptors; forced process cleanup is limited to afterEach fallback. This improves evidence but does not replace the public runtime proof above.

    The candidate remains blocked. No PR should be opened or merged until this contract is implemented and independently rereviewed.

  5. rynfar commented on Aug 30, 2026

    @rynfar
    Author

    Ownership inventory amendment for the authoritative-cleanup blocker

    The accepted cleanup design requires a new public capability-gated query and exported result type. Before editing, I am extending the existing recovery claim to these additional paths:

    • packages/coding-agent/src/modes/index.ts
    • packages/coding-agent/src/index.ts
    • packages/coding-agent/test/daemon-client.test.ts
    • packages/coding-agent/docs/agent-connection.md

    The already-claimed protocol, supervisor, process/compatibility tests, .pylon review files, and change note remain owned by the same isolated recovery worktree. The new surface will keep protocol 7, add an additive schema revision and supervisor-only authoritative_owned_session_cleanup_v1 offer, reject stock 0.8.1 locally, expose only active | stopping | settled, and keep all worker/process/path/owner details private. No other checkout may edit these paths until this claim is released.

  6. rynfar commented on Aug 30, 2026

    @rynfar
    Author

    Exact candidate awaiting final independent review

    The recovery branch is now pushed at exact clean head 3ab9110d11643c8bc1da9472843a1b1c17b45d2c.

    New commits after the previously recorded four-blocker head:

    • 5d1c9e224b04c351bb1cdb69b216bea0fdd82d3b — fences attach admission, closes committed-but-interrupted private frames, and makes worker authority current-channel/current-roster scoped.
    • 3ab9110d11643c8bc1da9472843a1b1c17b45d2c — adds supervisor-only authoritative_owned_session_cleanup_v1, schema 27, privacy-safe get_owned_session_cleanup, descriptor-last verified removal, durable retry finalization, direct/crash/replacement proof, stock-v0.8.1 local rejection, documentation, and review ledger updates.

    Validation at this exact source state:

    • 688 focused tests across 13 protocol/client/session/snapshot/supervisor files;
    • 40 correlated lifecycle/queue/continuation tests;
    • 12 real-process supervisor tests, 8 fixture-gated skips;
    • both stock/current v0.8.1 adoption directions;
    • npm run check, root build, installer/browser smoke, and git diff --check.

    The real owner-socket-loss regression forced a visible stopping interval with SIGSTOP, then a different client proved settled, exact worker exit, and zero descriptors. The build-generated model catalog was restored.

    Two independent read-only exact-head reviews are still running. The candidate remains blocked from PR creation until both conclude and any P0/P1 findings are repaired and revalidated.

  7. rynfar commented on Aug 30, 2026

    @rynfar
    Author

    Five final-review P1 races repaired

    Exact clean head f163bddc8a76d5ecd02b848cdbcc80f7b9fa0753 adds f163bddc8 on top of the prior candidate and is pushed to the recovery branch.

    Repairs:

    1. Stop intent advances a revision, becomes durable, and then joins any admitted recovery/deferred-recovery task before process or descriptor absence can be proved. Every recovery/adoption path revalidates the current map object, stop revision, tombstone state, pid/start generation, and authenticated channel after asynchronous gates and before spawn or persistence.
    2. Shutdown no longer disables finalizers. It retains registry ownership, the catalog, and server while tombstone/process/archive/descriptor cleanup drains; non-timeout cleanup failures cannot abort the only retry path.
    3. Same-generation removal is single-flighted through one shared stop operation, so concurrent complete_owned_session calls cannot race descriptor removal.
    4. Finalizers are keyed by pid, process start ID, and stop revision. An old finalizer cannot suppress or clear a newer stop generation.
    5. Client-owned create handling rechecks owner liveness after the worker is registered, and a disconnected pre-ready command cannot cancel its cleanup timer.

    New deterministic proof includes a pre-spawn recovery fence, recovery-join ordering, concurrent completion, generation handoff, shutdown drain, and a real socket test that commits a create, proves no descriptor exists before disconnect, then observes the late registration progress to authoritative settled, process absence, and zero descriptors.

    Validation at this source state:

    • 693 focused tests across 13 files;
    • 40 correlated lifecycle/queue/continuation tests;
    • 13 real-process supervisor tests passed with 8 fixture-gated skips;
    • both stock/current v0.8.1 adoption directions (the combined compatibility run passed on repeat after one transient temp-directory cleanup race);
    • full npm run check, root build, installer/browser smoke, and git diff --check.

    The two original reviewers are re-reviewing exact head f163bddc8a76d5ecd02b848cdbcc80f7b9fa0753. The PR gate remains closed pending both approvals.

  8. rynfar commented on Aug 30, 2026

    @rynfar
    Author

    Published-recovery and reentrant-shutdown races repaired

    Exact clean pushed head 90e0f092c0b79d5ef2c4538b1fa2be69d61da6e9 adds 90e0f092c after the final re-review findings.

    • A canceled existing-worker replacement rollback still stops and joins its direct child, but it now retains the shared in-memory registration when an outer durable tombstone or supervisor shutdown owns cleanup. The outer authoritative stop can therefore verify and remove the descriptor last instead of losing its finalizer route.
    • Supervisor shutdown is now a single task. Reentrant public shutdown commands or signals join the active cleanup drain and cannot call process.exit while tombstone/archive/descriptor retries are pending.
    • Deterministic monitor regressions exercise a replacement child after publication with concurrent exact cleanup, and a second shutdown call while descriptor cleanup is blocked.

    Revalidated at this source state: 694 focused tests, 40 correlated-lifecycle tests, 13 real-process tests with 8 fixture skips, both stock/current v0.8.1 adoption directions, full check/build, installer/browser smoke, and git diff --check. The one earlier 36 MiB event-loop threshold miss passed both its immediate isolated rerun and the complete 694-test rerun and was treated as environmental load, not a source failure.

    All three reviewers are rechecking exact head 90e0f092c0b79d5ef2c4538b1fa2be69d61da6e9. The PR gate remains closed pending approvals.

  9. rynfar commented on Aug 30, 2026

    @rynfar
    Author

    Ordinary recovery rollback now retains cleanup authority

    Exact clean pushed head 49c592ffc863263123020c7a8aaa97158bbbc7ec adds 49c592ffc.

    The published-replacement preservation rule no longer depends on a direct child handle. Any non-descriptor recovery cleanup retains the same mapped registration when an outer tombstone or supervisor shutdown owns cleanup. The deterministic regression now executes both the cancellation/direct-child branch and the ordinary error/no-direct-child branch before allowing the outer descriptor-last stop to settle.

    Exact validation: 695 focused tests, 40 correlated-lifecycle tests, 13 real-process tests with 8 fixture skips, both stock/current v0.8.1 directions, full check/build, installer/browser smoke, and git diff --check.

    All three reviewers are rechecking 49c592ffc863263123020c7a8aaa97158bbbc7ec. The PR remains gated.

  10. rynfar commented on Aug 30, 2026

    @rynfar
    Author

    Pull request opened after exact-head approval

    PR #14 is open against pylon at exact head 49c592ffc863263123020c7a8aaa97158bbbc7ec.

    Three independent read-only reviews approved this exact commit with no P0/P1 findings: security/correctness, concurrency/durability, and a fresh full-regression review. Local validation and compatibility receipts are recorded in the PR body and comments above.

    Hosted checks are now queued. Merge remains gated on all required checks, resolved conversations, and recorded maintainer approval.

  11. rynfar commented on Aug 30, 2026

    @rynfar
    Author

    PR #14 hosted-CI repair receipt

    The initial hosted run at 49c592ffc863263123020c7a8aaa97158bbbc7ec passed build/check, process smoke, kernel, compatibility coverage, and most test jobs, but exposed six failures in coding-agent shards 1/3 and 3/3.

    Independent diagnosis found no production defect:

    • heartbeat and resume tests used stale partial doubles that violated the new exact worker-identity and post-validation attach-admission contracts;
    • update-restart assertions captured serialized Buffer output as if it were always a string;
    • the two-pass 5 MiB spill/re-encode regression retained a 15-second per-test timeout that was too tight under hosted shard load.

    Test-only repairs:

    • c37f4bddb360826b5878a9989f3779bea5ee2e33
    • c5a34dffcd44eaaa4d66ea43786d19260a10e7f2 (widens only that expensive regression's bounded timeout to 60 seconds)

    Validation:

    • the 55 affected tests pass at the final head;
    • local replay of hosted shard 1: 1,473 passed / 24 skipped;
    • local replay of hosted shard 3: 1,243 passed / 22 skipped;
    • exact final-head npm run check and pre-commit checks pass;
    • production files remain byte-for-byte unchanged from the three-reviewer-approved 49c592ffc863263123020c7a8aaa97158bbbc7ec.

    Updated upstream synthetic receipt:

    • merge 8afe6a8d910e0f9fc3d896aa0bae871c2c59aeba / tree f6cff7f22608329b3e9fd4e99c4bd8c66ad14ccd;
    • exact parents c5a34dffcd44eaaa4d66ea43786d19260a10e7f2 and a903d4b6768f484bd6d459b7b0aa7dee38e461e2;
    • same independently approved conflict resolutions as the prior synthetic merge;
    • the new synthetic tree differs from the prior approved synthetic tree only in the four test-only CI repairs;
    • exact synthetic npm run check and 55/55 affected tests pass; exact-commit synthetic re-review is queued.

    The final hosted run for c5a34dffcd44eaaa4d66ea43786d19260a10e7f2 is now in progress. PR #14 remains blocked until every required check, conversation, and maintainer-approval gate is satisfied.

  12. rynfar commented on Aug 30, 2026

    @rynfar
    Author

    Exact final-head upstream synthetic re-review: approved with no P0/P1.

    • synthetic merge: 8afe6a8d910e0f9fc3d896aa0bae871c2c59aeba
    • tree: f6cff7f22608329b3e9fd4e99c4bd8c66ad14ccd
    • parents: c5a34dffcd44eaaa4d66ea43786d19260a10e7f2 + a903d4b6768f484bd6d459b7b0aa7dee38e461e2
    • compared with prior approved synthetic 3c12d54895edb69ff7db64abb928605f15ba6ca3: exactly the four test-only hosted-CI repair files differ; no production path differs
    • all four resolved-conflict blobs are byte-for-byte identical to the prior approved synthetic
    • git diff --check, exact-tree npm run check, and 55/55 repaired tests pass

    The final hosted check run remains the only current validation gate before conversation audit and exact-head maintainer approval.

  13. added 2 commits that reference this issue on Aug 30, 2026
    8b504e3
    70b6530
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions