R89.C — θ Phase 3 Staging — Implementation Packet

Step 3 of the 5-step chain for β task 4f718f92-12d7-4176-a3c6-3682ba27aef9.

Audit (§Step 1) inventoried the spec surface and locked the 13-entry shape. Contract (§Step 2) locked the per-entry behavioural shape and the eight invariants the file must encode. This packet (§Step 3) plans the writing: entry-by-entry, gives the title, the worktree slug, the Depends-on / Effort, and the 1-line description for each entry. Step 4 (the prompt file itself) then fills in the per-entry body.


1. Round + base + writeback expectations

  • Round: R89 Phase A (T0 mandate: autonomous Phase 2+3 execution)
  • Sub-round letter: C (R89.A = λ Phase 2 kickoff; R89.B = TBD; R89.C = θ Phase 3 staging)
  • Base SHA: fab4bf57 (main; R88.B close)
  • Branch: feature/r89-c-theta-phase-3-staging
  • Worktree: .worktrees/claude/r89-c-theta-phase-3-staging
  • β task ID: 4f718f92-12d7-4176-a3c6-3682ba27aef9
  • Test gates: npm run build && npm run lint && npm test — all three must pass on the unchanged Phase 0 + κ tree (no src/ deltas)

2. Entry-by-entry packet

13 entries in the order they will appear in the prompt file. Slugs follow kappa convention (p3-x-y-<short-name>). Each row notes the canonical mapping back to task-breakdown.md.

# Title Worktree slug Effort Depends on Wave Canonical map
1 P3.1.1 — Vote Message Types + Canonical Wire p3-1-1-vote-messages M Phase 2 λ seal; κ P1.5.4 canonical (shipped) 1 P3.1.1
2 P3.1.2 — Quorum Computation p3-1-2-quorum M P3.1.1 2 P3.1.2
3 P3.1.3 — Round / View State Machine p3-1-3-round-state-machine L P3.1.2, P3.6.1 (VRF stub for leader selection) 3 P3.1.3 (state-machine half)
4 P3.2.1 — Finality State Machine (5 levels) p3-2-1-finality-sm L P3.1.2 3 P3.2.1
5 P3.3.1 — Gossip Protocol — IHAVE/IWANT Wire p3-3-1-gossip-ihave-iwant M P3.1.1 2 P3.3.1 (wire half)
6 P3.3.2 — Gossip — Bloom Filter Dedup p3-3-2-bloom-dedup M P3.3.1 4 P3.3.1 (dedup half)
7 P3.3.3 — Gossip — Adaptive Fanout p3-3-3-adaptive-fanout S P3.3.1 4 P3.3.1 (fanout half)
8 P3.4.1 — Signed Time Anchors (STA) p3-4-1-time-anchors M P3.1.1, λ P2.1.1 2 P3.4.1
9 P3.5.1 — Equivocation Detection + Idempotent Slashing p3-5-1-equivocation M P3.1.2, λ P2.2.2 3 P3.5.1
10 P3.6.1 — VRF Stub (Leader Election) p3-6-1-vrf-stub M P3.1.1; ADR-002 (PROPOSED — ships stub) 3 (new)
11 P3.7.1 — θ MCP Tool Surface p3-7-1-mcp-tools M P3.1.2, P3.2.1, P3.4.1 4 (new)
12 P3.8.1 — Test Corpus + Parity Harness p3-8-1-parity-harness L P3.1.2, P3.2.1, P3.5.1 5 (new)
13 P3.9.1 — Fork Trigger Hook (ι handoff stub) p3-9-1-fork-hook S P3.1.2 5 (new)

Granularity total: 13. Inside contract §7’s [12, 15] band. ✓

3. Wave gating rationale

Wave 1 (post-Phase-2 λ seal)

  • P3.1.1 — Solo. Every other slice consumes its message types and canonical wire shape. No useful concurrent work exists until this lands.

Wave 2 (post-P3.1.1, 3-parallel)

After P3.1.1 lands, three independent surfaces can fan out:

  • P3.1.2 — Quorum computation. Pure-function module; depends only on P3.1.1’s message types.
  • P3.3.1 — Gossip wire layer (IHAVE/IWANT). Independent of voting.
  • P3.4.1 — Time Anchors. Independent of voting; needs only P3.1.1 sigs.

This is the maximum-parallelism wave. All three are scoped, testable in isolation, and have no inter-slice cross-references.

Wave 3 (post-P3.1.2, 4-parallel)

This is one above the 3-parallel safety ceiling. The justification: all four sub-tasks are state-machine work with hard dependencies on P3.1.2 but no inter-dependency. They were each Phase-1-κ-grade complexity slices (L or M), so each takes ~1 day. The R87 κ Wave 7 ran 3-parallel; this stretches to 4 because the slices are even more isolated (different files, no shared type-base modifications beyond what P3.1.2 stabilizes).

  • P3.1.3 — Round / View state machine. Uses VRF stub (P3.6.1) for leader selection. The VRF stub itself is in the same wave; the executor can mock the VRF result during development and import the real stub at PR-finalization.
  • P3.2.1 — Finality SM. Uses quorum from P3.1.2.
  • P3.5.1 — Equivocation. Uses P3.1.2 vote types + λ P2.2.2 penalty hook.
  • P3.6.1 — VRF stub. Independent module; HMAC-SHA256 stub per ADR-002 Option A.

If Wave 3 proves too wide, PM should drop P3.6.1 (the VRF stub) into a post-Wave-3 Wave 3.5 — its only consumer in this wave is P3.1.3, which can proceed with a mocked VRF until Wave 3.5 lands.

Wave 4 (post-P3.3.1, 3-parallel)

After the gossip wire lands, the gossip-specific extensions go parallel:

  • P3.3.2 — Bloom filter dedup. Self-contained module.
  • P3.3.3 — Adaptive fanout. Self-contained module.
  • P3.7.1 — MCP tool surface. Depends on P3.1.2, P3.2.1, P3.4.1 which all land in Waves 2–3. Independent of gossip-extension work.

Wave 5 (closer)

  • P3.8.1 — Test corpus + parity harness. Tests every preceding slice. Mirrors P1.5.5 in κ.
  • P3.9.1 — Fork trigger hook. ι Phase 5 handoff stub. Tiny.

4. ADR-status handling

4.1 ADR-002 (VRF) — PROPOSED

The prompt file ships P3.6.1 as a stub per ADR-002 Option A (HMAC-SHA256 internal implementation). Acceptance criteria for P3.6.1 are deliberately stub-shaped:

  • vrf_eval(seed: bigint, input: bigint): {output: bigint, proof: Buffer}
  • Output deterministic; verifiable via vrf_verify(seed, input, output, proof)
  • HMAC-SHA256 internals (NOT RFC 9381 ECVRF)
  • Interface designed for swap-in: a future Wave 6 or Phase 1.5 task can replace the HMAC core with @noble/curves Ed25519 without changing the public API
  • Documentation comment cites ADR-002 and warns “NOT externally verifiable”

This is the same shape δ took at P0.5.1/P0.5.2 per ADR-005 §Decision.

4.2 ADR-003 (BFT library) — PROPOSED

The state-machine slices (P3.1, P3.2, P3.5) are network-transport-agnostic; they ship UNCONDITIONALLY (Option C “spike” content). The gossip-transport slices (P3.3.x) have the following disclosure embedded in their entries:

> **GATE:** This slice ships gossip-transport code. ADR-003 is PROPOSED at
> R89.C staging. Executor should confirm with PM that ADR-003 has been
> Accepted (or that Option C-spike is the active strategy) BEFORE picking
> up this entry. If ADR-003 selects Option B (libp2p), the file paths and
> dependency footprint in this entry MAY change; re-issue the prompt with
> a revised packet.

This delivers the ADR-003 disclosure where it bites — in the gossip slices themselves — not just in the file intro.

5. λ dependency handling

The prompt file’s intro publishes:

> **Phase 2 λ Reputation is in flight at R89.C staging time.** ...

Two specific λ outputs are consumed:

  • P2.1.1 reputation schema — consumed by P3.4.1 (STA needs “top N arbiters by arbitration reputation”). Without P2.1.1 the entry’s “Eligible publishers” acceptance criterion cannot land.
  • P2.2.2 penalties — consumed by P3.5.1 (equivocation slashes the arbitration domain by 8000bps via λ’s penalty surface). Without P2.2.2 the slashing acceptance criterion cannot land.

The prompt file enforces this by:

  1. Naming both λ deps explicitly in the affected entries’ “Depends on” line
  2. Repeating the λ-in-flight disclosure inside each affected entry’s body
  3. Recommending the executor delay dispatch of those entries until Phase 2 seals

6. Single-arbiter compatibility

Per contract §5, two entries (P3.1.2 quorum, P3.2.1 finality) embed the single-arbiter compatibility clause in their acceptance criteria. The implementation pattern:

function quorumThreshold(n: bigint): bigint {
  return (n * 2n) / 3n + 1n;  // n=1 → 1, n=4 → 3, n=7 → 5, n=10 → 7
}

// Phase 0 deployment: n = 1 always; quorum_threshold(1n) = 1n; trivial.
// Phase 3+ deployment: n > 1; real BFT semantics activate.

The acceptance criterion is a unit test:

test("quorum is 1 for single-arbiter", () => {
  expect(quorumThreshold(1n)).toBe(1n);
});

This protects the consensus.md §Phase 0 posture promise: “trivially finalized because n = 1”.

7. Per-entry body sketches (Step 4 inputs)

Below are the 1–2 line content sketches for each of the 13 entries. Step 4 will expand each into ≥80 lines per the contract.

Entry 1: §P3.1.1 — Vote Message Types + Canonical Wire

Defines Vote, Commit, Reveal, ViewChange, EquivocationProof message types. Each carries the 3-tuple (round_id, merkle_root, rule_version_hash) plus Ed25519 signature. Canonical JSON serialization (deterministic key order, no whitespace) feeds the hash inputs. Tests: serialize 5 messages, verify hash determinism across 1000 iterations, verify signatures roundtrip.

Entry 2: §P3.1.2 — Quorum Computation

Pure functions: quorumThreshold(n), maxFaulty(n), hasQuorum(votes, n), intersect(quorumA, quorumB) (honest-majority overlap property). Worked table from consensus.md §Quorum math is an acceptance fixture. Tests: property test over n ∈ [1, 100]; single-arbiter clause.

Entry 3: §P3.1.3 — Round / View State Machine

Implements the commit-reveal protocol. Phase A commit, Phase B reveal, Phase C verify+aggregate. Timer constants from consensus.md (T_round=30s, T_commit_phase=10s, T_reveal_phase=10s, T_timeout=60s). View-change triggered on timeout | equivocation_observed | malformed_proposal. VRF called for next-leader selection. Emits VIEW_CHANGE_ACCEPTED into ζ.

Entry 4: §P3.2.1 — Finality State Machine (5 levels)

PENDING → SOFT → QUORUM → HARD → ABSOLUTE. Monotonic. Dispute window 100 epochs (per task-breakdown.md). HARD requires two consecutive QUORUM rounds without conflicting reveal. ABSOLUTE on epoch seal. No external side effects before HARD. Single-arbiter clause: n=1 reaches QUORUM trivially.

Entry 5: §P3.3.1 — Gossip Protocol — IHAVE/IWANT Wire

Wire shapes IHAVE, IWANT. Triple-anchor validation: rule_version_hash, state_root continuity, fork_id all must match before IWANT is issued. ADR-003 gate disclosure inside the entry.

Entry 6: §P3.3.2 — Gossip — Bloom Filter Dedup

Bloom filter sized via m = -n * ln(p) / (ln(2))². k = (m/n) * ln(2) ≈ 7 hashes per insert / query. False positive rate < 1%. Fresh filter per round.

Entry 7: §P3.3.3 — Gossip — Adaptive Fanout

fanout = max(3, min(10, 15 − connectivity_score)). Recomputed every 5 epochs from IHAVE/IWANT success metrics. Worked table from s08 is a fixture.

Entry 8: §P3.4.1 — Signed Time Anchors (STA)

Eligible publishers: top N arbiters by λ.reputation.arbitration (λ dep). Anchor format {publisher, timestamp_ms, epoch, signature}. Median over last K epochs. Drift detection: |local - median| > 30000ms. Monotonicity

  • replay protection (epoch < current - 10 rejected). λ-in-flight disclosure.

Entry 9: §P3.5.1 — Equivocation Detection + Idempotent Slashing

Detects double-signing on same (round_id, finality_level) tuple by same arbiter. Generates proof per consensus.md §Equivocation proof structure. Verifies BOTH signatures. Calls λ.applyPenalty(arbiter, DAMAGE_FRAUD=8000bps, domain=”arbitration”, event_id=proof_hash). Idempotent: proof_hash dedup.

Entry 10: §P3.6.1 — VRF Stub (Leader Election)

HMAC-SHA256(seed || input) → output. verify(seed, input, output) recomputes HMAC. NOT externally verifiable; comment cites ADR-002 Option A. Interface designed for swap to @noble/curves ECVRF without API change. Tests: determinism (10k inputs same seed → 10k identical outputs); collision property (different seeds → different outputs).

Entry 11: §P3.7.1 — θ MCP Tool Surface

Registers 5 MCP tools: consensus_propose, consensus_vote, consensus_finality, consensus_gossip, vrf_eval. Each is a thin handler over the corresponding domain module. Single-arbiter compat: tools return trivially-finalized results when n=1. Test plan: in-process MCP harness.

Entry 12: §P3.8.1 — Test Corpus + Parity Harness

Multi-arbiter simulation. 4 scenarios in default corpus:

  1. n=1 (single-arbiter Phase 0 compat)
  2. n=4, all honest (quorum reached trivially)
  3. n=4, 1 Byzantine (vote divergence → quorum still reached)
  4. n=4, equivocator (slashing fires; idempotent under re-submission) Mirrors P1.5.5 in κ. 10k synthetic events in <5s. Determinism: two runs produce byte-identical reports.

Entry 13: §P3.9.1 — Fork Trigger Hook (ι handoff stub)

When θ cannot reach quorum within 2*T_timeout, fire ForkTriggerEvent into a registered handler. Default handler: no-op (logs only). ι Phase 5 will register a real handler. Acceptance: hook fires with right payload shape; no-op handler does not crash; ι.implementation is OUT OF SCOPE.

8. Risk register

Risk Likelihood Impact Mitigation
ADR-002 moves to Rejected (require Option B noble) Low M Stub interface designed for swap; only P3.6.1 entry rewrites
ADR-003 moves to Option B (libp2p) Medium L Gossip slices flag the gate; rewrite needed only for P3.3.x
Phase 2 λ slips past R110 Medium M Prompt file STAYS staged; no dispatch authority. Replan at PM’s call
Granularity creep beyond 15 Low L Contract §7 hard cap; verification gate at Step 5
Per-entry length under 80 lines Low M kappa-pattern body covers it; verification checklist Step 5
Heritage citation slips in Low L Contract §6 prohibition; grep gate at Step 5

9. Acceptance check for Step 4 output

Step 4 produces docs/guides/implementation/task-prompts/p3.1-theta-consensus.md. Step 4 is acceptable iff:

  • File exists at that path ✓
  • Frontmatter matches contract §1.2 ✓
  • All 6 structural sections present per contract §1.3 ✓
  • 13 entries present in the order specified in this packet §2 ✓
  • Each entry has the 15-element shape per contract §1.4 ✓
  • Each entry’s body ≥80 lines (target ≥100 for kappa parity) ✓
  • ADR-002 + ADR-003 status disclosures present (intro + per-slice) ✓
  • λ-dependency disclosure present (intro + 2 affected slices) ✓
  • Single-arbiter clauses in P3.1.2 + P3.2.1 ✓
  • No heritage citations except flagged HERITAGE ✓
  • No src/ deltas ✓
  • All file refs link-checked ✓

Step 5 verification (Step 5) audits these and lands the coverage matrix.

10. Test plan for the chain

The 5-step chain itself has no src/ deltas, but the build/lint/test gates must still pass on the unchanged tree:

cd .worktrees/claude/r89-c-theta-phase-3-staging
npm run build   # passes on unchanged Phase 0 + κ
npm run lint    # passes on unchanged tree
npm test        # passes; expected ~2406/2406 per memory

Expected result: zero regressions, since the chain ships only docs/ artefacts.


Packet complete. Step 4 (write the prompt file itself) follows.


Back to top

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

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