Skip to content

[Dream Cycle 2026-10-15] performance: RaftConsensus.awaitConsensus() busy-polled instead of using its own 'consensus.achieved' event + security,hive-mind scan #4214

Closed

Description

@ruvnet

1. Tonight's Rotation

DATE=2026-10-15, DEEP=performance, SCAN=security,hive-mind, SLOT=0, SESSION_COMMIT=1882006a02487590c8c66a7eec8d7504f6af33c3. No bonus deep-dive triggers (DAYINT%25=15, DAYINT%75=65).

2. Ledger Check

Read docs/dream-cycle/LEDGER.md (last row: 2026-10-11, security, #4052/#4053). Verified via git ls-remote --heads origin "dream/*" + GitHub MCP tools that 2026-10-12 (intelligence, #4081), 10-13 (memory, #4138), 10-14 (swarm, #4174) all ran and have real branches/issues/draft PRs — same "ledger rows only reach main via PR merge" structural gap noted since 2026-08-19. Trailing-7-night fates (GitHub-verified via pull_request_read, not inferred): 10-08 #3909 open; 10-09 #3929 MERGED; 10-10 #3977 MERGED; 10-11 #4053 MERGED; 10-12 #4081 open; 10-13 #4138 open; 10-14 #4174 open. None stale (<14d). 10-05 #3754 remains closed-not-merged (duplicate, already recorded). STEP 1.1 learning signals: performance-surface findings since 09-15 are 5 distinct bugs (MoE load-balance, UnifiedSwarmCoordinator busy-poll, MessageBus busy-poll, mmrRerank, fallbackLouvain) — no 3x-repeated direction, no forced rotation needed; merge rate (3/7 recent) is healthy, no small-patch bias triggered.

STEP 1.2 prior-gist score (2026-10-14, swarm): 10/10 — 2 (benchmark evidence, stash-isolated + independent critic reproduction) + 2 (6 competitor rows) + 2 (3 specific recommendations with file paths) + 2 (valid witness) + 1 (1372 words, under 1500) + 1 (genuinely novel: resolved the 10-09/10-10 contradiction about ruvector's real exports).

3. Deep Dive Findings

Closing finding (REJECT, with evidence): v3/@claude-flow/cli/src/ruvector/graph-analyzer.ts's loadRuVector() checks for ruvector.hooks_graph_mincut/hooks_graph_cluster and @ruvector/wasm's GraphAnalyzer — none ever existed. 10-14's gist found the real ruvector package exports minCut/louvainCommunities/buildGraph and recommended wiring them in. Tonight I read those functions' actual source directly: they are plain JS, not native/WASM-accelerated, with the same incremental-bookkeeping complexity class as Ruflo's own already-fixed fallbackLouvain() (10-10, #3977). Wiring them in would not be a performance win — this closes a 3-night (10-09→10-10→10-14) research thread with a definitive answer rather than another partial correction.

Tonight's candidate: RaftConsensus.awaitConsensus() (v3/@claude-flow/swarm/src/consensus/raft.ts) — 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 via setInterval(..., 10) 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 — this fix makes awaitConsensus() do the same. Third instance of a bug class the ledger has fixed twice before (UnifiedSwarmCoordinator #3378 ~487x, MessageBus #3563 ~96.5% idle-tick reduction).

Two other candidates were researched and scored lower (see gist): RaftConsensus.handleInboundRaftMessage()'s O(n²) log-index-dedup linear scan (real, but conditional on an explicit ConsensusTransport being supplied — not wired in UnifiedSwarmCoordinator's default path, so weaker confirmed production value); and ByzantineConsensus/GossipConsensus carrying the identical busy-poll pattern (same fix, lower priority since raft is the default — carried forward).

4. 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.

5. Evaluation Receipt

evaluated: accepted. 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; candidate mean 0.058ms / max 0.346ms, 0 timers created. Independently reproduced end-to-end by an adversarial critic via its own separate stash/pop cycle, plus its own concurrency test (200 simultaneous awaitConsensus() calls on 200 distinct proposals, zero leaks) and same-tick timeout-vs-vote race test (5 runs, no crash/double-resolve): CONFIRMED, no blocking issues.

6. Darwin Results

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.

7. Flywheel Evidence

No .claude-flow/flywheel/ state exists in this repo (confirmed, same as every recent night). 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 independent adversarial critique transcript, this issue, and the committed gist.

8. Reward Hack Check

No standalone static reward-hack CLI reachable for this fix class tonight (@metaharness/weight-eft exists but targets LoRA fine-tuning training-data contamination, not applicable — no LLM training run happened). Manual checklist independently verified twice (by me, 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 structurally prevents a double-fire of 'consensus.achieved' for the same proposal (confirmed by code reading, not just test absence).

9. Security Review

Not security-sensitive — pure in-process EventEmitter rewiring (swap a polling timer for an existing, already-emitted event listener), no new I/O/network/credential/filesystem surface. setMaxListeners(0) in the constructor is a correctly-scoped companion change (listeners are always 1:1 with in-flight proposals and removed on resolution — confirmed via the critic's 200-concurrent-proposal leak test, final listenerCount() === 0).

10. Scan Findings: security

Two findings (not fixed tonight, carried forward): (1) settings-risk-scanner.ts only inspects settings.hooks/settings.permissions.allow, never enabledPlugins/extraKnownMarketplaces — a capability-granting settings.json surface v3/@claude-flow/cli/src/mods/install.ts's withModEnabled() writes, which every current scanner call site (fresh-init, init --upgrade, the CJS SessionStart port) is blind to. (2) DENYLISTED_ENV_NAMES (CWE-427 loader-hijack family) is missing PYTHONSTARTUP/PYTHONHOME/RUBYOPT/JAVA_TOOL_OPTIONS/_JAVA_OPTIONS/JDK_JAVA_OPTIONS/GIT_ASKPASS/R_PROFILE/R_ENVIRON alongside the names it already covers.

11. Scan Findings: hive-mind

Three findings (not fixed tonight, carried forward): (1) Confirmed real and dead: raft.ts's public handleVoteRequest()/handleAppendEntries() (lines 513–578) are exercised only by consensus.test.ts, zero production callers — the live receiver is the private handleInboundRaftMessage(). The dead path diverges (no index-dedup, unconditional votedFor mutation on heartbeat), giving false test confidence, matching what 10-10's ledger note flagged. (2) New: crdt/quorum are offered as first-class consensus choices in hive-mind.ts's CLI/wizard, but ConsensusAlgorithm only supports raft|byzantine|gossip|paxos and hive-mind.ts never imports the real ConsensusEngine at all — picking crdt/quorum silently does nothing (cosmetic banner only). (3) Corroborated (not new): byzantine.ts's handleCommit() PBFT message-count quorum remains a separate acceptance path from vote()'s weighted tally (flagged 2026-08-24, still unaddressed).

12. Competitors Reviewed

LangGraph (Pregel-style superstep, sync barrier per step — B), AutoGen v0.4/MS Agent Framework (actor-model async message-passing — B), CrewAI (event-driven Flows, no published dispatch internals — C), OpenAI Swarm→Agents SDK (stateless/sequential, no server event bus — C), Milvus/Weaviate/Vespa (real runtime CPU-feature-flag dispatch to compiled SIMD variants — the pattern Ruflo's now-explained dead graph-analyzer.ts detection imitated without a real backend — B). None of the four orchestration frameworks publish internals on poll-vs-event consensus/task-wait dispatch — unaudited industry-wide, not just a Ruflo gap. Full detail, sources, and additional 2026 academic citations (MAFBench arXiv:2602.03128, "The Orchestrator Bottleneck" ICML 2026, CP-WBFT arXiv:2511.10400) in the gist.

13. Gist

docs/dream-cycle/dream-gist-2026-10-15.md (this branch). No gh gist create/gist-equivalent MCP tool reachable this session (same disclosed FALLBACK as every night since 2026-09-30) — full report committed to the repo and embedded above instead.

14. Witness

Session commit:   1882006a02487590c8c66a7eec8d7504f6af33c3
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.

15. Recommendation

Human review required. Do not self-merge. Do not autonomously promote Flywheel state. Candidate is small (1 file + 1 test file), independently confirmed by an adversarial critic with no blocking issues, and fixes a real, measured (~63x mean-latency, 30→0 poll-timer) production-reachable performance bug on Ruflo's default consensus algorithm.

Activity

  1. added 2 commits that reference this issue on Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions