Good first issue. Prose-only, tightly bounded, no behaviour change, and the correct end-state is fully specified below.
Context
#16404 / PR #16442 introduced a provenance claim on the backup receipt and, during cross-family review, renamed it from empty to provenEmpty so that one bundle-meta.json never publishes two meanings of the bare word empty (integrity[].status: 'empty' answers survivability — does this bundle hold restorable rows; the capture block answers provenance — was there genuinely nothing to capture).
The assertions were renamed with it and pass. The test titles and comments were not, so the spec that serves as executable documentation for the claim still calls it empty.
Flagged by @neo-gpt-emmy in the exact-head review (PRR) as the sole remaining truth-fold. It landed minutes after merge, so it is recorded here rather than lost.
Live latest-open sweep: checked latest 20 open issues at 2026-08-03T18:27:17Z; no equivalent found. A2A in-flight claim sweep at the same minute (30 messages, all read-states): no overlapping [lane-claim].
The Problem
A reader grepping provenEmpty does not find the specs that define it. The spec file is the executable documentation of the vocabulary, and it names a field that no longer exists on the object it asserts against — the assertions say .provenEmpty while the titles above them say empty.
This is small, but it is the same drift class the rename existed to remove: prose and contract disagreeing about what a word means, in an artifact whose whole job is to make that word unambiguous.
The Architectural Reality
Two vocabularies deliberately coexist in bundle-meta.json, and only one of them moved:
| surface |
claim |
may be renamed? |
integrity[].status: 'empty' |
survivability — no rows to restore |
never — persisted wire value, matched by exact string in already-deployed readers |
capture.sources[].provenEmpty |
provenance — the facts establish there was nothing to capture |
yes; introduced by #16404, no bundle predates it |
That asymmetry is load-bearing and is why the fold lands only on the capture side.
The Fix
Rename the provenance claim in test titles and comments only, in test/playwright/unit/ai/services/shared/captureReceipt.spec.mjs:
- L18 —
pin the axes as orthogonal and \empty` as the one derived claim`
- L22 —
`empty` requires a measured zero AND a continuous lineage
- L26 —
a CHANGED lineage never derives \empty``
- L43 —
an UNKNOWN lineage never derives \empty``
- L55 —
a populated source is never \empty`, whatever its lineage`
- L87 —
manufacture \empty` for every source whose id could not be observed`
Line numbers are origin/dev at time of filing; grep rather than trusting them.
Out of Scope
Everything else already reads correctly and must not be touched:
ai/services/shared/captureReceipt.mjs — its two remaining bare empty mentions are correct (one refers to the integrity status, one is historical narrative).
ai/services/memory-core/helpers/bundleIntegrity.mjs, ai/scripts/maintenance/backup.mjs, learn/agentos/tooling/RestorationRunbook.md — already correct.
test/playwright/unit/ai/scripts/maintenance/backup.spec.mjs — its four mentions are deliberate: historical narrative, plus a test whose subject is the bare word (the bare property \empty` exists in NEITHER block`).
- Any occurrence of
integrity[].status: 'empty', emptySubsystems, or the receipt projection keys. Renaming those is a false-green regression that a cross-version witness in backup.spec.mjs will catch — see the four-cell matrix in PR #16442.
Acceptance Criteria
Avoided Traps
Do not run a blanket empty → provenEmpty replacement across the test tree. The word is correct in several places and load-bearing in one: integrity[].status: 'empty' is a wire value whose spelling is owned by readers already deployed on planes running four figures of commits behind. PR #16442 shipped exactly that rename and measured the result — oldReader + newBundle returned restorable: true for a zero-row bundle, i.e. a gutted store reported as a usable recovery source, with the other three cells of the matrix green. It was reverted. The distinction is what this ticket preserves, not something it may simplify.
Related
#16404 — the close-target this follows (CLOSED); its Contract Ledger and ACs are already folded to provenEmpty.
- PR
#16442 — where the rename and its revert both happened; carries the four-cell compatibility matrix.
#16348 — parent.
Origin Session ID: eeacb603-97f1-4241-9b2f-3a542cab6d2c
Retrieval Hint: query_raw_memories on "provenEmpty capture receipt provenance vs survivability" or "wire value spelling oldest reader four-cell matrix".
Good first issue. Prose-only, tightly bounded, no behaviour change, and the correct end-state is fully specified below.
Context
#16404/ PR#16442introduced a provenance claim on the backup receipt and, during cross-family review, renamed it fromemptytoprovenEmptyso that onebundle-meta.jsonnever publishes two meanings of the bare wordempty(integrity[].status: 'empty'answers survivability — does this bundle hold restorable rows; the capture block answers provenance — was there genuinely nothing to capture).The assertions were renamed with it and pass. The test titles and comments were not, so the spec that serves as executable documentation for the claim still calls it
empty.Flagged by @neo-gpt-emmy in the exact-head review (PRR) as the sole remaining truth-fold. It landed minutes after merge, so it is recorded here rather than lost.
Live latest-open sweep: checked latest 20 open issues at
2026-08-03T18:27:17Z; no equivalent found. A2A in-flight claim sweep at the same minute (30 messages, all read-states): no overlapping[lane-claim].The Problem
A reader grepping
provenEmptydoes not find the specs that define it. The spec file is the executable documentation of the vocabulary, and it names a field that no longer exists on the object it asserts against — the assertions say.provenEmptywhile the titles above them sayempty.This is small, but it is the same drift class the rename existed to remove: prose and contract disagreeing about what a word means, in an artifact whose whole job is to make that word unambiguous.
The Architectural Reality
Two vocabularies deliberately coexist in
bundle-meta.json, and only one of them moved:integrity[].status: 'empty'capture.sources[].provenEmpty#16404, no bundle predates itThat asymmetry is load-bearing and is why the fold lands only on the capture side.
The Fix
Rename the provenance claim in test titles and comments only, in
test/playwright/unit/ai/services/shared/captureReceipt.spec.mjs:pin the axes as orthogonal and \empty` as the one derived claim``empty` requires a measured zero AND a continuous lineagea CHANGED lineage never derives \empty``an UNKNOWN lineage never derives \empty``a populated source is never \empty`, whatever its lineage`manufacture \empty` for every source whose id could not be observed`Line numbers are
origin/devat time of filing; grep rather than trusting them.Out of Scope
Everything else already reads correctly and must not be touched:
ai/services/shared/captureReceipt.mjs— its two remaining bareemptymentions are correct (one refers to the integrity status, one is historical narrative).ai/services/memory-core/helpers/bundleIntegrity.mjs,ai/scripts/maintenance/backup.mjs,learn/agentos/tooling/RestorationRunbook.md— already correct.test/playwright/unit/ai/scripts/maintenance/backup.spec.mjs— its four mentions are deliberate: historical narrative, plus a test whose subject is the bare word (the bare property \empty` exists in NEITHER block`).integrity[].status: 'empty',emptySubsystems, or the receipt projection keys. Renaming those is a false-green regression that a cross-version witness inbackup.spec.mjswill catch — see the four-cell matrix in PR#16442.Acceptance Criteria
captureReceipt.spec.mjsrefers to the provenance claim as bareempty; all sayprovenEmpty.grep -n 'provenEmpty' test/playwright/unit/ai/services/shared/captureReceipt.spec.mjsreturns the defining test titles, not only the assertion lines.npm run test-unit -- test/playwright/unit/ai/services/shared/captureReceipt.spec.mjspasses unchanged — the count must not move, since no assertion changes.npm run test-unit -- test/playwright/unit/ai/scripts/maintenance/backup.spec.mjsstill passes, in particular thecross-versionblock — proving no wire-vocabulary occurrence was swept up by an over-broad find/replace.emptymentions listed under Out of Scope are unchanged.Avoided Traps
Do not run a blanket
empty→provenEmptyreplacement across the test tree. The word is correct in several places and load-bearing in one:integrity[].status: 'empty'is a wire value whose spelling is owned by readers already deployed on planes running four figures of commits behind. PR#16442shipped exactly that rename and measured the result —oldReader + newBundlereturnedrestorable: truefor a zero-row bundle, i.e. a gutted store reported as a usable recovery source, with the other three cells of the matrix green. It was reverted. The distinction is what this ticket preserves, not something it may simplify.Related
#16404— the close-target this follows (CLOSED); its Contract Ledger and ACs are already folded toprovenEmpty.#16442— where the rename and its revert both happened; carries the four-cell compatibility matrix.#16348— parent.Origin Session ID: eeacb603-97f1-4241-9b2f-3a542cab6d2c
Retrieval Hint:
query_raw_memorieson "provenEmpty capture receipt provenance vs survivability" or "wire value spelling oldest reader four-cell matrix".