You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs/verification.md — "Action receipts and embodied workflows" (the four-outcome table); spec/trace-v0.2.md §3.3 (verification protocol, receipt outcomes); schema/trace-claim.json (one new optional claim — schema PR to track the spec PR per CONTRIBUTING).
Problem
The four action-receipt verifier outcomes conflate two materially different missing-receipt cases under receipt_missing_required:
an adversarial or negligent silence — a receipt for a consequential action is simply absent; and
an operator-disclosed, bounded loss — the emitter crashed, lost a tail of receipts, and said so.
Under a profile with action_receipts: required (#66), completeness is the load-bearing property — but no emitter can be made gap-proof: any high-throughput writer has a crash window in which a tail of receipts is lost. As currently specified, an honest operator who loses 50 ms of receipts in a crash produces the same verifier outcome as an adversary who silently deleted the receipts for a consequential action.
This creates a perverse incentive: backfilling or fabricating receipts scores better than disclosing the loss. An evidence standard should reward honesty under failure, not punish it.
Proposed change
1. New optional, chain-bound claim GapDisclosure:
{
"type": "GapDisclosure/1.0",
"range_start_after": "<chain position of the last receipt before the gap>",
"range_end_before": "<chain position of the first receipt after resumption>",
"cause": "crash | shutdown | backpressure | unknown",
"receipts_lost_estimate": 3,
"disclosed_at": "<chain position where this disclosure is sealed>"
}
Gap boundaries MUST be expressed in chain/log order, not timestamps — a key that signs receipts also signs its own timestamps, so self-reported time cannot bound a gap (same rationale as the entry-ID anchoring in [pre-v1.0] Key revocation: TraceRevocation claim + entry-ID anchor #67).
The disclosure MUST be signed by the receipt-issuing key or its hierarchical parent, and MUST itself be sealed into the receipt chain at the resumption point, so the disclosure is as tamper-evident as the receipts it accounts for.
receipts_lost_estimate MAY be null when the emitter cannot bound the count; the range bounds are still normative.
2. Fifth verifier outcome, and a re-scope of the fourth:
Outcome
Meaning
receipt_gap_disclosed
The profile required receipts and one or more are missing, but a valid GapDisclosure covers the missing range. Bounded, operator-attested negative evidence — the evidence-layer analogue of receipt_valid_rejected's "valid rejection is not malformed evidence".
receipt_missing_required(re-scoped)
A required receipt is missing and no valid disclosure covers it. Silent, presumptively adversarial.
3. Verifier obligations (normative):
The verifier MUST validate the disclosure's signature, its chain binding, and that the disclosed range covers the missing receipts.
The verification statement MUST report disclosed gaps (range, cause, estimate) rather than collapsing them into a boolean — the gap detail is exactly what a relying party's policy needs.
Whether receipt_gap_disclosed is accepted (degraded-with-disclosure) or rejected (fail-closed) MUST be a verifier policy input, not implementation-defined behavior.
A GapDisclosure that fails validation, or whose claimed coverage contradicts receipts actually present, MUST yield receipt_invalid — a forged or self-contradictory disclosure is worse than none.
Backward compatibility
Non-breaking (new optional field, informative addition)
Breaking
One new optional claim + one added outcome. Suggest gating the new outcome under the widened profile version already contemplated in #114, so existing v0.2 verifiers are untouched.
Motivation
Production precedent: I operate a per-action receipt system whose emitter writes a functionally equivalent disclosure on crash recovery (bounded tail loss, chain resumes, disclosure sealed into the chain) — and relying parties treat a disclosed gap very differently from a silent one. Without this distinction, TRACE profiles with action_receipts: required are either unachievable at production rates or quietly dishonest — "perfect or broken" chains pressure operators to backfill.
If accepted, I will submit the spec PR after the comment window, the schema PR tracking it, and conformance vectors for trace-tests:
receipts dropped, no disclosure → receipt_missing_required
Spec section affected
docs/verification.md— "Action receipts and embodied workflows" (the four-outcome table);spec/trace-v0.2.md§3.3 (verification protocol, receipt outcomes);schema/trace-claim.json(one new optional claim — schema PR to track the spec PR per CONTRIBUTING).Problem
The four action-receipt verifier outcomes conflate two materially different missing-receipt cases under
receipt_missing_required:Under a profile with
action_receipts: required(#66), completeness is the load-bearing property — but no emitter can be made gap-proof: any high-throughput writer has a crash window in which a tail of receipts is lost. As currently specified, an honest operator who loses 50 ms of receipts in a crash produces the same verifier outcome as an adversary who silently deleted the receipts for a consequential action.This creates a perverse incentive: backfilling or fabricating receipts scores better than disclosing the loss. An evidence standard should reward honesty under failure, not punish it.
Proposed change
1. New optional, chain-bound claim
GapDisclosure:{ "type": "GapDisclosure/1.0", "range_start_after": "<chain position of the last receipt before the gap>", "range_end_before": "<chain position of the first receipt after resumption>", "cause": "crash | shutdown | backpressure | unknown", "receipts_lost_estimate": 3, "disclosed_at": "<chain position where this disclosure is sealed>" }receipts_lost_estimateMAY be null when the emitter cannot bound the count; the range bounds are still normative.2. Fifth verifier outcome, and a re-scope of the fourth:
receipt_gap_disclosedGapDisclosurecovers the missing range. Bounded, operator-attested negative evidence — the evidence-layer analogue ofreceipt_valid_rejected's "valid rejection is not malformed evidence".receipt_missing_required(re-scoped)3. Verifier obligations (normative):
receipt_gap_disclosedis accepted (degraded-with-disclosure) or rejected (fail-closed) MUST be a verifier policy input, not implementation-defined behavior.GapDisclosurethat fails validation, or whose claimed coverage contradicts receipts actually present, MUST yieldreceipt_invalid— a forged or self-contradictory disclosure is worse than none.Backward compatibility
One new optional claim + one added outcome. Suggest gating the new outcome under the widened profile version already contemplated in #114, so existing v0.2 verifiers are untouched.
Motivation
Production precedent: I operate a per-action receipt system whose emitter writes a functionally equivalent disclosure on crash recovery (bounded tail loss, chain resumes, disclosure sealed into the chain) — and relying parties treat a disclosed gap very differently from a silent one. Without this distinction, TRACE profiles with
action_receipts: requiredare either unachievable at production rates or quietly dishonest — "perfect or broken" chains pressure operators to backfill.If accepted, I will submit the spec PR after the comment window, the schema PR tracking it, and conformance vectors for
trace-tests:receipt_missing_requiredreceipt_gap_disclosedreceipt_missing_requiredreceipt_invalidreceipt_invalid(contradiction)Related issues or PRs
#66 (action_receipts axis — this is the ADR offered there) · #76 (disclosure vs. silence at verify time) · #67 (log-order anchoring rationale) · #114 (profile-versioned enum widening) · #116 (verification-statement content)