{{ message }}
Repository navigation
feat(ruflo-swarm): live swarm pane mod for Claude Code (function hooks) - #3607
Merged
Merged
Conversation
…ctions, commands Function-hooks module for the ruflo-swarm plugin: reads ruflo's swarm, agent, task, claims and hive-mind files via $.fs, merges Claude Code's own subagents, draws a live Pane, and acts through fixed-argv ruflo CLI calls with confirm-on-destructive and verify-on-disk. Co-Authored-By: RuFlo <ruv@ruv.net>
… ruflo run Co-Authored-By: RuFlo <ruv@ruv.net>
…and audit options Co-Authored-By: RuFlo <ruv@ruv.net>
…efresh benchmark at 8/32/100 agents Co-Authored-By: RuFlo <ruv@ruv.net>
…; document the mod Live run on 2.1.282 showed a named Agent call runs as an in-process teammate listed as type 'teammate', and a loop whose tool calls reach the hooks with no spawn or listing. Name the first, call the second a subagent by id suffix. Co-Authored-By: RuFlo <ruv@ruv.net>
… argv value Co-Authored-By: RuFlo <ruv@ruv.net>
…ract); keep pane state per folder Reads agents[id].config.host (kind ruos), .claude-flow/ruos/hosts.json and the tail of .claude-flow/ruos/events.jsonl as agreed with the ruflo-ruos lane: ids checked, names cleaned and bounded, free text (error, lastResult) never read, an event log past 2 MB not read. The router pick and selection held in $.store are now keyed by working directory, and the router line says the pick's age, after a live 2.1.287 run showed a pick from another folder. Co-Authored-By: RuFlo <ruv@ruv.net>
They import 'claude-code/testing', which exists only under `claude plugin test plugins/ruflo-swarm`; root vitest cannot resolve it (same pattern as ruflo-x-gateway). The pure readers still run under root vitest via tests/parse.spec.ts. Co-Authored-By: RuFlo <ruv@ruv.net>
ruvnet
added a commit
that referenced
this pull request
Oct 1, 2026
…artbeat - every remote command gets a fresh 64-bit nonce; markers print as RUOS<nonce>_NAME via printf 'RUOS%s_NAME' '<nonce>', so the token never appears in the command or its desktop_exec echo; parsers accept only whole lines with that command's nonce (test replays raw echoes that contain every marker name) - test that poll command + 2 KiB slice fit the ~4 KiB cap, echo counted - pid file holds digits only; audit hash masks the nonce (stable) - scripts/ci-test-baseline.txt: the 6 node:test files (run by smoke.sh) that root vitest reports as 'No test suite found' (lead-authorized) - hosts.json heartbeatAt is ISO for the swarm pane (#3607) Co-Authored-By: RuFlo <ruv@ruv.net>
ruvnet
added a commit
that referenced
this pull request
Oct 1, 2026
…lerant $ (ADR-404)
- engine.create adds a flat $.ruflo noun (types/index.d.ts, plugin.json
"types"): segment({id,text}), lastRoute(), snapshot(). Other mods (ruOS,
ruflo-ruos) contribute status segments instead of drawing a second bar.
Segments are untrusted: id token, control/bidi stripped, 48 chars, max 8,
sorted by id. One drawer for the status line, from the $ beneath.
- plugin.register trust gate: names what a later-registering user-tier mod
can do (process.run, http.fetch, env.set; tool.check, tool.call, *,
classic.*, plugin.register, prompt.compose), from the host scan.
userConfig modTrust: observe (default) | refuse-risky | off, plus
modTrustAllow. Under refuse-risky a gate failure refuses.
- every ui.status/log/toast call tolerates refusal (admins can withhold $)
- tsconfig extends the types Claude Code >= 2.1.287 writes on load
- kit tests: 15/15 on Claude Code 2.1.287, incl. two tighten-only tool.check
mods composing, trust refusal at load, budget ladder with options
- vitest: 95/95 (noun/segment and trust suites added)
- CI ratchet baseline: the four engine-kit files (claude-code/testing is not
resolvable by root vitest), as #3605/#3607 do
Co-Authored-By: RuFlo <ruv@ruv.net>
…ust gate Co-Authored-By: RuFlo <ruv@ruv.net>
This was referenced Oct 1, 2026
This was referenced Oct 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

What this adds
A Claude Code mod (function-hooks module) in
plugins/ruflo-swarm/hooks/. It draws a live swarm pane inside Claude Code and acts on the swarm through the real ruflo CLI. Without function hooks, nothing in it loads, and the plugin's existing commands, skills and agents behave exactly as before (thehooks.jsonholdsmodulesonly).Only
plugins/ruflo-swarm/**changes. Nothing outside the plugin is touched: no CLI source, nomarketplace.json, no lockfile.Feature checklist
$.session.usage(); a missing figure shows asn/arouteThresholdit reads "below threshold"agent.spawn: Claude Code subagents become members; opt-ininjectSpawnContextappends a one-line swarm note (facts from disk only)turn.*/tool.call: live activity per loop;tool.callonly observes and passes the result through unchangedon('*')audit: event and tool names plus agent ids, buffered (max 200) and flushed every 30 s viamemory storeinto theruflo-swarm-auditnamespace. Never content./ruflo-swarm-pane,/ruflo-swarm-status [json],/ruflo-swarm-topology,/ruflo-swarm-claims,/ruflo-swarm-consensus; the markdown/ruflo-swarm:watchalso opens the pane, then falls throughclaims_status) and says so.agent logs; for Claude Code loops it shows the tool calls the hooks saw, labelled "activity".$.ruflonounContract with mods-planner (core mod)
Proposed in writing at the start; no reply had arrived when this PR opened, so I proceeded as the proposal allowed. mods-planner has since agreed to (a), (b) and (c) as written. Their notes:
agent.spawnonly when the user turns oncostHardStop. It then denies spawns at 100% of budget and never rewrites the prompt. The two compose: a deny ends the dispatch, and this mod's opt-in note only appends text.$.ruflo(segment,lastRoute,snapshot, typed inplugins/ruflo-mods/types/index.d.ts). Reading the router pick from it is a possible follow-up; this PR does not depend on it.The terms as proposed:
$.ruflonoun. This mod reads swarm state from disk itself, so it can be removed or used without the core mod. If the core mod later ships$.ruflowith atypes/index.d.ts, the router pick can come from it in a follow-up.prompt.submit,tool.check, learning signals and the trust gate. This mod owns the Pane (requestId: ruflo-swarm) and theruflo-swarm-*commands. It registers noprompt.submitand notool.checkhook. It observes PromptHint renders only to learn the viewport, always callsnext(e)and draws nothing there.marketplace.jsonbelong to mods-planner. This PR edits onlyplugins/ruflo-swarm/.claude-plugin/plugin.json(version 0.3.0, auserConfigblock, keywords) andplugins/ruflo-swarm/hooks/hooks.json.Update: 2.1.287 live, and ruOS-hosted agents
Live on 2.1.287 with no env var. After the account switch the rollout flag serves on.
~/.local/bin/claude --plugin-dir plugins/ruflo-swarmloads the module:hooks module ruflo-swarm@inline loaded (worker, environment 1, tier user).ui.open ruflo-swarm ruflo-swarm (unasked, 210 columns): placed).claude -p "/ruflo-swarm-status"prints the real mesh swarm.claude plugin testruns and passes there (38/38).The 2.1.287 "did not load" finding below was account-specific: the rollout flag was off for the earlier account.
ruOS remote hosts (contract agreed in writing with the ruflo-ruos lane, PR #3605). The pane reads three things:
agents[id].config.hostwherekind === 'ruos';.claude-flow/ruos/hosts.json;.claude-flow/ruos/events.jsonl.What it shows:
coder @ruos-desktop-1. Its state comes from the newest run event (running,completed,failed (exit N),stopped).How it treats the files:
error,lastResult) is never read.No button mutates ruOS. The pane never depends on a
$.ruosnoun. It never uses the/ruoscommands or theruos-viewpane id. It only observesmcp__ruos__*tool calls.Seen live on 2.1.287 against a real ruflo swarm, with contract files I wrote by hand: ruflo-ruos was not running here. The tiles showed
coder @ruos-desktop-1(working) andtester @ruos-desktop-2(done), and the hosts section showed the heartbeat age.Fix found live:
$.storebelongs to the plugin, not the folder, so a router pick from another project appeared in a new folder's pane. Persisted pane state is now keyed by working directory, and the router line shows the pick's age. A test covers this.Evidence
Engine-kit tests (
claude plugin test plugins/ruflo-swarm): 38 pass, 0 fail on 2.1.287 (32/32 earlier on 2.1.282). The fixtures are the files ruflo 3.49.0 really wrote in a run; the hive token is replaced by a sentinel that is asserted never to render. Coverage:$.ui.mountaccepted at 24 to 160 columns;$call the mod makes is denied, and it still starts, answers and draws;registerwith a recordingon, because inline test plugins can't import the module;vitest:
npx vitest run --root plugins/ruflo-swarm tests/parse.spec.tsgives 6/6.Other checks:
claude plugin validate --strict plugins/ruflo-swarm: clean on 2.1.282 and 2.1.287.tsc -p plugins/ruflo-swarm: clean, against upstreammods/types/claude-code.d.ts(written by 2.1.277), fetched byscripts/fetch-mod-types.shinto a gitignored.claude/types/.smoke.sh: 11/11.audit-plugin-manifest,audit-plugin-hooks-cross-platformandaudit-plugin-mcp-prefix: ok.smoke-all-plugins: 35/36. The one failure is inruflo-metaharnessstep 17z6, which was already failing and is untouched here.Benchmark (engine kit, 60 iterations each, through
$.ui.render):Render time stays flat because the tile grid is capped (at most 6 rows, about 36 tiles, then "+N more agents"). 100 agents does not draw 100 tiles.
Throttling, so no hook waits on slow work:
Live run (Claude Code 2.1.282,
CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 --plugin-dir plugins/ruflo-swarm, in a real swarm made withnpx @claude-flow/cli@3.49.0 swarm init+agent spawn×3–4 +task create×2 + claims +hive-mind init+ a consensus proposal)hooks module ruflo-swarm@inline loaded (worker, environment 1, tier user); events: session.start,ui.render,ui.close,command.run,turn.start,turn.complete,agent.spawn,tool.call, then$.command.register (ruflo-swarm): /ruflo-swarm-status listed.claude -p "/ruflo-swarm-status"printed the real swarm: the queen, coder busy, tester and reviewer idle,tasks: 1 pending, 1 claimed.★queen ⇄ coder ⇄ tester ⇄ reviewer ⇄ architect ⇄ claude (main),all-to-all: 6 members, 15 links). At session start it opened by itself (ui.open … (unasked, 210 columns): placed)./ruflo-swarm:watchopened the pane (ui.open … (asked)), andcommand.run settled … next() included: the markdown command still ran.ctrl+x tab, Tab and Enter:agent logsand showed its lines;✓ seen on disk, andclaims.jsonreally read"status": "paused";✓ seen on disk: stop agent …, andagents/store.jsonreally readcoder terminated.hooks route --format jsonin a turn set the pane's router line torouter: tester 72% (testing-task) · alt reviewer 62%.Exploresubagent appeared as a tile (viaagent.spawn) and turned✓ done.suggestioncolour while Read ran, then back to idle. This was checked from ANSI captures (38;5;174→38;5;153→38;5;246).What the live run found (engine and ruflo behaviour, not fixed here)
CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1. The brief expected it on by default there. Debug log:installed plugins' hooks modules not loaded: rollout flag (tengu_plugin_hooks_modules) is off, from GrowthBook (the disk cache of an earlier session); built-in plugins load regardless. The kit tests and validate still pass on 2.1.287.agent.spawn. A namedAgentcall ran as an in-process teammate (spawnInProcessTeammate), and$.agent.list()listed it as typeteammate. Teammates are now labelled by theirname.tool.callagentIdmatched neither the spawn result noragent.list(). The pane names such a loopsubagent …<id suffix>rather than guessing which agent it is.hive-mind consensus -a vote … --voter-id <agent>prints[OK] Vote recorded (For: undefined, Against: undefined)and records no vote inhive-mind/state.json. The pane reports it as "exited 0 but the disk does not show it".hooks routereportsmatched: trueat 0.41 for gibberish (zzqx florp). This is adjacent to hooks_route reports its highest confidence (70%) exactly when nothing matched, including on blank and non-English tasks #3567 and is noted in hive-mind consensus vote prints [OK] Vote recorded but writes no vote #3609. The pane shows the score and "below threshold", never a bare match.Not verified
sec-default(no managed settings on this machine). Refusal of every$call is covered in the kit instead.mcp.calltransport: mutations go throughnpx --offline -y @claude-flow/cli@latest(mcp execfor the claims tools), not the MCP server.memory store/memory retrieve), not from a live session withaudit: true.Security
$.process.runwith fixed argv, through one of four fixed prefixes. The default,npx --offline, never touches the network.JSON.stringifyand passed as a single argv element.-is refused.tool.checkhook and noprompt.submithook. No secrets are involved.CI
Root
vitest runcollects*.test.ts. The seven engine-kit test files importclaude-code/testing, which exists only insideclaude plugin test, so they are listed inscripts/ci-test-baseline.txtwith a comment, following the ruflo-x-gateway precedent. The lead authorized this for these files only. The pure readers still run under root vitest throughtests/parse.spec.ts(6/6).Packages
None need a version bump or publish: this is plugin content only, and its version is now 0.3.0. The marketplace entry (if a description or version sync is wanted) belongs to mods-planner.
🤖 Generated with RuFlo