A recorded removal and a never-landed write are observationally identical at the consumer boundary. Exposing the absence assertion on a machine-readable discovery surface is necessary and sufficient to separate them.
55e6493 — separable, necessary, reconciliation-distinguished, one keying finding); reproduce from clean clones with reproduce.sh; every pin is in source-lock.json. Tests the 111-case corpus next door.| artifact | role | |
|---|---|---|
| producer | remember-okf-sample-bundle ea18185f | remember/0.2 emitted shape — log, store, manifest, tombstones |
| consumer | throughline c512e8b9 · never_landed.py | assertion-consistency checker |
| corpus | deletion-conformance-fixture | 111 cases at v2.1, used in runs 01–07; v2.2 added six cases in response to this test; v2.3 (2026-09-03) annotates the producer’s shipped repair and adds two reconciliation cases |
Three records, one convention. A created and present. B created, then removed — tombstone records presence, date, reason, successor. C creation asserted, never landed.
current consumer A → present B → never_landed C → never_landed B and C COLLIDE repaired consumer A → present B → removed C → never_landed B and C DIVERGE
The producer recorded the removal faithfully. The consumer correctly reported a claim with no landing. The information is destroyed in the gap between them: the removal lives in tombstones.md prose, and the machine-readable index references no tombstones at all. Neither implementation is at fault.
Lift the absence record into the machine index and add one consumer lookup — before calling a claim never-landed, ask whether an absence record survives for that identity — and B and C diverge, with A and the claim counts undisturbed. Strip the record while leaving the prose tombstone fully intact, and they collide again. The requirement is therefore not to record the removal, which the producer already did.
.manifest.json” — that is one producer's carrier, not a format primitive.Rendering the corpus into the producer's shape twice — strict, refusing anything requiring a key the emitter does not define, and forced, translating everything — produces the same clean downstream verdict by opposite means.
| strict | forced | |
|---|---|---|
| source cases | 111 | 111 |
| expressible | 1 | 111 |
| refused untranslatable | 110 | 0 |
| preserved | 1 | 1 |
| coerced | 0 | 110 |
| dropped | 4 | 451 |
| fabricated | 0 | 110 |
| downstream verdict | clean | clean |
The checker cannot distinguish the two runs; the receipt can, and its fidelity warning cannot be detached from the green result. Absence of an alarm is not evidence of preservation. refused is kept distinct from coerced: a translator that declines to represent what it cannot carry preserves integrity; one that silently forces it does not.
Representation portability is 1 of 111 while checker portability holds on two flags — different properties. The corpus under-represents the case the finding turns on: every expressible case is presence=removed, and exactly one never_landed case exists. The --id-key failure mode is a property of stores whose filenames diverge from stable ids, so a fixture on stem-named stores never exercises it. An earlier exploratory figure of 671 breaks reconciles exactly as 451 dropped + 110 coerced + 110 fabricated — the measurement became more discriminating; the quantity did not change.
--id-key resolution path and the empty-log guard are @andrewcrenshaw's contributions to @inkxel's checker; the checker and the bundle are their work. This package composes them and adds the translation, the receipt, the fixture, and the repair test. Both published baselines reproduce exactly here — a third independent reproduction of each.