mirror of
https://github.com/ruvnet/ruflo.git
synced 2026-09-14 14:01:28 +08:00
66fbe7ce12
Closes the gap raised by ruflo#3069: ADR-322C required independent statistical recomputation but specified only the bootstrap seed, not the generator it drives. A verifier could not reproduce a receipt's statistics from the document - the only way to recompute correctly was to read flywheel-receipt.ts, which is a copy rather than an independent check. Appends an "Update (2026-08-19)" section per this repo's ADR convention (ADR-380) rather than editing the text above. It makes normative: the LCG and its constants, the seeding rule and exact byte slice, the resampling procedure and draw order, the quantile rule, tie/edge cases, and the scale-12 decimal encoding the statistics must reproduce byte-for-byte. It also records the ledger divergence (flat unsigned commits[] on main vs the content-addressed segments and signed head the ADR describes, with flywheel-ledger-head/v1 unused), the disabled projection path, and that strictness is versioned. Acceptance is demonstrated, not asserted: conformance/recompute_reference.py is written only from the amendment prose, in a different language from the reference implementation, and reproduces the fixture exactly. Adds receipt-bootstrap-reference.example.json because the existing example turned out not to discriminate - its deltas take two distinct values, so a wrong seed slice, wrong byte order, or entirely different LCG constants all yield the same 2.5th percentile. The new fixture has heterogeneous deltas and separates all three. Records one defect found while building it (gap G5): createFlywheelReceipt computes statistics from full-precision means while storing scale-12 strings, so a receipt whose mean needs more than twelve decimals fails its own verifyFlywheelReceipt. Reproduced at mean 0.7687083333333332. Existing fixtures use exactly-representable means, which is why it was not caught. Co-Authored-By: claude-flow <ruv@ruv.net>
7.7 KiB
7.7 KiB
Conformance checklist — Witness / Receipt Contract v1
Testable criteria for consumers of witness-receipt-contract.md:
rvm-witness anchoring (ruvnet/rvm#35), the autogenous ledger
(ruvnet/autogenous#10), and the ruvector-agent-memory TARL ledger
(ruvnet/RuVector#840).
Each item is a test to write, not a box to assert. Items derived from ADR-322C's own §"Required tests" are marked [322C]; the rest are the consumer-facing consequences of the same rules.
A. Producer conformance (you emit records)
- A1 Every emitted record validates against the matching file in
schemas/withadditionalProperties: false. This is the only enforcement of ADR-322C's unknown-field rule — ruflo's own verifier does not whitelist fields (contract §2.4, gap G3). - A2 No record contains
NaN,±Infinity, or negative zero anywhere. - A3 Every fractional value is a canonical decimal string, not a binary float. Round-tripping a record through your serializer does not change its content ID.
- A4
candidateIdrecomputes asSHA-256(JCS(candidatePolicy)), andreceiptIdasSHA-256(JCS(payload minus receiptId))(contract §3). - A5 Re-evaluating the same candidate produces the same
candidateIdwith a newevaluationRunIdand a newreceiptId. [322C] - A6 The signature covers
UTF8(domain) || 0x00 || JCS(payload)with the exact domain string for the record type (contract §4). Verify againstexamples/receipt-accepted.example.json, whose signature was produced by ruflo's implementation. - A7 Every gate marked
truehas atermVerificationentry, and no authorizing term istrusted-assertionwithout a named attestor (contract §5). - A8
pairedOutcomesis emitted, is the same length and order asheldOutDeltas, and each per-task delta reproduces its aggregate. Aggregate-only receipts are refused by the promotion authority (ADR-381).
B. Verifier conformance (you check records)
- B1 Altering any byte of a canonical record or a referenced object
fails verification. [322C] Confirmed behaviour: flipping one score in the
reference fixture yields
receipt content ID mismatch. - B2 Reordering object keys in transport does not change validity — canonicalization sorts keys. A verifier that depends on serialized order is non-conforming.
- B3 A cryptographically valid signature from a signer outside the trusted set is refused, not merely flagged.
- B4 A signature whose
domainis not the exact expected string is refused, including one that is otherwise valid under a different ruflo domain. This is the replay defence — an ADR-103 witness signature must not verify as a promotion signature (contract §4). - B5 The statistical decision is recomputed, not read. Your bootstrap
reproduces
relativeLift,pairedBootstrapProbability,pairedBootstrapDeltaCILow95, andacceptedexactly forexamples/receipt-bootstrap-reference.example.json, using the procedure now normative in ADR-322C §Update (2026-08-19). Expected:0.100428571429,1,0.0489,true, withseedHex = 167c9185d76163dfa69ae57c11af813669a6353abcb49d7af673acf918f3a7c3. Use that fixture, notreceipt-accepted.example.json— only its deltas are heterogeneous enough to discriminate. Verified: a wrong seed slice, wrong byte order, or different LCG constants all produce0.0425on the accepted-receipt fixture (indistinguishable) but0.04975/0.04965/0.04915on this one.conformance/recompute_reference.pyis a runnable oracle you can diff your implementation against. - B5a Your producer computes statistics from the encoded scores, not from internal full-precision values — a verifier only ever sees the encoded form. Note contract gap G5: ruflo's own producer does not yet do this, so a receipt whose mean needs more than twelve decimals fails its own verifier. Distinguish that failure from tampering when triaging.
- B6 Float and decimal fixtures produce identical hashes across every runtime you support (Rust and TypeScript both matter here). [322C]
- B7 Corpus-role disjointness is checked — a task ID appearing in both
selectionTaskIdsandpromotionHoldoutTaskIdsfails verification. - B8 An expired receipt (
expiresAt <= now) is refused. - B9 Your report labels every authorizing term
recomputed,signature-verified, ortrusted-assertion, and refuses to describe a promotion as independently verified while any authorizing term is an unapproved assertion. [322C]
C. Ledger and anchoring
- C1 Removing, reordering, or reparenting a commit breaks verification from head to genesis. [322C]
- C2 Exactly one commit may consume a given
receiptId; a second attempt is idempotent, not a second promotion (ADR-322A). - C3 You anchor the receipt (
receiptId), whose signature is implemented — not the ledger head, whoseruflo/flywheel-ledger-head/v1signature is specified but not implemented onmain(contract gap G2). If you anchor a head, you are anchoring an unsigned hash chain; say so. - C4 An anchored record's stated assurance is the assurance it carries,
not the assurance of the chain it landed in. A service-side record anchored into
a hypervisor chain does not acquire hypervisor-side guarantees
(
rvmADR-285). Uses thePROPOSED-EXTENSIONfield inschemas/ruflo-anchor-record-v1.schema.json. - C5 No dependency edge is introduced between
rvm-witnessandautogenous's witness crate in either direction — both consume this contract independently (rvm#35,autogenous#10).
D. Separation of powers across the repo boundary
- D1 The party emitting receipts is not the party that decides
allowedProposerSubstitutions,trustedPublicKeys, orapprovedAttestors. A cross-repo promotion that lets one actor supply both collapses the proposer/promoter split ADR-322 exists to maintain. - D2 A receipt carrying
proposerSubstitutiondoes not promote unless that exact substitution string is explicitly allow-listed. Default denies. - D3 Promotion preconditions are re-checked at promotion time, not
inherited from evaluation time:
baselineRef,expectedLedgerHead,gateVersion,policySchemaVersion,safetyEnvelopeRef,expiresAt(ADR-322A §"Promotion compare-and-swap").
E. Statistical claims you republish
- E1 Any restatement of the family-wise false-promotion bound carries both
qualifiers: it is per evidence epoch, and ADR-381 is
Proposed(though the mechanism is present onmain). Contract §6.2. - E2 A governed reset opens a new epoch, expires outstanding receipts, and
restarts allocation at
k = 1. A receipt whoseissuedAtpredates the current epoch boundary is refused even if registered after the reset. - E3 You do not claim phase-3/4 properties.
safetyEnvelopeRefis carried and compared, but the signedSafetyEnvelopeit names is a phase-3 artifact that does not exist yet (contract §7).
F. Versioning
- F1 You pin
schemaVersionandgateVersionstrings, not ruflo git SHAs (ruvnet/ruflo#3066). - F2 An unrecognized
schemaVersionis refused, never best-effort parsed. - F3 A
gateVersionchange is not applied retroactively to receipts issued under an older gate.