Verification — P3.2.1 Finality State Machine

Branch: feature/p3-2-1-finality-sm Worktree: .worktrees/claude/p3-2-1-finality-sm Base: origin/main @ 498e6ea5


1. Gates — all green

$ npm run build
> tsc        ── 0 errors

$ npm run lint
> eslint src ── 0 warnings/errors

$ npm test
Test Suites: 61 passed, 61 total
Tests:       2862 passed, 2862 total
Time:        45.578 s

Baseline before this task (from feature/p3-2-1-finality-sm @ 498e6ea5):

  • 60 suites, 2820 tests reporting 2818 pass + 1 skipped + 1 pre-existing failure (reputation/tools.test.ts — better-sqlite3 schema issue, unrelated to P3.2.1, the failure was flaky and self-resolved during full re-run).

Delta:

  • +1 suite (src/__tests__/domains/consensus/finality.test.ts)
  • +42 tests (all passing)
  • Pre-existing reputation flake now also green; not introduced by P3.2.1.

The expected delta of “+25 to +40” was slightly exceeded — 42 tests because some AC# items expanded into multiple cases (e.g. AC#13 covers PENDING/SOFT/QUORUM in 3 separate tests; AC#11 covers QUORUM + PENDING + SOFT no-op paths).


2. Acceptance criteria — mapping to executed tests

AC# Description Test name (Jest path) Result
AC#1 FINALITY_ORDER shape AC#1 FINALITY_ORDER has 5 levels in canonical order (2 cases) PASS
AC#2 Initial level is PENDING AC#2/AC#18-#20 constructor > AC#2: new FSM starts at PENDING PASS
AC#3 PENDING→SOFT on first vote AC#3/AC#24 PENDING → SOFT > AC#3: at n=4, first vote transitions PENDING → SOFT PASS
AC#4 SOFT→QUORUM at threshold AC#4/AC#5 SOFT → QUORUM > AC#4: at n=4, threshold(4)=3 — third matching vote transitions SOFT → QUORUM PASS
AC#5 Same-sender duplicate ignored AC#4/AC#5 SOFT → QUORUM > AC#5: at n=4, two votes from same sender count as one — no QUORUM with sender duplicates PASS
AC#6 QUORUM→HARD on 2 consecutive AC#6/AC#7/AC#8/AC#23 QUORUM → HARD > AC#6: QUORUM held two consecutive epochs same triple → HARD PASS
AC#7 Different triple in epoch 2 resets AC#6/AC#7/AC#8/AC#23 QUORUM → HARD > AC#7: different triple in epoch 2 — stays at QUORUM, snapshot updated PASS
AC#8 observeEquivocation blocks HARD AC#6/AC#7/AC#8/AC#23 QUORUM → HARD > AC#8: equivocation observed between QUORUM moments — stays at QUORUM PASS
AC#9 HARD→ABSOLUTE on sealEpoch + window AC#9/AC#10/AC#11 HARD → ABSOLUTE > AC#9: sealEpoch at hardEpoch + 100 transitions HARD → ABSOLUTE PASS
AC#10 sealEpoch before window is no-op AC#9/AC#10/AC#11 HARD → ABSOLUTE > AC#10: sealEpoch at hardEpoch + 50 stays at HARD (window not elapsed) PASS
AC#11 sealEpoch below HARD is no-op AC#9/AC#10/AC#11 HARD → ABSOLUTE > AC#11: sealEpoch at QUORUM is a silent no-op + PENDING + SOFT cases PASS
AC#12 Monotonicity rollback throws AC#12 monotonicity > monotonicity enforced: __advance backward throws PASS
AC#13 requireExternalEffectsAllowed throws at PENDING/SOFT/QUORUM three separate tests PASS
AC#14 requireExternalEffectsAllowed silent at HARD/ABSOLUTE two separate tests PASS
AC#15 Single-arbiter n=1 trace AC#15 single arbiter n=1 full PENDING → ABSOLUTE trace > n=1 full lifecycle with 4 transitions logged PASS
AC#16 transitions() returns copy AC#16/AC#17/AC#22 transitions() > AC#16: mutating returned array does not affect internal log PASS
AC#17 Each transition has bigint epoch + Buffer evidence AC#16/AC#17/AC#22 transitions() > AC#17: each transition has bigint epoch + non-empty Buffer evidence PASS
AC#18 n_arbiters=0 throws QuorumError AC#2/AC#18-#20 constructor > AC#18: constructor with n_arbiters=0 throws QuorumError PASS
AC#19 Default 100n window AC#2/AC#18-#20 constructor > AC#19: default dispute_window_epochs is 100n PASS
AC#20 Custom dispute window honored AC#2/AC#18-#20 constructor > AC#20: custom dispute_window_epochs is honored PASS
AC#21 Forbidden-token self-scan AC#21 finality.ts body uses none of κ-forbidden tokens > forbidden tokens absent from non-comment source PASS
AC#22 5-level full path = 4 transitions AC#16/AC#17/AC#22 transitions() > AC#22: full PENDING → ABSOLUTE traversal records exactly 4 transitions PASS
AC#23 observeEquivocation does NOT add transition AC#6/AC#7/AC#8/AC#23 QUORUM → HARD > AC#23: observeEquivocation does NOT add a transition entry PASS
AC#24 Foreign round_id silently skipped AC#3/AC#24 PENDING → SOFT > AC#24: votes with foreign round_id are silently skipped PASS
AC#25 Evidence determinism AC#25 evidence determinism > two FSM instances with identical inputs produce byte-equal evidence PASS

3. Coverage observations

src/domains/consensus/finality.ts reported coverage from the --testPathPattern=consensus/finality filtered run shows the full file is exercised. The full-suite run (npm test) also passes the file.


4. Forbidden-token audit (manual cross-check)

Module body (JSDoc-stripped) scanned manually after the corpus self-scan test. Confirmed absent:

  • Math.<letter>
  • Date.<letter>
  • setTimeout, setInterval, setImmediate
  • process.hrtime, performance.now
  • Float literals (\b\d+\.\d+\b and [^.\w]\.\d+\b)

The body contains the literal await once — in a JSDoc string fragment that the comment-stripper removes before the regex. Verified clean by AC#21 test.


5. Commits

# SHA (abbrev) Subject
1 356d3720 audit(p3-2-1-finality-sm): inventory surface
2 928ddefd contract(p3-2-1-finality-sm): behavioral contract
3 304fc864 packet(p3-2-1-finality-sm): execution plan
4 (next) feat(p3-2-1-finality-sm): monotonic 5-level finality FSM with HARD-gate side effects
5 (this) verify(p3-2-1-finality-sm): test evidence

6. Approval

Step 5 of 5 complete. Ready for PR.


Back to top

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

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