Audit — P3.8.1 Parity Harness (R89 θ Wave 5)

§1. Surface inventory

P3.8.1 closes the Phase 3 verification loop. It mirrors κ P1.5.5 — a fixed-corpus deterministic simulation harness that runs every shipped θ module under a 4-scenario default corpus and produces a structured report. Scenario 3 is the consensus.md §Worked example (4-node BFT vote on event E), shipped as test data so future θ changes cannot drift from the canonical spec example without breaking the harness.

The harness is not a runtime artifact (no MCP tools, no DB writes, no I/O). It is a pure simulation library plus a Jest test driver. The 4-scenario corpus is data; the harness is a function from (Scenario, seed) to ParityReport.

§2. Inputs the harness consumes

Module Imports used Role in harness
src/domains/consensus/messages.ts Vote, signMessage, nextLogical, buildEquivocationProof, resetLogicalForTesting Build signed votes per arbiter; build equivocation proofs
src/domains/consensus/quorum.ts hasQuorum, quorumThreshold, voteGroupKey, detectDoubleVote Compute majority quorum group; detect SC4 double-vote
src/domains/consensus/finality.ts FinalitySM, FinalityLevel Drive the finality FSM with the simulated votes
src/domains/consensus/equivocation.ts applyEquivocationSlash, evidenceHashHex, EQUIVOCATION_DOMAIN Apply the SC4 slash and verify idempotency
src/domains/reputation/schema.ts ReputationRow, ReputationHistoryRow Provide the attacker row + history Set scaffold

The harness does not consume:

  • gossip-wire.ts, bloom-dedup.ts, adaptive-fanout.ts — network-layer concerns; the harness models a synchronous global view (all arbiters see all votes immediately).
  • round-state.ts — the commit-reveal FSM is round-internal; the harness sidesteps the commit/reveal phases (every arbiter’s vote is “instantly revealed”) and directly drives the finality FSM with fully-signed votes.
  • time-anchors.ts — Phase 4 epoch sealing is out of scope for SC1–SC4 (the 4 scenarios target QUORUM, not HARD or ABSOLUTE).
  • vrf-stub.ts — no view-change in the 4 scenarios.
  • tools.ts — MCP tools are read-only adapters; the harness operates on the in-memory primitives directly.

§3. Pattern source — κ P1.5.5

Mirror points from src/domains/rules/parity-harness.ts + its test:

  • Default corpus is data, not codedefault-corpus.ts exports SCENARIO_* constants + a DEFAULT_CORPUS frozen tuple.
  • Pure functional surfacerunScenario(scenario, seed) → ParityReport, no I/O, no DB.
  • Determinism via seed — two runs with the same seed produce byte-identical reports under canonicalize comparison.
  • Performance budget — 10k synthetic events across all 4 scenarios in under 5 seconds.
  • Self-scan — the harness body contains no forbidden determinism-breaking tokens (Math.*, Date.*, Math.random, etc.). A test asserts this via post-comment-strip regex pass over the source.

§4. Worked example — verbatim mapping

The spec at docs/3-world/physics/laws/consensus.md §Worked example specifies n=4, quorum=3, nodes A/B/C/D, A/B/C honest, D Byzantine, merkle_root = 0xab12… (3-of-4 majority), D divergent with 0xCAFE….

SCENARIO_3 IS this example. The harness:

  1. Builds 4 Ed25519 keypairs deterministically from seed.
  2. Builds 4 signed Votes — A/B/C on merkle_root = 0xab12…padded to 32B, D on merkle_root = 0xCAFE…padded to 32B. All share the same rule_version_hash.
  3. Feeds them to FinalitySM with epoch = 1n.
  4. Asserts FinalitySM.current() === 'QUORUM' (3-of-4 honest).

The “0xab12…” / “0xCAFE…” short prefixes in the spec are prefixes of 32-byte roots. The harness uses Buffer.alloc(32) with the prefix written at offset 0 and zeros elsewhere — that satisfies the prefix match and keeps the rest deterministic.

§5. Existing exports relied on

Vote shape (P3.1.1)

interface Vote {
  readonly sender_id: string;
  readonly round_id: bigint;
  readonly merkle_root: Buffer;        // 32 bytes
  readonly rule_version_hash: Buffer;  // 32 bytes
  readonly vote_type: 'ACCEPT' | 'REJECT' | 'ABSTAIN';
  readonly timestamp_logical: bigint;
  readonly signature: Buffer;          // 64 bytes (Ed25519)
}

FinalitySM (P3.2.1)

  • Constructor: new FinalitySM(round_id, n_arbiters, dispute_window_epochs?)
  • receiveVote(vote: Vote, currentEpoch: bigint): void
  • current(): FinalityLevel

SC1 (n=1): one vote ⇒ PENDING → SOFT → QUORUM in a single receiveVote call (because quorumThreshold(1n) === 1n).

SC2 (n=4 all honest): four matching votes ⇒ first vote takes PENDING → SOFT, then on the third matching vote QUORUM is hit (threshold = quorumThreshold(4n) = 3n).

SC3 (n=4, D divergent): A/B/C on 0xab12 + D on 0xCAFE. The largest matching group is {A,B,C} size 3 ≥ 3 ⇒ QUORUM.

SC4 (n=4, D equivocator): A/B/C honest on 0xab12 + D signs twice with different roots (0xab12 and 0xCAFE). The honest group still reaches QUORUM; the slasher fires on the double-vote proof.

Equivocation slash (P3.5.1)

  • applyEquivocationSlash({proof, attackerPubKey, current_epoch, attackerRow, history, alreadyApplied}): ApplySlashResult
  • Successful slash: applied: true, evidence_hash_hex, next_row, history_event. Caller persists.
  • Re-submission of same proof: applied: false, reason: 'duplicate' (idempotency via the alreadyApplied: Set<string> keyed on evidence_hash_hex).

The harness uses an in-memory Set<string> for alreadyApplied. SC4’s report.slashings_applied counts successful slashes (applied === true). Re-submitting the same proof produces applied: false, reason: 'duplicate', which the harness counts as 0 additional slashings — total still 1.

§6. Determinism budget

Sources of non-determinism the harness eliminates:

Source Replacement
randomBytes (Ed25519 key generation) Seeded HMAC-SHA512 derivation from seed. We derive each arbiter’s 32-byte seed from HMAC(seed_root, label), then pass through node:crypto.createPrivateKey({key: …, format: 'raw'}) for Ed25519.
Date.now for performance test process.hrtime.bigint() is also forbidden by κ self-scan. Use the test driver’s local clock import only inside the test file, NOT the harness.
Math.random for vote ordering Vote order is the scenario’s declared order — deterministic by construction.
Lamport clock leak across scenarios resetLogicalForTesting() is called at the top of each runScenario call.

process.hrtime.bigint() is a forbidden token by the κ scanner regex \bprocess\.(?:hrtime|nextTick)\b. The performance assertion lives in the test file (a *.test.ts), where forbidden tokens are not scanned — only the harness’s own source body needs to be clean.

§7. Report shape

type ParityReport = {
  scenario_id: string;
  n: bigint;
  rounds_executed: bigint;
  finality_reached: FinalityLevel;
  equivocation_proofs: EquivocationProof[];
  slashings_applied: bigint;
  determinism_check: {seed: bigint; second_run_identical: boolean};
};

Two-run determinism check happens inside runScenario itself — the function runs the scenario twice and compares canonical-encoded report bytes (with the determinism_check field stripped). The outer field records the verdict.

§8. Forbidden by task

  • No Math.*, Date.*, Math.random, setTimeout, setInterval, setImmediate, process.hrtime, fetch, XMLHttpRequest, [native code], await, async function, await import, floating- point literals.
  • No new npm deps.
  • No parallel scenario execution.
  • No reads from disk in the harness body (the test file may read source files for self-scan; the harness itself does not).
  • No mutations of src/domains/consensus/* or src/domains/reputation/*.

§9. Out-of-scope

  • Persisting ParityReports — they are returned values, not stored rows.
  • Cross-scenario aggregate metrics — each scenario is independent.
  • Parity vs an alternate consensus implementation — this is corpus parity, not implementation parity.
  • HARD or ABSOLUTE finality scenarios — SC1–SC4 all target QUORUM.
  • View-change / VRF leader rotation — no leader-failure scenarios.

§10. References

  • docs/guides/implementation/task-prompts/p3.1-theta-consensus.md §P3.8.1
  • docs/3-world/physics/laws/consensus.md §Worked example (lines 92-120)
  • src/__tests__/domains/rules/parity-harness.test.ts (κ pattern source)
  • src/domains/rules/parity-harness.ts (κ implementation reference)
  • src/domains/consensus/messages.ts, quorum.ts, finality.ts, equivocation.ts (the surface this harness drives)

Back to top

Colibri — documentation-first MCP runtime. Apache 2.0 + Commons Clause.

This site uses Just the Docs, a documentation theme for Jekyll.