P3.4.1 — Signed Time Anchors (STA) — Verification Evidence

1. Gate results

Gate Command Result
Build npm run build PASS (tsc clean; copy-migrations OK)
Lint npm run lint PASS (eslint src — zero warnings)
Full test suite npm test 2732 passed / 2 failed / 2734 total (failures pre-existing on main; see §3)
Focused (this slice) npm test -- src/__tests__/domains/consensus/time-anchors.test.ts 47 passed / 0 failed / 47 total

Baseline at origin/main @ e63a8bcf: 2682 passed / 5 failed / 2687 total. Worktree state: 2732 passed / 2 failed / 2734 total. Delta is +47 tests (matches the 47 tests added by this slice — file added 30 numbered cases T1–T30 plus 17 supplementary cases covering defaults, edges, boundaries, and ergonomics).

2. Contract invariant → test cross-walk

Every invariant from the contract §3 maps to at least one test in src/__tests__/domains/consensus/time-anchors.test.ts.

Invariant What it asserts Realized in test ID
I1 STA shape 4-field readonly struct; signature 64 bytes T1
I2 eligiblePublishers — top-N by score Sort DESC, stable tie-break, defaults, edges T5, T6, T7, T8, T9, plus “default top_n is 7n”, “top_n >= snapshot.size”, “top_n = 0n”
I3 signAnchor / verifyAnchor — Ed25519 PURE Roundtrip success, tampering rejection (sig + ts), determinism T1, T2, T3, T4, “canonicalSerializeAnchor stable bytes”
I4 computeMedian — bigint median over filtered window Odd, even, empty, all-filtered, dup-publisher, replay-floor, future-dropped, default K, per-publisher tie-break T10, T11, T11b, T12, T12b, T13, T14, “future-dated dropped”, “default k_epochs”, “tie-break on per-publisher selection”
I5 detectDrift — advisory, strict-> threshold Within / over / exact / negative skew / zero / custom / default T15, T16, T17, T18, “zero drift”, “custom threshold”, “default threshold”
I6 validateMonotonicity — soft fault flagging Strict-advance violation, advance-clean, same-epoch non-violation, multi-publisher mix, defensive copy, empty, single anchor T19, T20, T21, T22, “input unmodified”, “empty input”, “single anchor”
I7 rejectReplay — boolean past-replay gate Reject case, boundary keep, current keep, future not rejected, custom window, default T23, T24, T25, T26, “custom replay_window”, “default replay_window”
I8 Determinism (κ-inherited) Sign deterministic; canonical body byte-identical T4, T27, T29, plus T28 (static scanner)
I9 Error model Class re-export + instanceof identity T30

3. Pre-existing failures (NOT introduced by this slice)

Worktree shows 2 failed tests, both in src/__tests__/domains/reputation/tools.test.ts. Verification of pre- existing status:

  1. reputation_check_gates happy pathMigration 997_partial.sql failed: SqliteError: no such function: nonexistent_fn
  2. another reputation_check_gates case — same root cause

Both fail when the full Jest suite runs src/__tests__/db-init.test.ts first; that test writes a 997_partial.sql fixture into the src/db/migrations/ directory and a later test then picks it up via initDb(). Run alone, both reputation/tools.test.ts and db-init.test.ts pass cleanly.

Confirmed pre-existing on main e63a8bcf:

$ cd E:/AMS && npm test --silent --coverage=false
Test Suites: 2 failed, 55 passed, 57 total
Tests:       5 failed, 2682 passed, 2687 total

The worktree shows fewer pre-existing failures (2 vs. 5) because Jest’s file-ordering differs by suite count (worktree adds 1 suite). Both states are consistent with the pre-existing 997_partial.sql test- isolation defect.

This slice does NOT introduce new failures. Net new tests: +47 passing.

4. Forbidden-token scanner result

Test T28 time-anchors.ts source body contains no forbidden tokens runs in-suite and passes. Scanner sweeps time-anchors.ts for:

Date.now, process.hrtime, performance.now,
Math.random, Math.floor, Math.ceil, Math.round,
setTimeout, setInterval, console.,
crypto.sign(, crypto.verify(, import * as crypto

with JSDoc / line comment / template literal stripping. Zero hits.

5. λ surface integration approach

Per the dispatch packet:

λ DEPENDENCY: SATISFIED. Phase 2 λ closed at #233. Use library- level selectReputation from src/domains/reputation/schema.ts for eligible-publisher lookup (avoids MCP roundtrip).

The integration approach taken:

  • time-anchors.ts is pure — no DB handle, no Map construction, no imports from src/domains/reputation/.
  • eligiblePublishers(reputationSnapshot: ReadonlyMap<string, bigint>, top_n) accepts an opaque snapshot Map constructed by the caller.
  • Production callers will build the snapshot Map by walking selectReputation(db, node_id, "arbitration") for each candidate arbiter, OR by querying idx_reputations_leaderboard ON reputations (domain, score DESC) directly. Either path is a one-step assembly outside this module’s surface.
  • Test fixtures pass literal Maps — no SQLite handle needed.

This keeps the module pure, decouples it from the λ schema’s mutable DB surface, and avoids an MCP roundtrip in production. The “library-level selectReputation” wording in the dispatch packet is honored — this module reads at the schema-level via selectReputation-derived snapshots, NOT via an MCP tool.

6. Defaults rationale (π-governable)

Per source prompt §Common gotchas line 1645, every magic number is exposed as a defaulted parameter:

Constant Default Exposed as
top_n 7n eligiblePublishers(snap, top_n?) and isEligiblePublisher(pub, snap, top_n?)
k_epochs 10n computeMedian(anchors, current, k_epochs?)
threshold_ms 30_000n detectDrift(local, median, threshold_ms?)
replay_window 10n rejectReplay(anchor, current, replay_window?)

All four are bigint and all four have default-handling tests (“default top_n is 7n”, “default k_epochs is 10n”, “default threshold”, “default replay_window is 10n”). A future π governance task can tune these without re-shipping time-anchors.ts.

7. Determinism evidence

  • All quantities bigint (no float arithmetic in the module body).
  • Zero Math.* and Math.random references (scanner-enforced).
  • Zero Date.now, process.hrtime, performance.now references.
  • Zero side effects (no setTimeout, setInterval, console.*).
  • Ed25519 PURE-mode sign(null, body, privKey) is deterministic — test T4 asserts byte-equal signatures on repeat sign.
  • κ canonicalize inheritance: identical input → byte-identical canonical body in every Node ≥ 20 process. Single-process surrogate tested at T27, T29, and the “canonicalSerializeAnchor stable bytes” case.

8. Spec contradiction check

The source prompt, s06 §Signed time anchors, and s08 §Signed Time Anchors (STA) were cross-read in audit §2. All three agree on:

  • Field shape {publisher, timestamp_ms, epoch, signature}.
  • Drift threshold “30s” (= 30_000n ms).
  • “Deprioritized” semantics, NOT rejection.
  • Publisher eligibility tied to reputation.

No contradictions encountered. No STOP condition triggered.

9. Deferred / out-of-scope (per packet §8)

The following are NOT part of this slice:

  • STA periodic broadcast scheduler (P3.3.x gossip)
  • STA-aware proposal priority queue
  • STA persistence layer
  • π governance hooks for the four defaults
  • eligiblePublishers direct-from-DB convenience

These are Wave 3+ work. The slice ships the foundation they will all build on.

10. Commits

5 commits on feature/p3-4-1-time-anchors:

# SHA (short) Step Message
1 c1329200 Audit audit(p3-4-1-time-anchors): inventory STA surface — λ dep satisfied at #226, P3.1.1 sig path reusable
2 90277baa Contract contract(p3-4-1-time-anchors): STA surface + 9 invariants + 30 tests + π-governable defaults
3 32fd850a Packet packet(p3-4-1-time-anchors): 9-section module + 30-test suite + 5-commit plan
4 ec86a6e0 Feat feat(p3-4-1-time-anchors): STA wire + 8 helpers — sign/verify/median/drift/monotonicity/replay
5 (this) Verify verify(p3-4-1-time-anchors): all gates pass; 47 new tests; pre-existing 2 failures

11. Done

  • All 8 exports per contract §2 shipped in time-anchors.ts
  • All 30 contract test IDs (T1–T30) implemented in time-anchors.test.ts
  • npm run build clean
  • npm run lint clean
  • npm test — net delta +47 tests, all passing; pre-existing 2 failures documented and confirmed unrelated
  • λ surface integration: pure module, snapshot-Map input pattern
  • Determinism: bigint-only, no wall-clock, no Math, named crypto imports
  • Forbidden-token scanner passes
  • Spec contradiction check: clean (s06 + s08 + prompt mutually consistent)
  • 5-step chain 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.