LearnNewsExamplesServices
Frontmatter
id16863
titleEMPTY_MATERIALIZATION means both "rows landed" and "nothing arrived"
stateClosed
labels
bugaiarchitectureagent-os
assigneesneo-opus-vega
createdAtAug 10, 2026, 11:02 AM
updatedAtAug 10, 2026, 12:55 PM
githubUrlhttps://github.com/neomjs/neo/issues/16863
authorneo-opus-vega
commentsCount0
parentIssue16566
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 10, 2026, 12:55 PM

EMPTY_MATERIALIZATION means both "rows landed" and "nothing arrived"

Closed Backlog/active-chunk-14 bugaiarchitectureagent-os
neo-opus-vega
neo-opus-vega commented on Aug 10, 2026, 11:02 AM

[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

assertFullMaterializationEffectai/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

  • The two arms raise different codes. An effect-bearing materialization whose receipt does not prove this envelope yields MATERIALIZATION_UNPROVEN; a zero-effect one yields EMPTY_MATERIALIZATION.
  • The existing code keeps its existing meaning. Every pre-existing EMPTY_MATERIALIZATION assertion passes unchanged, proving no already-covered case is silently reclassified.
  • Mutation-convicted. Collapsing the split back to a single code reddens the effect-bearing test while the zero-effect test still passes — so the two tests do not co-assert.
  • The message no longer states the wrong arm as fact. The effect-bearing throw says the effect happened and the proof is missing.
  • Both throws carry the deciding values (ingested, deleted, receiptPresent, receiptMatchesDigest) and no paths, filenames, or repo content.
  • The taxonomy guard covers the new code by name, not only by count — bumping length alone would let a future code pass the guard unnamed.
  • The error table gained a row rather than widening the existing description.
  • Every spec importing the changed modules passes — 214 across six specs, found by grepping the changed module names rather than running only the nearest file.

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 🌿

tobiu referenced in commit ff96657 - "EMPTY_MATERIALIZATION means both "rows landed" and "nothing arrived" (#16864) on Aug 10, 2026, 12:55 PM
tobiu closed this issue on Aug 10, 2026, 12:55 PM