LearnNewsExamplesServices
Frontmatter
id16114
titleReceipt replay rejects Dream-owned session metadata
stateClosed
labels
bugairegressionarchitecture
assigneesneo-gpt
createdAt7:40 AM
updatedAt9:45 AM
githubUrlhttps://github.com/neomjs/neo/issues/16114
authorneo-gpt
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAt9:45 AM

Receipt replay rejects Dream-owned session metadata

Closed Backlog/active-chunk-10 bugairegressionarchitecture
neo-gpt
neo-gpt commented on 7:40 AM

Context

The first live post-merge validation of PR #16110 has falsified its already-present recovery path without forcing or restarting shared infrastructure.

A current-source session-summary sweep staged and acknowledged five durable receipts between 2026-07-29T05:21:29Z and 05:21:47Z. At 05:33:10Z, the next natural sweep failed before new synthesis with:

Session-summary receipt replay verification failed for summary_019f9e1e-2ef1-72c3-a04d-6bc67a531a8b.

Direct SQLite-envelope versus Chroma read-back established the mismatch exactly:

  • the summary document matches;
  • every metadata key/value carried by the durable receipt matches;
  • Chroma additionally carries graphDigested:true and digestState:'digested', written by DreamService after summary synthesis;
  • two of the five newly staged receipts have those two extra fields and fail whole-object equality; the other three are exact and do not.

The first failing coordinator row remains completed, its receipt is intact, and result_last_replayed_at is still null. This is therefore not receipt loss. It is an ownership mismatch between immutable synthesis output and mutable downstream lifecycle metadata on the same Chroma row.

The Problem

ai/services/memory-core/helpers/sessionSummaryReceiptStore.mjs:202-204 currently defines an exact row as an exact document plus isDeepStrictEqual(row.metadata, receipt.metadata). That makes the receipt appear to own the complete metadata object forever.

ai/daemons/orchestrator/services/DreamService.mjs:602-629 legitimately extends the same row later with graphDigested, digestState, digestAttempts, and deferReason. Chroma document upsert preserves unrelated existing metadata keys. Recovery therefore sees a healthy, receipt-consistent row as mismatched, upserts the receipt payload, reads the still-preserved Dream fields back, and fails the same whole-object comparison again.

Because recoverSessionSummaryReceipts() runs before drift detection, one such row aborts the whole summarization child. The durable recovery mechanism introduced by #16105 thus blocks normal summary progress as soon as Dream-owned metadata reaches a receipt-backed row.

The Architectural Reality

  • SessionService.mjs:787-880 owns the synthesized document and base metadata, writes them to Chroma, and stages that exact synthesis result in SQLite.
  • sessionSummaryReceiptStore.mjs is the existing 269-LOC helper that owns receipt encoding, matching, replay, and CAS finalization. The Agent OS structure map confirms ai/services/memory-core/helpers/ as the established owner; no new file or service is needed.
  • DreamService.mjs:602-629 owns post-synthesis graph-digestion lifecycle fields on the same Chroma row.
  • The receipt must be authoritative for its own document and metadata fields. It must not claim ownership of later fields written by another service.
  • A missing or receipt-owned mismatch still requires replay and strict read-back. Extra downstream-owned fields alone do not.

The Fix

  1. Replace whole-object metadata equality with a strict receipt-owned-key comparison: the document and every metadata key/value encoded in the receipt must be present and deeply equal; unrelated live metadata keys are tolerated.
  2. Preserve the existing replay decision for a missing row, a changed document, a missing receipt-owned key, or a changed receipt-owned value.
  3. Verify replay with the same ownership-aware predicate so Chroma metadata merge semantics cannot create a permanent retry/failure loop.
  4. Add deterministic coverage for an already-present row carrying Dream-owned overlays, plus a real disposable-Chroma witness for the merge shape that escaped the fake collection.
  5. Keep receipt encoding, SQLite schema, CAS/ABA protection, batching, lease handling, and no-model recovery unchanged.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
Receipt row-match predicate sessionSummaryReceiptStore.mjs Exact document plus strict equality for every receipt-owned metadata key; tolerate unrelated live keys Treat any missing/changed owned field as replay-required Helper JSDoc Focused unit matrix
Receipt issuance / durable decode sessionSummaryReceiptStore.mjs Issuance enforces the declared allowed and required key sets; version-1 decode validates only the stable outer receipt shape so historical missing or retired metadata keys remain recoverable Corrupt bytes, unsupported versions, invalid outer identity, and non-object metadata remain fatal Helper JSDoc Shape-drifted durable-envelope recovery test
recoverSessionSummaryReceipts() sessionSummaryReceiptStore.mjs Already-present Dream overlays count as present, not corruption; real base drift still replays and verifies Retain the durable envelope and fail loud when owned fields cannot be verified Method JSDoc Fake-collection and disposable-Chroma witnesses
Dream lifecycle overlays DreamService.mjs Remain Dream-owned and survive an idempotent present-row recovery check Missing rows replay the durable synthesis base and re-enter normal Dream eligibility Existing DreamService JSDoc Cross-service regression assertion
Summarization child startup SessionService.mjs recovery call One legitimately enriched receipt-backed row cannot abort the sweep Existing thrown recovery errors remain fatal for corrupt envelopes or unverifiable owned fields Existing lifecycle comments Production-shaped multi-row test

Decision Record impact

none — this repairs the ownership contract inside the existing Memory Core receipt helper. It does not change MCP APIs, AiConfig, SQLite schema, Chroma ownership, or the Body/Brain boundary.

Acceptance Criteria

  • A row with an exact receipt document/base metadata plus graphDigested / digestState is classified present, not replayed, and recovery completes.
  • Extra downstream metadata is preserved; recovery does not delete, overwrite, or absorb it into the durable receipt.
  • A missing receipt-owned key, changed receipt-owned value, or changed document still triggers replay and strict post-upsert verification.
  • Recovery continues past multiple enriched receipt-backed rows instead of aborting the summarization child on the first one.
  • A disposable-Chroma test reproduces real metadata-merge behavior; the existing forced-stop/restart, idempotence, corruption, lease, and CAS tests remain green.
  • No model invocation, schema migration, new file, config key, or operator action is introduced.
  • AC7 [L3-deferred — post-merge current-source runtime needed]: the production-shaped receipt sweep that failed at 2026-07-29T05:33:10Z completes on current source, and #16017 remains open until that receipt evidence is posted.

Out of Scope

  • Resetting or reclassifying Dream lifecycle fields when a session is deliberately re-synthesized. That is a distinct lifecycle-invalidation question and requires its own falsifier before work.
  • Persisting mutable Dream lifecycle overlays inside the synthesis receipt.
  • Changing Chroma's metadata merge semantics, deleting/recreating healthy rows, or broadening durable receipts to other collections.
  • Reopening the closed #16105; this is its post-merge successor defect.
  • Closing #16017 before current-source runtime evidence passes.

Avoided Traps

  • Treat all extra metadata as corruption. The live fields are produced by the intended downstream consumer.
  • Relax the entire comparison. Receipt-owned values remain type-strict and deep-equal; only ownership scope changes.
  • Copy Dream fields into the envelope. They are mutable after staging, so the receipt would become stale by construction.
  • Delete/recreate an already-correct row. That would discard useful lifecycle state and turn an idempotent read path into needless mutation.
  • Make the fake collection define Chroma semantics. The escaped case exists because the fake replaces metadata while real Chroma preserves unrelated keys.

Duplicate / Claim Sweep

Live latest-open sweep: checked the latest 20 open issues from live GitHub, created-descending, at 2026-07-29T05:40:02Z; no equivalent found.

A2A in-flight sweep: checked the latest 30 messages across all read states immediately before creation; no competing claim or equivalent ticket intent found. The only matching message was this lane's own live-falsifier notice to Vega.

Knowledge Base and committed-corpus sweeps found #13647, #13697, and the REM epic #12065 as adjacent graph-projection history, plus #16017 / #16105 / PR #16110 as the direct incident lineage. None owns receipt equality under downstream metadata overlays.

Related

Related: #16017 Related: #16105 Related PR: #16110 Related: #13647 Related: #13697

Origin Session ID: 019fac51-ddcb-7212-902e-09d3a9d19098

Handoff Retrieval Hints

  • query_raw_memories("session summary receipt replay verification graphDigested digestState Dream-owned metadata")
  • Runtime witness: 2026-07-29T05:21:29Z..05:33:10Z
  • Exact specimen: summary_019f9e1e-2ef1-72c3-a04d-6bc67a531a8b
  • Implementation anchor: PR #16110 / merge 3cedd76cac

Authored by Euclid (GPT-5 Codex, Codex Desktop).

tobiu referenced in commit 975c199 - "fix(memory-core): scope receipt equality to owned metadata (#16114) (#16116) on 9:45 AM
tobiu closed this issue on 9:45 AM