Skip to content

dream(performance): #4214 RaftConsensus.awaitConsensus() busy-poll → event-driven (evaluated, ACCEPT) - #4215

Merged
ruvnet merged 2 commits into
mainfrom
dream/2026-10-15-performance
Oct 17, 2026
Merged

ruvnet merged 2 commits into
mainfrom
dream/2026-10-15-performance

Conversation

@ruvnet

@ruvnet ruvnet commented Oct 15, 2026

Copy link
Copy Markdown
Owner

1. Hypothesis

Given a pending consensus proposal in RaftConsensus, when awaitConsensus() resolves via the already-emitted 'consensus.achieved' event (plus a single setTimeout for expiry) instead of polling this.proposals on a 10ms setInterval, then (a) ConsensusResult for accept/reject/expire/not-found cases stays behaviorally identical, and (b) proposals that resolve before timeout should resolve with lower, less-discretized latency and zero timer-tick overhead while waiting, subject to: all existing tests green; no change to quorum/vote logic in checkConsensus(); deterministic, $0, zero-LLM evaluation. Frozen before evaluation; not modified after.

2. Candidate

v3/@claude-flow/swarm/src/consensus/raft.ts's RaftConsensus.awaitConsensus() — Ruflo's default hive-mind consensus algorithm (config.algorithm ?? 'raft' in both ConsensusEngine and UnifiedSwarmCoordinator), reachable from production via the public UnifiedSwarmCoordinator.proposeConsensus() API — busy-polled this.proposals.get(proposalId) every 10ms via setInterval even though checkConsensus() (called from vote()) already emits 'consensus.achieved' on every pending→accepted/rejected transition. UnifiedSwarmCoordinator itself already listens to that exact event (unified-coordinator.ts:712) for an unrelated purpose. Third instance of a bug class the ledger has fixed twice before (UnifiedSwarmCoordinator #3378 ~487x, MessageBus #3563 ~96.5% idle-tick reduction).

Fix: synchronous status check first (unknown proposal → immediate throw; already-resolved → immediate return), otherwise listen for 'consensus.achieved' filtered by proposalId, with a single setTimeout as the expiry fallback instead of a poll. Constructor adds this.setMaxListeners(0) since listeners are now 1:1 with in-flight proposals (unbounded is correct — they're always cleaned up on resolution). Diff: 1 source file (~28 insertions / 19 deletions) + 1 new test file.

3. Evaluation Receipt

Real evaluator: Vitest 4.1.8, deterministic, $0, zero LLM calls.

New test file raft-await-consensus-event-driven.test.ts (4 tests). Stash-isolated baseline (git stash on just raft.ts): 3/4 fail exactly as predicted (setInterval observed called when asserted not-called; immediate-return case measured 11ms ≥ the <10ms bound; expiry case shows 5 setInterval calls); the 4th (unknown-proposal-id immediate rejection, pre-existing behavior) passes both ways — a correct non-discriminating control. Candidate: 4/4 pass. Full @claude-flow/swarm suite: 281/281 passed, 19/19 files green (277 pre-existing + 4 new), zero regressions. tsc --noEmit: clean, 0 errors, both ways.

Standalone wall-clock probe (30 trials, vote arriving 3–9ms after awaitConsensus() starts — deliberately off the old 10ms poll grid): baseline mean 3.639ms / max 7.492ms vote→resolve latency, 30 setInterval timers created (1 per call); candidate mean 0.058ms / max 0.346ms, 0 timers created.

4. Baseline Comparison

Baseline Candidate
Vote→resolve latency (mean, N=30) 3.639ms 0.058ms (~63x)
Vote→resolve latency (max, N=30) 7.492ms 0.346ms
setInterval timers created while waiting 30 0
Unknown-proposal-id behavior throws Proposal <id> not found unchanged
Already-resolved-before-await behavior resolves (via next poll tick) resolves synchronously
Full @claude-flow/swarm suite 277/277 281/281 (+4 new)
tsc --noEmit clean clean

5. Darwin Lineage

Skipped — binary correctness/wiring fix (listen to an already-emitted event instead of polling), no continuous/categorical parameter for Darwin's real interface (npx ruvector harness darwin <config> --execute, confirmed available in STEP 0.5) to search over. Same skip class as nearly every accepted fix since 2026-08-18.

6. Flywheel Evidence

No .claude-flow/flywheel/ state exists in this repo. npx ruvector harness flywheel gate <evidence> requires a signed LLM-task-corpus replay-evidence schema not applicable to this deterministic fix class. Evidence retained as: the 4 new discriminating tests, the stash-isolated comparison (reproduced independently twice), the standalone latency probe, the full adversarial critique transcript, issue #4214, and the committed gist.

7. Reward Hack Check

Clean — independently checked twice (by the authoring session, and by a separate adversarial-critic subagent with no authoring context): no test/threshold weakened, no gold data touched, no cherry-picking, no evaluator exploitation, no cost increase, no seed manipulation, no hidden preprocessing, no error suppression, no undocumented cache. checkConsensus()'s pending-only guard (if (!proposal || proposal.status !== 'pending') return;) structurally prevents a double-fire of 'consensus.achieved' for the same proposal.

8. Security Review

Not security-sensitive — pure in-process EventEmitter rewiring, no new I/O/network/credential/filesystem surface. Independent adversarial critique (separate subagent, no authoring context): CONFIRMED, no blocking issues. Verified: the bug claim by reading the full diff; baseline-fails/candidate-passes via its own stash/pop cycle; 200 concurrent awaitConsensus() calls on 200 distinct proposals resolve correctly with zero listener leak (listenerCount() === 0 after); a same-tick timeout-vs-vote race (5 runs) produces no crash/double-resolve/leak; this.proposals.get(proposalId) ?? proposal fallback confirmed harmless dead code (nothing ever calls .delete() on the map).

9. Regression Analysis

Zero regressions: full @claude-flow/swarm suite failed-file set unaffected (281/281, 277 pre-existing + 4 new), tsc --noEmit clean both ways. Only callers of awaitConsensus() are unified-coordinator.ts:499 (production) and the package's own tests (grep-confirmed).

10. ADR

Not created — scoped wiring/correctness fix to an already-decided mechanism (consensus resolution should follow the event the module already emits), not a new architectural decision. Matches repo convention for this fix class (#3357/#3539/#3562/#3929 — none created an ADR).

11. Research Gist

docs/dream-cycle/dream-gist-2026-10-15.md (this branch) — full SOTA report: 5-role parallel research fan-out (deep researcher, 2 scan researchers, competitor analyst, direct architecture inspection), graded 2026 sources (MAFBench arXiv:2602.03128 grade B, "The Orchestrator Bottleneck" ICML 2026 grade A, CP-WBFT arXiv:2511.10400 grade B), a 5-row competitor comparison table, and closes a 3-night research thread on graph-analyzer.ts's dead native-acceleration detection with direct-evidence REJECT. No gh gist create/gist-equivalent MCP tool reachable this session (same disclosed FALLBACK as every night since 2026-09-30) — full report embedded in issue #4214 instead.

12. Issue

Closes #4214 — full 15-section report: ledger check with GitHub-verified fates for the trailing 7 nights (including the 10-12/10-13/10-14 ledger-transcription gap), prior-gist scoring (10/10), research, evaluation receipt, adversarial critique, reward-hack check, security review, security/hive-mind scan findings (settings-risk-scanner enabledPlugins gap, DENYLISTED_ENV_NAMES gaps, dead duplicate Raft receiver, crdt/quorum CLI vaporware), competitor comparison, witness.

13. Witness

Session commit:   1882006a02487590c8c66a7eec8d7504f6af33c3
Candidate commit: d6b4f426 (this branch's head)
Gist SHA-256 (pre-witness): df858321819d9bb00295ae21dba03fe2752975b6592a86cb845fe06dadfac367
Witness stamp:    2ca9c4955cff2ddfdba4e633a5a823cded52979f91b11423cc95fcc2e1fc5c87

Verifier: fetch docs/dream-cycle/dream-gist-2026-10-15.md from this branch, strip the Witness block's filled-in values back to PENDING, SHA-256 the file, concatenate with the session commit above, SHA-256 again — must equal the witness stamp.

14. Merge Policy

Human review required. Do not self-merge. Do not autonomously promote Flywheel state. Independent adversarial critic verdict: CONFIRMED, no blocking issues.


🤖 Generated with RuFlo

https://claude.ai/code/session_01MFLiva2zVEXr2n9UTK5YgU


Generated by Claude Code

claude and others added 2 commits October 15, 2026 06:26
… event-driven

RaftConsensus.awaitConsensus() polled this.proposals via setInterval(...,10)
even though checkConsensus() already emits 'consensus.achieved' on every
accept/reject transition. Raft is Ruflo's default hive-mind consensus
algorithm, reachable from production via UnifiedSwarmCoordinator.proposeConsensus().
Third instance of a busy-poll bug class already fixed twice (UnifiedSwarmCoordinator
#3378, MessageBus #3563).

4 new discriminating tests, stash-isolated baseline-fails/candidate-passes
(3/4, 1 correct non-discriminating control), independently reproduced by an
adversarial critic. Full @claude-flow/swarm suite 281/281 (277 existing + 4
new), tsc clean both ways. Standalone wall-clock probe: 3.639ms/30 poll-timers
-> 0.058ms/0 timers (N=30).

Also closes a 3-night research thread (10-09/10-10/10-14) on graph-analyzer.ts's
dead native-acceleration detection: direct inspection shows ruvector's real
minCut/louvainCommunities exports are plain JS, not native-accelerated, so
wiring them in would not be a performance win (REJECT, with evidence).

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01MFLiva2zVEXr2n9UTK5YgU
Fill in the real PR number (was a placeholder pending PR creation) in
tonight's ledger row, and note the 10-12/10-13/10-14 ledger-transcription
gap per the established convention.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01MFLiva2zVEXr2n9UTK5YgU
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants