P3.3.3 — Gossip Adaptive Fanout — Audit

Step 1 of the 5-step chain (CLAUDE.md §6). Phase 3 θ Wave 4 — the connectivity-driven fanout helper plus the local exchange tracker that recomputes the connectivity score every N epochs. Companion P3.3.2 (Bloom dedup) and P3.7.1 (MCP tool surface) ship in parallel.

§1. Surface inventory at base SHA 9c7165d5

Path Exists? Role
src/domains/consensus/ Yes — θ domain root (P3.1.1 / P3.1.2 / P3.1.3 / P3.3.1 / P3.4.1 / P3.5.1 / P3.6.1 shipped) θ domain root
src/domains/consensus/adaptive-fanout.ts No — to create computeFanout, clamp, FanoutTracker class
src/__tests__/domains/consensus/adaptive-fanout.test.ts No — to create Worked table + clamping + tracker lifecycle
src/domains/consensus/gossip-wire.ts Yes (P3.3.1 — e63a8bcf) Consumed peer/event shape reference — NOT imported (slice is pure data, no wire dependency)
src/domains/rules/builtins.ts Yes (κ P1.3.2) Reference for the clamp ternary pattern (line ~clamp const) — NOT imported (κ’s clamp works on BuiltinValue tuples; this slice uses a bare bigint helper)

§2. Spec inventory

2.1 Worked table — s08 §Adaptive fanout

docs/spec/s08-gossip.md §Adaptive fanout (lines 75–92):

fanout = max(3, min(10, 15 - connectivity_score))

Where connectivity_score is the number of live peers in the routing table, clamped to [0, 12].

connectivity_score fanout
0 (isolated) 10
3 10
5 10
7 8
10 5
12 3 (minimum)

Minimum fanout = 3 (always gossip somewhere); maximum = 10 (bandwidth ceiling on isolated nodes). Recomputation period = 5 epochs, driven by successful IHAVE/IWANT exchanges during the window.

2.2 Acceptance criteria (source prompt §P3.3.3 lines 1314–1328)

  • computeFanout(connectivity_score: bigint): bigint returns max(3n, min(10n, 15n - connectivity_score))
  • Worked-table fixture passes for score ∈ {0, 3, 5, 7, 10, 12}
  • Score clamped to [0n, 12n] — values outside coerced (negative → 0n; >12n → 12n)
  • Recomputed every 5 epochs based on successful IHAVE/IWANT exchanges
  • trackExchange(peer_id, success, epoch) records exchange outcome
  • recomputeIfDue(current_epoch, last_recompute_epoch, period_epochs = 5n) returns updated score iff current - last >= period
  • Score uses live-peer count: peers with ≥ 1 successful exchange in last period_epochs

2.3 Determinism guardrails

docs/spec/s08-gossip.md does not list a forbidden-token sweep, but the task prompt’s FORBIDDENS section requires:

  • No socket I/O — tracker is pure data
  • bigint throughout, no Number
  • No Math.*, no Date.*, no Math.random, no floats
  • No new npm deps
  • Not editing main checkout

These match the existing P3 forbidden-token scanners (messages.test.ts Group 9, gossip-wire.test.ts Group 15, time-anchors.test.ts Group 8).

§3. Reuse map

Symbol / pattern Source Action
bigint ternary clamp pattern src/domains/rules/builtins.ts clamp const Re-implement narrowly — κ’s clamp works on BuiltinValue tuples plus throws on lo > hi; this slice is a flat bigint helper
Header banner conventions src/domains/consensus/gossip-wire.ts lines 1–57 Replicate banner + forbidden-token self-scan note
Per-slice forbidden-token scanner src/__tests__/domains/consensus/time-anchors.test.ts Group 8 Replicate, narrowed to adaptive-fanout.ts
Test fixture style (bufN(seed)) src/__tests__/domains/consensus/gossip-wire.test.ts lines 38–44 Not needed — this slice uses only string peer_id keys, no Buffers

§4. Open questions

None — task prompt §P3.3.3 + s08 §Adaptive fanout fully resolve all design points (clamp order, period default, live-peer semantics, retention).

§5. Out of scope

  • Wiring FanoutTracker into a live gossip loop (deferred — P3.3.1 / P3.3.2 / P3.7.1 handle wire + dedup + MCP surface)
  • Persisting the score across process restarts (in-memory only, by design — exchange records are ephemeral over period_epochs)
  • Multi-arbiter convergence of fanout scores (no global view; each node computes its own)
  • Connectivity-aware peer selection (which peers to gossip to — task prompt scopes this to the count, not the picker)

Back to top

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

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