[WARRANT CORRECTED — 2026-08-10] This ticket originally justified the MATERIALIZATION_UNPROVEN arm as the shape of the live ingested=50, embeddings=50, errors=0-with-no-receipt incident. That is wrong: the incident had no receipt at all, not a mismatched one, so it belongs to the EMPTY_MATERIALIZATION arm.
No known producer path delivers effect-plus-unmatched-proof — persistManifestSnapshot mints a matching receipt on positive effect and reuses a prior one only when its digest already matches. So the arm lands as defence-in-depth on a fail-closed guard's own predicate, not as a reproduction of an observed failure. Found by applying @neo-opus-grace's rule (ask what the boundary a test replaces REFUSES) to my own fixture; @neo-gpt-emmy's exact-head read of the producer contract closed both routes I had assumed open.
What still stands unchanged: the EMPTY_MATERIALIZATION narrowing (its arm is exercised by 116 pre-existing specs) and the message correction (one message previously asserted the zero-effect arm as fact for both branches). Folded across four surfaces at 254abfe00b.
Context
Sub of #16566, carrying its AC-5 ("The underlying error is surfaced rather than wrapped… KB_TENANT_REPO_SYNC_EMPTY_MATERIALIZATION must distinguish effect-without-receipt from a genuinely empty envelope"). Split out because #16566 is an epic and ACs belong in its subs, and because one PR can carry only one Resolves.
Deploy-relevant: this is the code an operator reads if the corpus does not fill after a plane rebuild, and today it cannot tell them which stage to look at.
The Problem
assertFullMaterializationEffect — ai/daemons/orchestrator/services/TenantRepoSyncService.mjs:483 — guarded on a disjunction of two opposite findings and raised one code with one message:
| arm |
what happened |
correct response |
hasEffect && !provesCurrentAttempt |
rows landed; no receipt proves this attempt |
do not re-ingest — the data is in, the proof is missing |
!hasEffect && !provesUncommittedRetry |
nothing ingested or deleted |
look at the embed stage |
The single message — "produced no durable positive-effect proof" — describes the second arm, so an operator hitting the first was told the reverse of what happened. details carried only phase.
hasEffect, validReceipt, provesCurrentAttempt and provesUncommittedRetry were all already computed immediately above the throw. No new measurement was needed; the error was discarding what it knew.
The Architectural Reality
- A
details field cannot fix this. grep -rn "lastErrorDetails\|lastErrorReason\|lastErrorMessage" ai/ --include="*.mjs" → no match. The durable per-repo state persists lastErrorCode alone (TenantRepoSyncService.mjs:1790, checkpoint at :2138), so a discriminator in details is dropped at the persistence boundary and reaches nobody. A code is the only channel that survives to the reader.
IngestionService.persistManifestSnapshot already emits a rich logger.warn naming which branch left the receipt null. It runs in kb-server; the code is raised in the orchestrator. Two containers — and #16566's own Facts section records that channel failing: "create-app's 2026-08-06 EMPTY_MATERIALIZATION evidence is unrecoverable… Container stdout retains nothing." The discriminator existed only in the channel already proven not to survive.
- Premise corrected while building the fixture. Effect-with-no-receipt-at-all is not a reachable steady state: production mints a receipt whenever an attempt is present and effect is non-zero, and
makeFakeIngestionService faithfully models that. The reachable shape is a receipt that exists and does not match this envelope's digest — one of the documented reasons production skips receipt creation. A fixture asserting "no receipt at all" would have been testing a shape production cannot produce.
The Fix
KB_TENANT_REPO_SYNC_MATERIALIZATION_UNPROVEN added to TenantRepoSyncErrors.mjs (declaration + TENANT_REPO_SYNC_ERROR_CODES).
- The guard split into two
throws, each with the counts and booleans that decided it — counts and booleans only, matching the credential discipline already applied to ingestion error messages.
EMPTY_MATERIALIZATION retained on the zero-effect arm, the one its existing message was already accurate for, so no current consumer changes meaning underneath it.
- The error table at
TenantRepoSyncService.mjs:1093 gains a row, rather than having the existing row's description widened.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
KB_TENANT_REPO_SYNC_MATERIALIZATION_UNPROVEN |
TenantRepoSyncErrors.mjs taxonomy |
New per-repo lastErrorCode for the effect-bearing arm |
n/a — new code, no prior consumer |
error table :1093 + declaration comment |
new discriminating spec |
KB_TENANT_REPO_SYNC_EMPTY_MATERIALIZATION |
same |
Narrowed to the zero-effect arm only |
unchanged for existing consumers |
same table row, corrected |
116/116 service specs unchanged |
TENANT_REPO_SYNC_ERROR_CODES |
frozen array |
9 → 10 members |
isTenantRepoSyncErrorCode unchanged |
JSDoc |
taxonomy spec: count and membership |
Decision Record impact
none. No ADR authority is amended: this narrows one error code's meaning and adds its sibling within an existing taxonomy.
Acceptance Criteria
Out of Scope
- #16566 AC-1 scheduling fairness — the heavy-maintenance starvation that also starves REM consolidation. Larger, and untouched here.
- #16566 AC-6's plane-named proof artifact — needs a live tenant ingest, not a unit test.
- The
IngestionService warn. It is correct and stays as is; this fixes the channel, not the diagnostic.
- Any change to what is ingested or when. Reporting only.
Avoided Traps
Putting the discriminator in details. It reads as the lighter-touch fix and would have satisfied the AC's wording in source while delivering nothing — there is no persisted field to carry it. Verified by grep before choosing, not after.
Asserting the incident's literal shape. The live observation was ingested=50, embeddings=50, errors=0 with no receipt, and a fixture built to that literal shape passed as a success because production mints a receipt on effect. Building the reachable shape instead is what made the test real; the first fixture would have been green and meaningless.
Bumping the taxonomy count to make the guard pass. The count is a tripwire; membership is the assertion.
Related
#16566 (parent — carries AC-5) · #16551 (reporting overlap) · #16045 (the receipt contract these arms enforce)
Origin Session ID: 4131135d-1b20-487f-9d23-d7213914246b
Retrieval Hint: query_raw_memories "EMPTY_MATERIALIZATION two arms effect without proof lastErrorCode no details field"
Authored by @neo-opus-vega 🌿
Context
Sub of #16566, carrying its AC-5 ("The underlying error is surfaced rather than wrapped…
KB_TENANT_REPO_SYNC_EMPTY_MATERIALIZATIONmust distinguish effect-without-receipt from a genuinely empty envelope"). Split out because #16566 is anepicand ACs belong in its subs, and because one PR can carry only oneResolves.Deploy-relevant: this is the code an operator reads if the corpus does not fill after a plane rebuild, and today it cannot tell them which stage to look at.
The Problem
assertFullMaterializationEffect—ai/daemons/orchestrator/services/TenantRepoSyncService.mjs:483— guarded on a disjunction of two opposite findings and raised one code with one message:hasEffect && !provesCurrentAttempt!hasEffect && !provesUncommittedRetryThe single message — "produced no durable positive-effect proof" — describes the second arm, so an operator hitting the first was told the reverse of what happened.
detailscarried onlyphase.hasEffect,validReceipt,provesCurrentAttemptandprovesUncommittedRetrywere all already computed immediately above thethrow. No new measurement was needed; the error was discarding what it knew.The Architectural Reality
detailsfield cannot fix this.grep -rn "lastErrorDetails\|lastErrorReason\|lastErrorMessage" ai/ --include="*.mjs"→ no match. The durable per-repo state persistslastErrorCodealone (TenantRepoSyncService.mjs:1790, checkpoint at:2138), so a discriminator indetailsis dropped at the persistence boundary and reaches nobody. A code is the only channel that survives to the reader.IngestionService.persistManifestSnapshotalready emits a richlogger.warnnaming which branch left the receipt null. It runs in kb-server; the code is raised in the orchestrator. Two containers — and #16566's own Facts section records that channel failing: "create-app's 2026-08-06EMPTY_MATERIALIZATIONevidence is unrecoverable… Container stdout retains nothing." The discriminator existed only in the channel already proven not to survive.makeFakeIngestionServicefaithfully models that. The reachable shape is a receipt that exists and does not match this envelope's digest — one of the documented reasons production skips receipt creation. A fixture asserting "no receipt at all" would have been testing a shape production cannot produce.The Fix
KB_TENANT_REPO_SYNC_MATERIALIZATION_UNPROVENadded toTenantRepoSyncErrors.mjs(declaration +TENANT_REPO_SYNC_ERROR_CODES).throws, each with the counts and booleans that decided it — counts and booleans only, matching the credential discipline already applied to ingestion error messages.EMPTY_MATERIALIZATIONretained on the zero-effect arm, the one its existing message was already accurate for, so no current consumer changes meaning underneath it.TenantRepoSyncService.mjs:1093gains a row, rather than having the existing row's description widened.Contract Ledger Matrix
KB_TENANT_REPO_SYNC_MATERIALIZATION_UNPROVENTenantRepoSyncErrors.mjstaxonomylastErrorCodefor the effect-bearing arm:1093+ declaration commentKB_TENANT_REPO_SYNC_EMPTY_MATERIALIZATIONTENANT_REPO_SYNC_ERROR_CODESisTenantRepoSyncErrorCodeunchangedDecision Record impact
none. No ADR authority is amended: this narrows one error code's meaning and adds its sibling within an existing taxonomy.Acceptance Criteria
MATERIALIZATION_UNPROVEN; a zero-effect one yieldsEMPTY_MATERIALIZATION.EMPTY_MATERIALIZATIONassertion passes unchanged, proving no already-covered case is silently reclassified.ingested,deleted,receiptPresent,receiptMatchesDigest) and no paths, filenames, or repo content.lengthalone would let a future code pass the guard unnamed.Out of Scope
IngestionServicewarn. It is correct and stays as is; this fixes the channel, not the diagnostic.Avoided Traps
Putting the discriminator in
details. It reads as the lighter-touch fix and would have satisfied the AC's wording in source while delivering nothing — there is no persisted field to carry it. Verified by grep before choosing, not after.Asserting the incident's literal shape. The live observation was
ingested=50, embeddings=50, errors=0with no receipt, and a fixture built to that literal shape passed as a success because production mints a receipt on effect. Building the reachable shape instead is what made the test real; the first fixture would have been green and meaningless.Bumping the taxonomy count to make the guard pass. The count is a tripwire; membership is the assertion.
Related
#16566 (parent — carries AC-5) · #16551 (reporting overlap) · #16045 (the receipt contract these arms enforce)
Origin Session ID: 4131135d-1b20-487f-9d23-d7213914246b
Retrieval Hint:
query_raw_memories"EMPTY_MATERIALIZATION two arms effect without proof lastErrorCode no details field"Authored by @neo-opus-vega 🌿