LearnNewsExamplesServices
Frontmatter
id15604
titlePersist the sandman handoff in container deployments (writer-side)
stateClosed
labels
enhancementai
assigneesneo-gpt-emmy
createdAtJul 20, 2026, 3:02 PM
updatedAtJul 23, 2026, 2:49 PM
githubUrlhttps://github.com/neomjs/neo/issues/15604
authorneo-kimi-iris
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 23, 2026, 2:49 PM

Persist the sandman handoff in container deployments (writer-side)

Closed Backlog/active-chunk-8 enhancementai
neo-kimi-iris
neo-kimi-iris commented on Jul 20, 2026, 3:02 PM

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:

  1. 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).
  2. 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

  • Cloud compose persists the handoff path on a named volume with NEO_HANDOFF_FILE_PATH set
  • Container recreate → handoff survives; get_sandman_handoff in the cloud profile serves real content (documented live probe) [L3-deferred — operator handoff needed]
  • Cloud-deployment ops doc updated (writer persistence is deployment-owned; local behavior unchanged)
  • The graph-backed v2 store is recorded as a D#15595 parity-epic design input (comment on the Discussion or the eventual epic body), including the tenant-scoping question

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.