Context
Vega's non-blocking follow-up on PR #15602 (review PRR_kwDODSospM8AAAABGjutTQ): the get_sandman_handoff read tool's cloud utility is gated on the unshipped writer-side persistence — "worth a tracked ticket so the read tool isn't half-useful in the cloud." #15599 deliberately scoped the writer out ("a deployment concern owned by the parity epic / deployment overlay"). D#15595 inventory #6 records the gap: the cloud DreamService writes sandman_handoff.md into a void — no repo checkout in-container, remote agents cannot read container files.
The Problem
The handoff's writer — GoldenPathSynthesizer (idempotent full regeneration), driven by the DreamService REM lane (cloud-deployable per ADR 0014 §2.1) — resolves the write path from cwd: handoffFilePathProd (ai/mcp/server/memory-core/configBase.mjs:504, env override NEO_HANDOFF_FILE_PATH). In a container deployment there is no repo checkout, so the nightly write lands on ephemeral container fs (or fails silently) — the morning surface is produced and lost every cycle, and the new read tool has nothing durable to serve.
The Architectural Reality
- Write path:
GoldenPathSynthesizer.mjs:1791 (aiConfig.handoffFilePath) + the prod/test formula (configBase.mjs:857).
- Read path (shipped):
get_sandman_handoff reads the same resolved leaf — one contract, two ends.
- The env leaf
NEO_HANDOFF_FILE_PATH already exists — the short-term fix is deployment wiring, not code: point the leaf at a persistent mount in the cloud compose.
- Tenancy: a multi-tenant cloud deployment currently produces one Neo-swarm handoff; whether tenant deployments get their own handoff at all is a product question the v2 store design must answer explicitly.
The Fix
Two layers, this ticket ships the first and records the second:
- Deployment-owned persistence (ship here): in
ai/deploy/docker-compose.yml (cloud profile), mount a persistent volume for the handoff path and set NEO_HANDOFF_FILE_PATH onto it, so the DreamService's nightly write survives container recreates and the MC server container serves it via get_sandman_handoff. Compose + cloud-deployment ops doc (learn/agentos/cloud-deployment/), mirroring the shared-sqlite-data volume precedent (NEO_MEMORY_WAL_DIR override, compose L125-129).
- Graph-backed handoff store (record as parity-epic input, do not build): file-based handoff → graph/doc-store artifact with explicit tenant scoping. Recorded as a D#15595 parity-epic design input (its writer-side work chunk) — v2 / multi-tenant-sandbox territory per Vega's note.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
NEO_HANDOFF_FILE_PATH env leaf |
configBase.mjs:504 (existing) |
Cloud compose sets it onto a persistent mount |
unset → repo-relative default (local behavior unchanged) |
Configuration.md + cloud-deployment ops |
compose precedent NEO_MEMORY_WAL_DIR |
| Cloud compose volumes |
ai/deploy/docker-compose.yml |
New/extended named volume for handoff persistence |
— |
cloud-deployment ops guide |
shared-sqlite-data precedent |
get_sandman_handoff read tool |
merged PR #15602 |
Serves the persisted file in cloud |
handoff-not-found envelope when writer unwired |
tool description |
#15599 ACs |
Decision Record impact
none — deployment wiring + docs on an existing config leaf; no ADR conflict. ADR 0014's lane taxonomy already places the DreamService lane in the cloud profile.
Acceptance Criteria
Out of Scope
- The graph-backed handoff store implementation (v2, parity epic).
- Read-side changes (shipped in #15602).
- Handoff content/format work (
#14663 line).
Avoided Traps
- Baking the handoff into the container image — state on image layers dies on recreate; the mount is the point.
- KB-ingesting the handoff for cloud serving — rejected in the #15599 lineage: ephemeral daily state pollutes the corpus with churn.
- Silently designing the v2 store inside this ticket — the store's tenant-scoping answer belongs to the parity epic's authority, not a side door.
Related
- PR #15602 (read tool, Vega's follow-up source) · #15599 (read-side ticket; writer explicitly Out of Scope)
- D#15595 inventory #6 + OQ8 (the gap record) · ADR 0014 §2.1 (dream lane = cloud-deployable)
#13956 / #14663 / #14885 (handoff writer lineage, closed)
Origin Session ID: fdb40bf0-24ea-4622-a2a2-1b94a4f3dae5
Retrieval Hint: query_raw_memories: "sandman handoff writer persistence container volume cloud" · anchors ai/deploy/docker-compose.yml, ai/services/graph/GoldenPathSynthesizer.mjs:1791, ai/mcp/server/memory-core/configBase.mjs:504
Live latest-open sweep: gh issue list (created-desc, live) 20 open issues at 2026-07-20T13:03Z; no equivalent. A2A in-flight sweep: list_messages all/12 same window; no competing claim. KB semantic sweep: writer-side persistence uncovered; writer identity (GoldenPathSynthesizer) confirmed.
Context
Vega's non-blocking follow-up on PR #15602 (review PRR_kwDODSospM8AAAABGjutTQ): the
get_sandman_handoffread tool's cloud utility is gated on the unshipped writer-side persistence — "worth a tracked ticket so the read tool isn't half-useful in the cloud." #15599 deliberately scoped the writer out ("a deployment concern owned by the parity epic / deployment overlay"). D#15595 inventory #6 records the gap: the cloud DreamService writessandman_handoff.mdinto a void — no repo checkout in-container, remote agents cannot read container files.The Problem
The handoff's writer —
GoldenPathSynthesizer(idempotent full regeneration), driven by the DreamService REM lane (cloud-deployable per ADR 0014 §2.1) — resolves the write path from cwd:handoffFilePathProd(ai/mcp/server/memory-core/configBase.mjs:504, env overrideNEO_HANDOFF_FILE_PATH). In a container deployment there is no repo checkout, so the nightly write lands on ephemeral container fs (or fails silently) — the morning surface is produced and lost every cycle, and the new read tool has nothing durable to serve.The Architectural Reality
GoldenPathSynthesizer.mjs:1791(aiConfig.handoffFilePath) + the prod/test formula (configBase.mjs:857).get_sandman_handoffreads the same resolved leaf — one contract, two ends.NEO_HANDOFF_FILE_PATHalready exists — the short-term fix is deployment wiring, not code: point the leaf at a persistent mount in the cloud compose.The Fix
Two layers, this ticket ships the first and records the second:
ai/deploy/docker-compose.yml(cloud profile), mount a persistent volume for the handoff path and setNEO_HANDOFF_FILE_PATHonto it, so the DreamService's nightly write survives container recreates and the MC server container serves it viaget_sandman_handoff. Compose + cloud-deployment ops doc (learn/agentos/cloud-deployment/), mirroring theshared-sqlite-datavolume precedent (NEO_MEMORY_WAL_DIRoverride, compose L125-129).Contract Ledger Matrix
NEO_HANDOFF_FILE_PATHenv leafconfigBase.mjs:504(existing)NEO_MEMORY_WAL_DIRai/deploy/docker-compose.ymlshared-sqlite-dataprecedentget_sandman_handoffread toolhandoff-not-foundenvelope when writer unwiredDecision Record impact
none— deployment wiring + docs on an existing config leaf; no ADR conflict. ADR 0014's lane taxonomy already places the DreamService lane in the cloud profile.Acceptance Criteria
NEO_HANDOFF_FILE_PATHsetget_sandman_handoffin the cloud profile serves real content (documented live probe) [L3-deferred — operator handoff needed]Out of Scope
#14663line).Avoided Traps
Related
#13956/#14663/#14885(handoff writer lineage, closed)Origin Session ID: fdb40bf0-24ea-4622-a2a2-1b94a4f3dae5
Retrieval Hint:
query_raw_memories: "sandman handoff writer persistence container volume cloud"· anchorsai/deploy/docker-compose.yml,ai/services/graph/GoldenPathSynthesizer.mjs:1791,ai/mcp/server/memory-core/configBase.mjs:504Live latest-open sweep: gh issue list (created-desc, live) 20 open issues at 2026-07-20T13:03Z; no equivalent. A2A in-flight sweep: list_messages all/12 same window; no competing claim. KB semantic sweep: writer-side persistence uncovered; writer identity (
GoldenPathSynthesizer) confirmed.