P3.1.3 — Round / View State Machine — Verification

Step 5 of the 5-step chain (CLAUDE.md §6). Verification evidence that the contract (§docs/contracts/p3-1-3-round-state-machine-contract.md) is honored by the implementation at feat_sha 3e8d2516.

§1. Three-gate evidence

1.1 npm run build — PASS

> colibri@0.0.1 build
> tsc

> colibri@0.0.1 postbuild
> node scripts/copy-migrations.mjs

copy-migrations: copied 8 migration(s)

Zero TypeScript diagnostics. --noEmit clean against tsconfig.json with exactOptionalPropertyTypes: true.

1.2 npm run lint — PASS

> colibri@0.0.1 lint
> eslint src

Zero ESLint errors across the consensus domain.

1.3 npm test — PASS

Test Suites: 64 passed, 64 total
Tests:       2970 passed, 2970 total
Snapshots:   0 total
Time:        30.103 s, estimated 44 s

Delta vs. baseline (origin/main @ 4ab4e8e8 = 2929 passing + 1 pre-existing reputation/tools migration-race flake = 2930):

  • +40 new tests in round-state.test.ts.
  • Pre-existing flake did NOT fire under full-suite this run (the same intermittent surface reported in earlier rounds).
  • 2970 / 2970 = 100% green across all 64 suites.

§2. Acceptance-criteria checklist

All 28 acceptance criteria from the contract §5 are mapped to tests in src/__tests__/domains/consensus/round-state.test.ts:

AC Description Test names
AC#1 ROUND_STATES tuple shape “AC#1 ROUND_STATES has 5 labels in canonical order”
AC#2 T_round === 30000n “AC#2 T_round === 30000n”
AC#3 T_commit_phase === 10000n “AC#3 T_commit_phase === 10000n”
AC#4 T_reveal_phase === 10000n “AC#4 T_reveal_phase === 10000n”
AC#5 T_timeout === 60000n “AC#5 T_timeout === 60000n (== 2n * T_round)”
AC#6 New FSM at COMMIT_PHASE “AC#6 new FSM at COMMIT_PHASE”
AC#7 begin() commit-hash construction “AC#7 begin() commit-hash construction”
AC#8 COMMIT_PHASE → REVEAL_PHASE on quorum-many commits “AC#8 COMMIT_PHASE -> REVEAL_PHASE on quorum-many commits”
AC#9 COMMIT_PHASE timeout → VIEW_CHANGE “AC#9 COMMIT_PHASE timeout -> VIEW_CHANGE”
AC#10 myReveal() in REVEAL_PHASE “AC#10 myReveal() in REVEAL_PHASE”
AC#11 REVEAL_PHASE → VERIFY_PHASE on quorum-many reveals “AC#11 REVEAL_PHASE -> VERIFY_PHASE on quorum-many reveals”
AC#12 Mismatched commit/reveal hash → liveness fault “AC#12 mismatched commit/reveal hash -> liveness fault”
AC#13 REVEAL_PHASE timeout flags committed-but-not-revealed “AC#13 REVEAL_PHASE timeout flags committed-but-not-revealed”
AC#14 Happy-path verify → COMPLETED with QuorumOutcome “AC#14 happy path verify -> COMPLETED with QuorumOutcome”
AC#15 detectDoubleVote non-empty → equivocation_observed “AC#15 detectDoubleVote non-empty -> VIEW_CHANGE equivocation_observed”
AC#16 Largest group < quorum → malformed_proposal “AC#16 largest group < quorum -> VIEW_CHANGE malformed_proposal”
AC#17 verify() idempotency “AC#17 verify() idempotency”
AC#18 triggerViewChange broadcasts my VC “AC#18 triggerViewChange broadcasts my VC”
AC#19 Quorum-many VCs → COMMIT_PHASE rotated leader “AC#19 quorum-many VCs -> COMMIT_PHASE with rotated leader”
AC#20 selectLeader invoked with (arbiters, prev_merkle_root, round_id) “AC#20 selectLeader called with (arbiters, prev_merkle_root, round_id)”
AC#21 View-change reasons accumulate in payload “AC#21 view-change reasons accumulate”
AC#22 Hash-binding mismatch calls markLivenessFault “AC#22 hash-binding mismatch calls markLivenessFault”
AC#23 REVEAL_PHASE timeout flags all non-revealing arbiters “AC#23 REVEAL_PHASE timeout flags all committed-but-not-revealed”
AC#24 VIEW_CHANGE_ACCEPTED event shape “AC#24 VIEW_CHANGE_ACCEPTED event shape”
AC#25 QUORUM_REACHED event shape “AC#25 QUORUM_REACHED event shape”
AC#26 4-node BFT worked example “AC#26 consensus.md sec.Worked example - 4-node BFT vote on event E”
AC#27 Single-arbiter clause “AC#27 single-arbiter clause (n=1)”
AC#28 Forbidden-token corpus self-scan “AC#28 round-state.ts body uses none of kappa-forbidden tokens”

28 / 28 ACs covered. Test surface: 39 test(...) calls grouped into 14 describe(...) blocks.

§3. Worked example confirmation

The consensus.md §Worked example — 4-node BFT vote on event E trace is reproduced bit-for-bit by the AC#26 test:

Step Spec event Implementation
t=0 commit phase A, B, C, D each broadcast COMMIT makePeerCommit for arb:2/3/4 + fsm.begin() for arb:1
All 4 commits received receivedCommits().length === 4
Phase A end Advance to REVEAL state() === 'REVEAL_PHASE'
t=10s reveal phase All 4 reveals broadcast + hash-verified buildPeerReveal for arb:2/3/4 + fsm.myReveal()
Phase B end Advance to VERIFY (implicit via auto-record after 3rd reveal — threshold reached)
Aggregation 3-of-4 winners {A,B,C} on 0xab12 winning_voters === ['arb:1', 'arb:2', 'arb:3']; merkle_root === tagBuf(0xab12)
Finality QUORUM emitted state() === 'COMPLETED'; quorumOutcome() populated
D’s vote Isolated minority, not slashed livenessFaults empty; D’s vote present in receivedReveals but not in winning_voters

§4. Forbidden-token self-scan evidence

AC#28 reads round-state.ts via readFileSync, strips block + line comments, then asserts none of:

  • /Math\.[A-Za-z]/
  • /Date\.[A-Za-z]/
  • /Math\.random/
  • /setTimeout/
  • /setInterval/
  • /setImmediate/
  • /process\.hrtime/
  • /performance\.now/
  • /\bcrypto\.[A-Za-z]/ — NAMED imports only
  • /\bNumber\(/ — no implicit Number-coercion
  • /\b\d+\.\d+\b/ and /[^.\w]\.\d+\b/ — no float literals

Plus a second test asserting:

  • import { ... } from 'node:crypto' block present
  • No import * as crypto from 'node:crypto'
  • No import crypto from 'node:crypto'

Both subtests pass under npm test.

§5. ζ schema notes (carried from audit §2.4)

The runtime ζ schema’s THOUGHT_TYPES tuple (src/domains/trail/ schema.ts §47-52) is unchanged by this slice. The VIEW_CHANGE_ ACCEPTED and QUORUM_REACHED events flow through the injected ZetaSink.emit(...) callback; integration with the thought_record MCP tool is a downstream concern (an adapter slice may either widen THOUGHT_TYPES to include 'consensus' or map the event onto 'analysis' with a discriminator inside content).

The task prompt’s instruction to “set thought_type='consensus' (new value, document it)” is recorded here for the downstream adapter task.

§6. λ.markLivenessFault notes (carried from audit §2.5)

λ’s reputation surface (P2.2.2 penalties.ts) exposes apply_penalty(band) but does not export markLivenessFault. The P3.1.3 slice defines its own LivenessSink interface and leaves the binding to a future λ slice. The single hook point is the constructor; when λ exposes a typed liveness API, one-line wiring in the consensus caller is sufficient.

§7. Commit chain

Step SHA Subject
1. Audit a8e4cc96 audit(p3-1-3-round-state-machine): inventory commit-reveal FSM + view-change surface (R89 θ Wave 3b)
2. Contract 2fba8524 contract(p3-1-3-round-state-machine): behavioural contract for commit-reveal FSM (R89 θ Wave 3b)
3. Packet 30354add packet(p3-1-3-round-state-machine): execution plan for commit-reveal FSM (R89 θ Wave 3b)
4. Feat 3e8d2516 feat(p3-1-3-round-state-machine): commit-reveal FSM + view-change wiring VRF (R89 θ Wave 3b)
5. Verify (this commit) verify(p3-1-3-round-state-machine): test evidence

§8. Sign-off

All three gates pass. All 28 acceptance criteria honored. Ready for PR open + PM gate.


Back to top

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

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