Repository navigation
Substrate single-writer chunk cache + engram-substrated ADR (ADR 0069/0070, stacked on #581) - #583
Merged
Merged
Conversation
nikhilunni
force-pushed
the
epic/545-rung1-cancel-evict
branch
from
July 6, 2026 13:29
8f9e81d to
b7f4185
Compare
nikhilunni
force-pushed
the
epic/547-single-writer
branch
from
July 6, 2026 13:30
3a20ee9 to
b5c653d
Compare
nikhilunni
changed the base branch from
epic/545-rung1-cancel-evict
to
epic/545-rungs-2-3
July 6, 2026 13:30
nikhilunni
force-pushed
the
epic/545-rungs-2-3
branch
2 times, most recently
from
July 6, 2026 22:06
a7064cd to
a558879
Compare
nikhilunni
force-pushed
the
epic/547-single-writer
branch
from
July 7, 2026 03:09
b5c653d to
f845653
Compare
nikhilunni
added a commit
that referenced
this pull request
Jul 7, 2026
…substrated design (#547) Rebased onto main past #585/#590-#596; ADRs renumbered 0072/0073 -> 0075/0076 (main's 0072 = substrate-survey-evidence, 0073 = binding-epoch). See PR #583 for the full description. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014jJi2vqAaxt3Q5UKxbe4Gx
nikhilunni
marked this pull request as ready for review
July 7, 2026 03:09
nikhilunni
force-pushed
the
epic/547-single-writer
branch
from
July 7, 2026 03:24
f845653 to
68375d6
Compare
nikhilunni
added a commit
that referenced
this pull request
Jul 7, 2026
…substrated design (#547) Rebased onto main past #585/#590-#596; ADRs renumbered 0072/0073 -> 0075/0076 (main's 0072 = substrate-survey-evidence, 0073 = binding-epoch). See PR #583 for the full description. Review fix folded in: the client replays `Hello` on every fresh dial (with_conn), not just at startup — the server's session-chunk pin set and proto check are per-connection state, and the designed-for reconnect (a rolled host-agent's successor, holding an empty pin set) is exactly when silently skipping the replay would leave the handler unpinned for the rest of the sandbox's life. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014jJi2vqAaxt3Q5UKxbe4Gx
…substrated design (#547) Rebased onto main past #585/#590-#596; ADRs renumbered 0072/0073 -> 0075/0076 (main's 0072 = substrate-survey-evidence, 0073 = binding-epoch). See PR #583 for the full description. Review fix folded in: the client replays `Hello` on every fresh dial (with_conn), not just at startup — the server's session-chunk pin set and proto check are per-connection state, and the designed-for reconnect (a rolled host-agent's successor, holding an empty pin set) is exactly when silently skipping the replay would leave the handler unpinned for the rest of the sandbox's life. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014jJi2vqAaxt3Q5UKxbe4Gx
nikhilunni
force-pushed
the
epic/547-single-writer
branch
from
July 7, 2026 03:39
68375d6 to
5bee563
Compare
nikhilunni
added a commit
that referenced
this pull request
Jul 7, 2026
…ntimeSpec, Revive (#544) Rebased onto main past #583/#585/#590-#596. Renumbered at land: ADR 0074 -> 0077 (main's 0074 = parking ladder); migrations 0087-0089 -> 0088-0090 (main's applied high-water = 0087). The queue_prompt column references died in the rebase (#592 removed it). Five pre-merge review findings fixed (see the ADR's divergence log): - get_session projection: the selected_skills removal left a missing comma aliasing live_disk_manifest_version AS park_rung — every session read un-parked (frozen-VM-advertised-Active on the rung-2 ascent) and live_disk_manifest read None. Live-PG round-trip test pins the projection now. - fork-at-attach minted an UNPUBLISHED (uuid, v0) into manifest_ref; sessions evicted before their first flush were unevictable and zero-dirty captures unresumable. Split: fork_identity is adopted at v1 by the FIRST publish; manifest_ref stays the resolvable base until then (+ regression test). - durable_head advanced on recoverable=false rows; now gated, and a demote of the current head re-points to the newest recoverable. - prepare_from_row swallowed RuntimeSpec read errors into "no skills"; now propagates (scanner/resume retry is the recovery). - the Idle->Created harness-failed park emitted no StatusChanged and swallowed transition failures; now emits + propagates. Accepted (zero users): no selected_skills backfill into session_runtime_specs; pre-deploy sessions lose their skill selection on the next re-prepare. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014jJi2vqAaxt3Q5UKxbe4Gx
nikhilunni
added a commit
that referenced
this pull request
Jul 7, 2026
…ntimeSpec, Revive (#544) Rebased onto main past #583/#585/#590-#596. Renumbered at land: ADR 0074 -> 0077 (main's 0074 = parking ladder); migrations 0087-0089 -> 0088-0090 (main's applied high-water = 0087). The queue_prompt column references died in the rebase (#592 removed it). Five pre-merge review findings fixed (see the ADR's divergence log): - get_session projection: the selected_skills removal left a missing comma aliasing live_disk_manifest_version AS park_rung — every session read un-parked (frozen-VM-advertised-Active on the rung-2 ascent) and live_disk_manifest read None. Live-PG round-trip test pins the projection now. - fork-at-attach minted an UNPUBLISHED (uuid, v0) into manifest_ref; sessions evicted before their first flush were unevictable and zero-dirty captures unresumable. Split: fork_identity is adopted at v1 by the FIRST publish; manifest_ref stays the resolvable base until then (+ regression test). - durable_head advanced on recoverable=false rows; now gated, and a demote of the current head re-points to the newest recoverable. - prepare_from_row swallowed RuntimeSpec read errors into "no skills"; now propagates (scanner/resume retry is the recovery). - the Idle->Created harness-failed park emitted no StatusChanged and swallowed transition failures; now emits + propagates. Accepted (zero users): no selected_skills backfill into session_runtime_specs; pre-deploy sessions lose their skill selection on the next re-prepare. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014jJi2vqAaxt3Q5UKxbe4Gx
nikhilunni
added a commit
that referenced
this pull request
Jul 7, 2026
…he engine terminates (#597) The ADR 0073 Superseded rejection arm logged "exiting cleanly" and broke the connection loop — but main holds a cmd_tx clone for the whole process, so `engine.await` never completed: the harness lingered ALIVE with its connection loop (and SIGUSR1 handler) dead. agentd's SpawnHarness reattach arm then saw a live pid on every subsequent start_agent, nudged it forever, and never respawned a fresh harness — so every resume from a live-harness checkpoint (evict_local, and the ADR 0028 host-loss recovery path) wedged prompt delivery permanently: the outbox looped NotFound -> "harness reattach issued" indefinitely (prod session 7ed23d9f, 2026-07-06; the #583 verification caught it). Fix: drop cmd_tx before `engine.await` — the engine's designed ChannelClosed teardown ("all command senders gone = the connection loop exited = process teardown") fires, the process exits, agentd reaps it, and the next SpawnHarness spawns a FRESH harness with the current binding epoch. The ChannelClosed arm now also SIGINTs claude (it skips the reap block, and an orphaned twin would contend with the successor's --resume on the same transcript; claude flushes per-message, so SIGINT is resume-safe). The idle-evict path never hit this (its drain produces claude-free snapshots); only checkpoints capturing a LIVE harness restore into the stale-epoch -> Superseded -> phantom-exit shape. Claude-Session: https://claude.ai/code/session_014jJi2vqAaxt3Q5UKxbe4Gx Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
nikhilunni
added a commit
that referenced
this pull request
Jul 7, 2026
…ntimeSpec, Revive (#544) (#584) Rebased onto main past #583/#585/#590-#596. Renumbered at land: ADR 0074 -> 0077 (main's 0074 = parking ladder); migrations 0087-0089 -> 0088-0090 (main's applied high-water = 0087). The queue_prompt column references died in the rebase (#592 removed it). Five pre-merge review findings fixed (see the ADR's divergence log): - get_session projection: the selected_skills removal left a missing comma aliasing live_disk_manifest_version AS park_rung — every session read un-parked (frozen-VM-advertised-Active on the rung-2 ascent) and live_disk_manifest read None. Live-PG round-trip test pins the projection now. - fork-at-attach minted an UNPUBLISHED (uuid, v0) into manifest_ref; sessions evicted before their first flush were unevictable and zero-dirty captures unresumable. Split: fork_identity is adopted at v1 by the FIRST publish; manifest_ref stays the resolvable base until then (+ regression test). - durable_head advanced on recoverable=false rows; now gated, and a demote of the current head re-points to the newest recoverable. - prepare_from_row swallowed RuntimeSpec read errors into "no skills"; now propagates (scanner/resume retry is the recovery). - the Idle->Created harness-failed park emitted no StatusChanged and swallowed transition failures; now emits + propagates. Accepted (zero users): no selected_skills backfill into session_runtime_specs; pre-deploy sessions lose their skill selection on the next re-prepare. Claude-Session: https://claude.ai/code/session_014jJi2vqAaxt3Q5UKxbe4Gx Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
nikhilunni
added a commit
that referenced
this pull request
Jul 7, 2026
GCS-free resume moves 4+5, rebased onto main past #583/#584/#597. Close the put_chunk exists()-arm write-through hole + add put_chunk_unchecked to drop the per-dirty-chunk GCS HEAD on flush paths. ADR renumbered 0075 -> 0078 at land (main's 0075 = substrate-single-writer, #583). Review fix folded in: the flush path's explicit cache.put after put_chunk_unchecked was a duplicate 16 MiB local write per flushed chunk (the store's internal write-through already warms the ONE cache — prod wires it in host-agent main). Cut to one path; the flush_write_throughs_chunks_into_local_cache test now mirrors the prod wiring (store carries the cache). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014jJi2vqAaxt3Q5UKxbe4Gx
nikhilunni
added a commit
that referenced
this pull request
Jul 7, 2026
GCS-free resume moves 4+5, rebased onto main past #583/#584/#597. Close the put_chunk exists()-arm write-through hole + add put_chunk_unchecked to drop the per-dirty-chunk GCS HEAD on flush paths. ADR renumbered 0075 -> 0078 at land (main's 0075 = substrate-single-writer, #583). The flush path keeps its explicit cache.put alongside the store-internal write-through: cache and store travel separately into the disk backend, so the store carrying a cache is prod wiring, not a structural guarantee — collapsing to one cache identity is ADR 0076 (substrated) territory. (A review pass tried cutting it; reverted — the CI-proven shape stands. Note: eviction_finalize_redrive fails on the DEV-VM for main and this branch alike — environmental, tracked in the dev-vm pitfalls; CI is the arbiter for that suite.) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014jJi2vqAaxt3Q5UKxbe4Gx
nikhilunni
added a commit
that referenced
this pull request
Jul 7, 2026
GCS-free resume moves 4+5, rebased onto main past #583/#584/#597. Close the put_chunk exists()-arm write-through hole + add put_chunk_unchecked to drop the per-dirty-chunk GCS HEAD on flush paths. ADR renumbered 0075 -> 0078 at land (main's 0075 = substrate-single-writer, #583). The flush path keeps its explicit cache.put alongside the store-internal write-through: cache and store travel separately into the disk backend, so the store carrying a cache is prod wiring, not a structural guarantee — collapsing to one cache identity is ADR 0076 (substrated) territory. (A review pass tried cutting it; reverted — the CI-proven shape stands. Note: eviction_finalize_redrive fails on the DEV-VM for main and this branch alike — environmental, tracked in the dev-vm pitfalls; CI is the arbiter for that suite.) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014jJi2vqAaxt3Q5UKxbe4Gx
nikhilunni
added a commit
that referenced
this pull request
Jul 7, 2026
GCS-free resume moves 4+5, rebased onto main past #583/#584/#597. Close the put_chunk exists()-arm write-through hole + add put_chunk_unchecked to drop the per-dirty-chunk GCS HEAD on flush paths. ADR renumbered 0075 -> 0078 at land (main's 0075 = substrate-single-writer, #583). The flush path keeps its explicit cache.put alongside the store-internal write-through: cache and store travel separately into the disk backend, so the store carrying a cache is prod wiring, not a structural guarantee — collapsing to one cache identity is ADR 0076 (substrated) territory. (A review pass tried cutting it; reverted — the CI-proven shape stands. Note: eviction_finalize_redrive fails on the DEV-VM for main and this branch alike — environmental, tracked in the dev-vm pitfalls; CI is the arbiter for that suite.) Claude-Session: https://claude.ai/code/session_014jJi2vqAaxt3Q5UKxbe4Gx Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This was referenced Jul 8, 2026
nikhilunni
added a commit
that referenced
this pull request
Jul 9, 2026
Phases 1+2 verified shipped in #583 (43c87b9): engram-substrate-proto, ChunkCacheReader, the populate server (pin→get→open→send-fd→unpin), the handler's 3-step read path, and the Hello/HelloAck readiness handshake. Divergence log records: one-PR delivery, per-connection pinning with Hello-replay-on-reconnect (vs the sketched sandbox-lifetime pin_all), and the ADR 0080 interaction (materializer = new in-process caller, single-writer holds by construction; its scratch statvfs guard is deliberately outside the cache's pin/evict bookkeeping). ADR 0076 (engram-substrated) deliberately remains Proposed/gated. Closes the in-repo scope of #547. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm24mF1hXuphYzXCLVxQ3d
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.
Interim single-writer chunk-cache discipline + the
engram-substrateddesign ADR (#547). Stacked on #581 (which stacks on #580) — merge #580 → #581 → this. This PR's own delta is its last three commits (the two ADRs, the single-writer implementation).What this does
The NVMe chunk-cache directory had 1 + N writers per host (host-agent + one uffd handler per Uffd sandbox), each with its own in-memory singleflight / pin set / evictor over shared bytes — the structural root of the #437
.partialrace, the pin-invisible eviction hazard (a handler sweep evicting host-agent-pinned base chunks), duplicate GCS fetches, and 583+55/7d ENOENTs. This makes exactly one process the writer, enforced at the type level:engram-substrate-proto(new crate): sync bincode framing + SCM_RIGHTS fd passing, no tokio/tonic (fault-path adjacency — thepeer.rsdiscipline).ChunkCacheReader: open/read/contains only — a client that compiles cannot mutate the directory.Helloanswers live probes (statfs TMPFS_MAGIC + cache-writability),Populateruns pin →cache.get→ open → send-fd → unpin against the one cache instance, so the global singleflight / pins / Hard host disk budget for the chunk cache: periodic enforcement, single evictor, pin-aware arithmetic, dedicated volume #528 budget govern handler traffic. Phase 2 pins the session manifest for the connection (= sandbox) lifetime.Helloruns before the FC-facing UDS binds, so an unready host fails at spawn instead of booting intoregister memory … userfaultfd.The daemon is ADR-only (gated)
ADR 0070 (
engram-substrated) is delivered as a design document with the crash-story as an explicit gate: a daemon crash closes every uffd → zero-fill guest corruption, andpidfd_getfdneeds a live donor. No implementation issue may be filed until fd-retention is chosen + prototyped. The interim protocol/reader/handshake port to the daemon unchanged.Tests
proto framing + socketpair fd round-trip; reader hit/miss; server singleflight-collapse + Hello probes; pinned-chunk-survives-populate-pressure (unrepresentable pre-0069); degraded-mode fallback-writes-nothing;
substrate_populatereal-client↔real-server integration (Linux-gated fd passing — wire the new FC-lane test intoci.yml's--testlist at merge; the socket-contract property is covered cross-platform, the VM path is dev-vm-validated per the CI test-sizing rule).just check+ full-workspace musl clippy green.VZ/Process
N/A by construction (no out-of-process cache clients off FC/Linux) — the host-agent's in-process cache is already the sole writer there; stated explicitly in ADR 0069.
Note
Includes a small
fix(linux-tests)commit (ADR 0067 fallout in cfg(linux)-gated FC test literals, caught by the musl cross-check) that is cherry-picked down to #580 so its FC lane heals; it appears here only because of the stack.🤖 Generated with Claude Code
https://claude.ai/code/session_014jJi2vqAaxt3Q5UKxbe4Gx