The 14-chapter guide (originally published on LinkedIn) covering what RuFlo is,
how the model / skill / MCP / runtime pieces differ, setup for Claude Code,
Codex, Claude Desktop, ChatGPT and Grok, a first bounded project review, cost,
and what to verify yourself.
Full text preserved: all 14 sections, 6 "Try it" and 7 "Success check"
callouts, prompts and commands verbatim, pinned to ruflo@3.38.23.
Chapter visuals recovered from the published article. The originals are
100-frame animated GIFs at 5-7 MB each -- ~82 MB for the set, which is not
reasonable permanent git weight -- so each chapter carries a static first-frame
still instead (1.1 MB total). The animated versions remain in the linked
article. Chapter order was verified visually against each caption rather than
assumed from DOM or upload order.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_013u4pmL9ZUAXb6usVQgNo67
A two-minute broadcast master produced end to end by AI, in which the creative
models were never allowed to publish it. It earns a place in Ruflo's docs
because it demonstrates the pattern Ruflo is built around -- generation
authority and promotion authority are different things -- applied somewhere
other than a codebase. The run terminates at `awaiting_approval` with
`publication_authorized: false`, the same shape as a witness receipt carrying
`authority: 'none'`.
Walkthrough covers all twelve scenes with a still, narration line, style,
timing and transition for each, plus the governance envelope, the consent model
(four presenter variants inheriting one attestation), and the declared audio
mastering targets.
Numbers were verified against the artifacts rather than copied from the
production notes: all six SHA256SUMS check out, and ffprobe independently
confirms codec, resolution, frame rate, sample rate, channels, duration and
byte size. Two caveats from the source are preserved rather than rounded away
-- the cost is a bounded estimate, not an invoice, and captions are a sidecar,
not burned in.
One rendering defect is documented rather than cropped out: scene 9's headline
clips to "Creative does not mean loos". At 28 characters it is the longest in
the production and every other panel is <=25, pointing at fixed-width
truncation in the overlay renderer.
Assets: 12 scene stills (1280px JPEG) + poster + contact sheet, 3.4 MB total.
The 84 MB master is deliberately not committed -- it is referenced, not vendored.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01Ff2xRKvYrqXJhefvcapfE1
* fix(migrate,mcp): unresolvable remediation command + false-success HTTP server exit
Two independently-reported, freshly-filed defects:
- #2985: `ruflo migrate status`'s ADR-128 removed-agent remediation
(shipped today via #2973/ADR-382 Part B) printed a command that cannot
succeed twice over: `ruflo plugins install <plugin>` requires -n/--name
(not a positional) AND targets the npm-package plugin system, while the
gap detector's own registry check (installed_plugins.json) is for the
Claude Code marketplace system entirely. Fixed to print the correct
`/plugin install <plugin>@ruflo` slash-command flow, with a one-time
"run /plugin marketplace add ruvnet/ruflo first" hint. Also corrects
three adjacent doc-drift items the reporter found while verifying:
README/USERGUIDE's `claude mcp add ruflo` examples (should be the
canonical `claude-flow` key per #2206 — the exact stray-key duplicate
#2612's healing logic exists to clean up), README's blanket "MCP server
registered: No" claim for the plugin path (ruflo-core ships its own
.mcp.json and does register one), and a stale comment in executor.ts
conflating the CLI-track's mcp__claude-flow__* tool refs with the
marketplace plugin binding (mcp__plugin_<plugin>_<server>__*).
- #2984: `ruflo mcp start -t http` printed a full "Status: Running"
success table — PID, health/RPC URLs — then exited 0 within seconds,
never actually leaving the port bound. Root cause: bin/cli.js's
"normal CLI mode" branch (which any explicit non-stdio --transport
always takes) unconditionally exits once the dispatched command's
action() promise resolves (#1552's "one-shot command" assumption).
mcp.ts's `start` action returned immediately after printing the table,
so the freshly-bound http.Server was torn down within milliseconds of
the printed success claim. Fixed by blocking in the foreground after a
successful start (SIGINT/SIGTERM-driven graceful shutdown), matching
daemon.ts's established `--foreground` pattern — `daemonize` isn't
currently wired to fork/detach anywhere in MCPServerManager, so every
successful start via this path is a foreground server regardless of
the -d flag.
Tests: migrate-agent-detection.test.ts updated for the corrected command
string; new mcp-http-foreground-2984.test.ts spawns the built CLI exactly
like the bug report and asserts the port actually accepts connections
after the success table prints (not just that it printed), then confirms
SIGTERM still produces a clean exit. Both verified via git-stash A/B:
fail against the pre-fix code, pass against the fix.
Full suite otherwise unaffected — same 9 pre-existing sandbox-environment
failures (ruvector/wasm bindings, embedder model downloads) reproduced
identically before this change.
Closes#2985. Closes#2984.
Co-Authored-By: RuFlo <ruv@ruv.net>
* revert: drop migrate-agent-detection changes, superseded by PR #2986
An external contributor (pacphi) independently opened #2986 fixing the
same #2985 with the identical installCommand correction plus a real
`migrate fix --agents` restore command — strictly more complete than
this branch's minimal fix. Dropping the overlapping files here to avoid
a duplicate/conflicting PR; reviewed #2986 and recommended it for merge.
Remaining on this branch: the #2984 mcp start -t http fix and the
doc-drift items #2986 explicitly scoped out (README/USERGUIDE mcp-add
key, executor.ts comment).
Co-Authored-By: RuFlo <ruv@ruv.net>
Surgical edits to top 50 lines per industry-trends review (no changes
below "Quick Start"). Three changes:
1. **Definition-first opening.** Replaces "Multi-agent AI harness for
Claude Code and Codex" with the now-standard "Agent = Model + Harness"
mental model, then locks in "Ruflo is the harness" with what's
actually in it. Matches how Databricks, LangChain, Hugging Face
define this layer in 2026.
2. **Two-tier badge clustering.** Top tier (4 for-the-badge badges) =
what first-time visitors actually act on: Try UI, npm install, License,
GitHub Star. Bottom tier (7 flat-square badges) = ecosystem context:
Goal Planner, Live Agents, RuVector, downloads, clones, Claude Code
plugin, Codex plugin. Cuts the wall-of-CTAs problem at the top.
3. **Origin story collapsed.** Moved the "Claude Flow is now Ruflo —
named by rUv..." block from page 1 into a <details> "Background"
block right above the GIF. Visible, but doesn't compete for attention
with the value prop.
Also removed the redundant RuVector badge that appeared on two strips,
and standardized en-dashes in the "New to Ruflo?" callout.
No content removed — every link, image, and structural section below
"Quick Start" is identical. Diff is +22/-21 in one file.
Co-Authored-By: RuFlo <ruv@ruv.net>
Two README updates in both root + ruflo/ mirrors.
(1) metaharness blurbs — rewrite to be understandable
Previous wording — "10 ruflo metaharness * subcommands + 9 MCP
tools — harness readiness scoring, MCP security scanning, threat
modeling, composite audits, genome similarity, one-command drift
detection — plus ruflo eject ..." — leaned on jargon and surface
counts that don't tell a user what value they get.
New wording, in 3 places:
"What You Get" capability row:
"Audit your AI agent setup before you ship. Grade readiness
(1-100), scan tool configs for security issues, snapshot the
whole project to catch regressions over time, and find templates
that match your repo. ruflo eject turns a ruflo project into a
standalone agent toolkit with its own name."
Plugin list (Architecture & Methodology section):
"Grade your agent setup, scan tool configs for security risks,
and track changes over time"
Documentation table:
"How to grade your agent setup, scan tool configs for security,
detect changes between runs, and eject a project into a
standalone agent toolkit. The audit-my-setup doc."
(2) Badge counts — refresh against live data
Live npm queries (last-year window across the 9 main ecosystem
packages — ruflo, claude-flow, @claude-flow/cli + codex + guidance,
agentic-flow, agentdb, ruvector, ruv-swarm) total 8.13M downloads.
The 22.2M+ badge was a stale manual figure with no source-of-truth
computation.
Live GitHub clone-traffic API across the 5 tracked repos
(ruvnet/{ruflo, agentdb, agentic-flow, ruvector, ruv-FANN}) totals
105,742 in the trailing 14d window. The 115k badge came from the
2026-05-19 ledger snapshot (4 weeks stale); the most recent
2026-06-14 snapshot had fetch-failed entries so the ledger wasn't
updated.
Updated:
ecosystem downloads 22.2M+ → 8.1M+
git clones (14d) 115k → 106k
Both linked to the same data/ sources (proof.json + ledger.json).
Co-Authored-By: RuFlo <ruv@ruv.net>
Per user direction: keep the README user-facing, no ADR references in
the metaharness blurbs. Touched only the 4 lines I added in the prior
two commits (capability table row, plugin list row, docs table row,
section row in both root + ruflo/ READMEs).
Pre-existing ADR mentions (ADR-123 graph-intelligence, ADR-033 ruvocal)
are left in place since they predate this work and aren't about
metaharness.
Co-Authored-By: RuFlo <ruv@ruv.net>
Right answer to "is it in the collapsed list?" was: no. Adding it:
* Inserted under "Architecture & Methodology" (sits naturally with
ruflo-adr, ruflo-ddd, ruflo-sparc — all meta-analysis / methodology
tooling)
* Bumped collapsed-list header "All 33 plugins" → "All 34 plugins"
* Bumped capability table "32 native Claude Code plugins" → "33"
(the marketplace count excludes ruflo-core)
* Cross-linked the plugin README + the metaharness-user-guide
Mirrored to both root README.md (relative paths) and ruflo/README.md
(absolute GitHub URLs since the wrapper ships standalone to npm).
Co-Authored-By: RuFlo <ruv@ruv.net>
Added docs/metaharness-user-guide.md — the deep-dive doc for ADR-150's
metaharness integration. ~400 lines covering:
* Quick start + the 10 CLI subcommands with example invocations
* The 9 MCP tools + handler contract
* The 4 ADR-150 architectural constraints (removable / optional /
graceful / CI-gate)
* Common workflows: daily drift check, weekly cron, PR audit gate,
template ranking
* ADR-152 §3.1 similarity math (cosine + categorical + jaccard
weighted blend) + verdict thresholds
* Router integration (ADR-148/149) + parallel-logging promotion gate
* `ruflo eject` safety semantics
* Troubleshooting (the v3.12.0 → 3.12.1 plugin-path issue, degraded
mode, cold-cache slowness, etc.)
* Internals + cross-references (ADRs, tracking issue, upstream
`ruvnet/agent-harness-generator`)
Linked from both README.md and ruflo/README.md in two places:
1. "What You Get" table — new row between Federation and Web UI
2. "Documentation" table — new row between User Guide and Benchmarks
The ruflo wrapper README uses absolute github.com links since the
file is shipped on its own to npm; the root README uses relative paths
since docs/ ships alongside it.
Co-Authored-By: RuFlo <ruv@ruv.net>
Drops two large hero images from the README header:
- RuFlo-agentic-appliance.png (cognitum.one/appliance)
- ruFlo-Summit.jpg (Budapest 2026 summit, #1967)
Badges and the main banner are retained.
Co-Authored-By: RuFlo <ruv@ruv.net>
Reverts the plain-language rewrite from 61b988e68 at the user's request.
Original README content restored from a88b9369b.
Co-Authored-By: RuFlo <ruv@ruv.net>
Replaces the 416-line marketing-heavy README with a 256-line plain-language
walkthrough aimed at a developer evaluating Ruflo for the first time:
- What it actually is (CLI + MCP server on top of Claude Code)
- What problem it solves (forgetting / single-threaded / one-model-fits-all)
- Three things you can use day-to-day
- Core concepts in one sentence each
- "When you should NOT use Ruflo" section
- File layout `init` writes, with reversal instructions
- FAQ on phone-home / data / uninstall
Co-Authored-By: RuFlo <ruv@ruv.net>
Trivial 3-character fix in 3 READMEs by @antonbozko-cell. Intro paragraphs referenced 'npx ruvflo init' instead of 'npx ruflo init' (which is used in the very next section).
Place above the ruFlo Summit badge in the three customer-facing READMEs
(repo root, ruflo wrapper package, @claude-flow/cli package). Same
hosting pattern as the summit asset.
Co-Authored-By: RuFlo <ruflo-bot@users.noreply.github.com>
Two plugin-list updates to root + ruflo READMEs:
1. Added `ruflo-graph-intelligence` (ADR-123 — sublinear graph
reasoning, PageRank, delta updates, complexity-aware execution)
under the Intelligence & Learning category. Count: 32 → 33.
2. Reshuffled the Path A featured install snippet to highlight the
capabilities users most often ask about today:
- Kept: `ruflo-core`, `ruflo-swarm`
- Added: `ruflo-rag-memory` (smart retrieval substrate built
on top of the ADR-125 memory consolidation we just shipped)
and `ruflo-neural-trader` (the headline domain-specific
plugin per ADR-117)
- Removed: `ruflo-autopilot`, `ruflo-federation` (still in the
33-list — just less of a first-touch story than memory +
neural-trader for the typical drop-in user)
Co-Authored-By: RuFlo <ruv@ruv.net>
Removed three badges that are now redundant after the ecosystem-
downloads badge landed in PR #2065:
- `npm downloads (ruflo)`
- `npm version (claude-flow)`
- `npm downloads (claude-flow)`
Keeping only `npm version (ruflo)` (the install-target users actually
type) + `ecosystem downloads 22.2M+` + `git clones 14d 115k` for the
ruflo-published-network signal. The `claude-flow` legacy umbrella is
still installable but doesn't need separate badge real estate.
Co-Authored-By: RuFlo <ruv@ruv.net>
Three small fixes:
- memory README: switched the two new badges' hrefs from relative
(`../../../data/clone-data.proof.json`) to absolute GitHub URLs
so they render correctly on the npm.js package page (npm strips
relative links in README markdown)
- root README: added the same `ecosystem downloads 22.2M+` and
`git clones 14d 115k` badges alongside the existing per-package
ruflo/claude-flow download badges
- ruflo/README.md: same addition, since it's the umbrella that
users see first via `npx ruflo@latest`
All three badge URLs point to `data/clone-data.{proof,ledger}.json`
which the 13-day cron in .github/workflows/clone-tracker.yml will
keep updating.
Co-Authored-By: RuFlo <ruv@ruv.net>
This commit closes the chain started in ca0a6fa5c (standalone use recipes
landed in repo but never reached npm because no version bumps shipped).
## Changes
### Module version bumps (alpha.43)
- @claude-flow/memory → 3.0.0-alpha.17 (republish standalone use README)
- @claude-flow/embeddings → 3.0.0-alpha.18 (republish standalone use README)
- @claude-flow/security → 3.0.0-alpha.8 (republish standalone use README)
- @claude-flow/neural → 3.0.0-alpha.9 (pin @ruvector/sona to 0.1.5,
republish standalone use README)
- @claude-flow/cli → 3.7.0-alpha.43 (repin to new modules above)
- claude-flow → 3.7.0-alpha.43 (umbrella)
- ruflo → 3.7.0-alpha.43 (umbrella)
### Umbrella bump (alpha.44) — npm download badges
- @claude-flow/cli → 3.7.0-alpha.44
- claude-flow → 3.7.0-alpha.44
- ruflo → 3.7.0-alpha.44
### Neural sona pin (root fix for #2022)
@claude-flow/neural alpha.8 had `"@ruvector/sona": "latest"`, and
@ruvector/sona@0.1.6 (current latest on npm) shipped as an empty publish
— README + package.json only, no index.js or native bins. So every
fresh `npm install @claude-flow/neural@alpha.8` broke at import time
with `Cannot find package '.../node_modules/@ruvector/sona/index.js'`.
Alpha.9 pins to the exact known-good 0.1.5 (which has index.js + 7
platform-specific .node bins). Will revert to a range once
@ruvector/sona@0.1.7+ ships with content (tracked in #2022).
### npm download badges across every published package README
Added `shields.io/npm/dm/<pkg>` (monthly downloads) badges to:
- claims, cli-core, plugins (had no npm badges at all)
- plugin-agent-federation, plugin-iot-cognitum (no npm badges)
- browser, codex, mcp (had version badge, missing downloads)
- umbrella root README (now badges both `ruflo` + `claude-flow`)
The ruflo/README.md and v3/@claude-flow/cli/README.md auto-copy from
root via prepublishOnly, so they inherit the new badges on publish.
Packages that already carried downloads badges (aidefence, deployment,
embeddings, guidance, hooks, integration, memory, neural, performance,
providers, security, shared, swarm, testing) are unchanged.
## Live functional check on the published artifacts
- Fresh `npm install @claude-flow/neural@3.0.0-alpha.9`:
sona resolves to 0.1.5; `import('@claude-flow/neural')` returns
A2CAlgorithm, BalancedMode, BaseModeImplementation, BatchMode,
CuriosityModule, ... (was broken before alpha.9).
- Standalone use sections from ca0a6fa5c now visible on npmjs.com for
memory@alpha.17, embeddings@alpha.18, security@alpha.8, neural@alpha.9.
- npm dm/ badges resolve and render the current weekly download count.
Co-Authored-By: RuFlo <ruv@ruv.net>
The CLI Install section listed `curl … | bash` first without
flagging that it's POSIX-only. Native Windows users (cmd /
PowerShell, no Git-Bash) get `'bash' is not recognized` and bounce
off the docs.
Split the section into two blocks so each platform has a path it
can actually copy-paste:
• macOS / Linux / WSL / Git-Bash: curl | bash (unchanged)
• All platforms (incl. native Windows): npx ruflo@latest init
wizard (same end result, no bash needed)
Adds a one-line callout explaining the choice and pointing folks
who copy the curl line on Windows toward the npx alternative.
Co-authored-by: Reuven <cohen@ruv-mac-mini.local>
Adds a linked banner above `# Ruflo` pointing at the newly-created
Summit announcement (#1967). The summit asset doc
(v3/docs/assets/ruflo_summit_budapest.md) is also reformatted into
table-led sections with light section emoji for skim-ability — same
content the issue body uses (image URL rewritten to the GitHub raw
path so it renders on the issue).
Co-Authored-By: RuFlo <ruv@ruv.net>
Every plugin title in the collapsed plugins table is now a link to
plugins/<name>/README.md. Verified all 32 targets exist. Also refreshed the
ruflo-agent row (renamed from ruflo-wasm, now spans both runtimes): "Run
agents — local WASM sandbox (rvagent) + Anthropic Claude Managed Agents (cloud)".
Co-authored-by: Reuven <cohen@ruv-mac-mini.local>
* docs(adr): ADR-115 — Claude Managed Agents as a cloud backend for rvagent (explored + validated)
Records the proposal to add `runtime: "managed"` to ruflo's rvagent
interface (ruflo-wasm / @ruvector/rvagent-wasm / wasm_agent_* MCP tools),
backed by Anthropic's Claude Managed Agents API (beta), as a second backend
alongside the local WASM sandbox — mirroring the ConsensusTransport
LocalTransport|FederationTransport pattern (ADR-095 G2 / ADR-104). The
agent/environment/session/events model maps ~1:1 to wasm_agent_create/
prompt/files/terminate + the gallery.
Validated live against api.anthropic.com/v1 with the org's Anthropic key
(GCP claude-flow/anthropic-api-key): created an agent + environment + session,
sent a bash task, observed the full event log (status_running → user.message
→ tool_use(bash) → tool_result → agent.message("Done.") → status_idle,
stop_reason end_turn), cleaned up. ~7s for a trivial Haiku task.
High-value combos noted: ruflo's own MCP server as a Managed Agent tool
source (cloud-hosted ruflo, zero local infra); Managed Agent session as a
third task_assign executor (closes#1916 items 3/4); long-running/async work;
skills + define-outcomes parity. Constraints: opt-in/off-by-default, needs
ANTHROPIC_API_KEY + beta access, plugin-level @anthropic-ai/sdk dep,
cost-tracking + orphaned-session GC, beta churn, branding.
Status: proposed. Registered in AgentDB (adr-ADR-115) + adr-patterns.
Co-Authored-By: RuFlo <ruv@ruv.net>
* refactor(plugins): rename ruflo-wasm → ruflo-agent (agent-runtimes home; ADR-115 prep)
`ruflo-wasm` only covered local WASM-sandboxed agents (rvagent —
@ruvector/rvagent-wasm, the wasm_agent_*/wasm_gallery_* MCP tools). It's
becoming the home for *all* ruflo agent runtimes: the existing local-WASM
backend plus Anthropic's Claude Managed Agents as a cloud backend (ADR-115),
behind one interface — same local-vs-cloud pattern as ConsensusTransport.
- git mv plugins/ruflo-wasm → plugins/ruflo-agent
- plugin.json: name ruflo-wasm → ruflo-agent; description/keywords now span
both runtimes (rvagent/wasm + managed-agents/anthropic/cloud)
- .claude-plugin/marketplace.json entry renamed
- README / plugins/README / docs/index / cli README / ruflo README /
discover-plugins SKILL / verification/inventory.json / ADR-115 — refs updated
- wasm_agent_* / wasm_gallery_* MCP tool names are unchanged (they stay; the
plugin is just renamed). managed_agent_* tools land next per ADR-115.
(Left `ruflo-wasm-mcp` in ruvocal/workerClient.ts alone — unrelated worker MCP name.)
Co-Authored-By: RuFlo <ruv@ruv.net>
* feat(cli): managed_agent_* MCP tools — Claude Managed Agents cloud runtime (ADR-115)
Six new MCP tools wrapping Anthropic's Claude Managed Agents REST API (beta,
`anthropic-beta: managed-agents-2026-04-01`) via plain fetch — no new SDK dep:
managed_agent_create agents.create + environments.create + sessions.create ↔ wasm_agent_create (CLOUD counterpart)
managed_agent_prompt events.send(user.message) + poll events until idle ↔ wasm_agent_prompt
managed_agent_status GET /v1/sessions/{id}
managed_agent_events GET /v1/sessions/{id}/events (full transcript) ↔ wasm_agent_files
managed_agent_list GET /v1/sessions
managed_agent_terminate DELETE /v1/sessions/{id} (+ optionally the env) ↔ wasm_agent_terminate
Needs ANTHROPIC_API_KEY (or CLAUDE_API_KEY); every tool degrades gracefully
with a structured "use wasm_agent_* for a local no-key runtime" error when the
key is absent — the CLI/MCP server stays up. ADR-112-compliant descriptions
(each says when wasm_agent_* / native is the right call instead). Registered
in mcp-client.ts. Lives conceptually in the `ruflo-agent` plugin (the
`ruflo-wasm`→`ruflo-agent` rename in the prior commit).
Validated live against api.anthropic.com with the org's Anthropic key (GCP
claude-flow/anthropic-api-key) via `ruflo mcp exec -t managed_agent_*`:
create (haiku) → prompt ("echo … > /tmp/r.txt && cat") → got finished:true,
status:idle, stopReason:end_turn, assistantText:"Done.", toolUses:[bash],
13 events → terminate (sessionDeleted+environmentDeleted). No-key path returns
the graceful error. audit-cli-mcp-tools 311 registered/0 dangling;
audit-tool-descriptions 0 no-guidance; cli build clean.
Co-Authored-By: RuFlo <ruv@ruv.net>
* feat(ruflo-agent): wire the managed-agent (cloud) runtime into the plugin + CI smoke
- skills/managed-agent/SKILL.md — drives the cloud runtime: managed_agent_create
/ prompt / status / events / list / terminate, with the WASM-vs-managed
decision table, the ANTHROPIC_API_KEY prereq + graceful fallback, the
mcpServers-reachability caveat, and the cost/cleanup discipline (a cloud
session bills until terminated). Explicit allowed-tools (no wildcard).
- commands/managed-agent.md — quick "list cloud sessions / status / transcript
/ clean up" command (the cloud sibling of /wasm).
- plugin.json — kept rvagent-wasm + ruvllm-wasm keywords (still built on those);
added managed-agents/anthropic/cloud.
- smoke.sh: step 1 now also asserts name=ruflo-agent + the managed-agents
keywords; step 10 expects ADR-0001 status `Accepted` (was stale `Proposed`,
pre-existing red); new step 12 — managed-agent skill+command exist + all 6
managed_agent_* tools referenced + the skill keeps an explicit allowed-tools
list + offers the wasm_agent_create fallback. 12 passed / 0 failed.
- .github/workflows/ruflo-agent-smoke.yml — new CI job running smoke.sh on any
plugins/ruflo-agent/** change (the plugin had no CI gate before).
Validated: `claude --plugin-dir plugins/ruflo-agent` loads the plugin and lists
its skills (managed-agent, wasm-agent, wasm-gallery); plugin.json + all skill
frontmatters parse; smoke.sh 12/0.
Co-Authored-By: RuFlo <ruv@ruv.net>
* docs(adr): ADR-115 → Accepted + Implementation section (managed_agent_* landed)
Status Proposed → Accepted. Added an Implementation (2026-05-12) section:
the ruflo-wasm→ruflo-agent rename, the 6 managed_agent_* MCP tools (plain
fetch, no new SDK dep, graceful no-key degradation, ADR-112 descriptions),
the managed-agent skill+command, the smoke.sh/ruflo-agent-smoke.yml CI guard,
the live e2e validation (create→prompt→terminate against api.anthropic.com
with the GCP anthropic-api-key) and `claude --plugin-dir` validation, and the
called-out follow-ups (runtime:"managed" unification, deployed HTTP ruflo MCP
server for the cloud-hosted-ruflo combo, task_assign/federation dispatch to a
Managed Agent session, cost-tracking + orphaned-session GC, multiagent/outcomes).
Tracking issue: #1931.
Co-Authored-By: RuFlo <ruv@ruv.net>
* test(cli): no-network guard for managed_agent_* tools (ADR-115)
__tests__/managed-agent-tools.test.ts (10 cases): the 6 managed_agent_*
tools are registered; each has an object inputSchema + a ≥80-char,
ADR-112-ish, unique description that names the wasm_agent_*/native fallback;
and — crucially — every handler degrades gracefully with no ANTHROPIC_API_KEY/
CLAUDE_API_KEY: no throw, structured error mentioning the key + the
wasm_agent_create fallback, no network call (so the CLI/MCP server stays up
offline). Plus managed_agent_prompt's missing-sessionId/message validation.
Co-Authored-By: RuFlo <ruv@ruv.net>
* docs(adr): ADR-115 — add the third runtime (Claude Agent SDK) to the picture
ADR-115 adds the *cloud* runtime (Managed Agents); add a section noting the
natural *third* runtime — the Claude Agent SDK (@anthropic-ai/claude-agent-sdk,
"Claude Code as a library": query()/ClaudeSDKClient, hooks, subagents, MCP,
permission modes, sessions, loads .claude/ config + plugins; runs in-process,
full host trust). ruflo-agent → three runtimes behind one model:
WASM (rvagent) local sandbox, no host fs/net untrusted/portable/offline
SDK (future) your process, your filesystem a real Claude agent loop on the local repo — the in-process `claude -p`
Managed Anthropic cloud container long-running/async, no local infra
The SDK runtime's killer combo: `mcpServers: { ruflo: { command: "npx", args: ["ruflo","mcp","start"] } }` — a *local* stdio MCP server, so the agent gets ruflo's 314 tools with zero deployment (vs Managed Agents needing a reachable URL). Anthropic positions SDK→Managed as prototype→production. Costs: heavy dep (bundles a Claude Code binary), full host trust (no sandbox — least isolated), needs an API key. Implementing it = a follow-up, likely ADR-116 (tracked in #1931).
Co-Authored-By: RuFlo <ruv@ruv.net>
---------
Co-authored-by: Reuven <cohen@ruv-mac-mini.local>
* feat(federation): ADR-111 Phases 4-6 — firewall projection + witness chain + MCP tools
Phases 4-6 of ADR-111. Phase 7 (cross-OS WG mesh bringup) is operator-mediated
and lives outside the package — staged but not auto-executed per CLAUDE.md
destructive-actions guidance.
## Phase 4 — Trust-graded firewall projection
`domain/services/wg-firewall-service.ts` (new)
- Two backends: linux-nftables (atomic `nft -f`, table `inet ruflo_fed`,
default DROP, interface-scoped) and darwin-pf (anchor-scoped pfctl).
- Projects WG_NETWORK_GATES → concrete rule lines per peer.
- UNTRUSTED excluded (no mesh IP), peers without wgMeshIP skipped.
- Defense-in-depth: regex-validated rule args (no shell metachars in
pubkey/IP/iface).
- Returns rule file content + the load command the operator runs —
service never shells out.
- 16 unit tests.
## Phase 5 — Witness attestation chain
`domain/services/wg-witness-service.ts` (new)
- Append-only chain of Ed25519-signed entries. Each entry hash-links to
the previous (`prevHash` = sha256 of previous canonical encoding).
- Canonical encoding: sorted keys, no whitespace, omitted-on-undefined.
- `verifyWitnessChain` walks the chain and reports `failedAt` + reason
(`broken-chain-link` | `invalid-signature-or-hash`).
- `attestWgCommand` adapter maps WgCommand.verb → event type.
- 11 unit tests (canonicalization stability, deterministic hash,
chain link integrity, signature-tamper detection).
## Phase 6 — Operator MCP tools
`mcp-tools.ts` (extended)
- `federation_wg_status` — per-peer mesh state (trust, meshIP, AllowedIPs,
suspended/evicted flags) via `WgMeshService.summarize()`.
- `federation_wg_attest` — operator-signed witness chain entry. v1 stub
returns wire-up instructions until plugin.ts integrates WgWitnessService.
- `federation_wg_keyrotate` — emits a new X25519 keypair (public key only
via MCP; private key write is operator-mediated) + the upgrade checklist.
Requires `confirm: true` (destructive — drops existing tunnels until peers
fetch updated manifest).
- `createMcpTools` gets optional `getWgMesh` getter (additive, no break).
- `FederationCoordinator.listPeers()` added as public discovery accessor.
## Tests
- 543/543 pre-existing + new tests pass (513 + 27 phase 4/5 + 3 plugin
test count assertions for the new MCP tools).
## Not in this PR (Phase 7 — operator-mediated)
- Real WG mesh bringup on mac↔ruvultra over tailscale: stage configs,
operator-approved `wg-quick up`, peer-add via federation manifests,
SUSPEND → confirm L3 isolation → reactivate restores, witness-verify
the change log. Tracked alongside #1879.
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(federation): security — validate peer wg fields before splice (ADR-111)
Manual security audit of ADR-111 Phases 1-6 found a real vulnerability in
Phase 2's WgMeshService that the existing tests did not catch.
## The bug
`WgMeshService.buildInterfaceConfig()` and `summarize()` spliced
`peer.metadata.wgPublicKey` / `wgMeshIP` / `wgEndpoint` into the wg-quick
config string *without validation*. The ADR's threat model explicitly
includes "compromised federation peer with valid WG key" — that peer
signs their own manifest, so the Ed25519 signature only proves origin,
not content safety.
Concrete attack: a compromised peer publishes
wgEndpoint = "host:51820\n[Peer]\nPublicKey = attacker\nEndpoint = ..."
and the operator's wg-quick config ends up with an extra [Peer] block.
Cross-peer lateral movement, bypassing the per-peer trust gates.
Phase 4 (WgFirewallService) already validated meshIP via
`assertSafeRuleArg`. Phase 2 didn't — fixed here.
## The fix
New `readSafePeerWgFields(peer)` returns the validated triple or null.
Three regexes:
- wgPublicKey: ^[A-Za-z0-9+/]{43}=$ (base64 X25519, 32 raw bytes)
- wgMeshIP: ^d.d.d.d/32$ + octet range check
- wgEndpoint: ^(hostname|ipv4|[ipv6]):port$ + port range check
Mismatched fields → peer is skipped entirely (no [Peer] block, no
AllowedIPs, summarize() returns empty strings). Silent skip beats partial
write because partial writes can leak attacker content into operator
review while looking syntactically valid.
## Tests
7 new adversarial cases under `WgMeshService — readSafePeerWgFields
(compromised-peer defense)`:
- newline injection in wgEndpoint
- wrong-length / non-base64 wgPublicKey
- out-of-range octet in wgMeshIP
- missing /32 CIDR
- port out of range
- IPv6 bracket positive case
- summarize() also sanitizes (regression: it used the same unsafe path)
Full suite: 550/550 pass.
Co-Authored-By: RuFlo <ruv@ruv.net>
* feat(federation): ADR-111 Phase 7 staging helper
Generates WG keypair + wg-quick config + per-OS firewall rules on the
current host, naming a single peer. Outputs go to /tmp/adr-111-stage.
Nothing loaded into the kernel — wg-quick up / nft -f / pfctl -f are
operator-mediated.
Usage:
node scripts/phase7-stage.mjs <localId> <peerId> <peerPk> <peerIP> <peerEndpoint>
Workflow:
1. Run on host A with placeholder peer values → emit host-A pubkey
2. Run on host B with placeholder peer values → emit host-B pubkey
3. Re-run on both with the OTHER host's pubkey → final config files
4. Operator reviews + activates per the printed checklist
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(federation): ADR-111 phase7-stage idempotent — reuse key file
Without persistence, every run generates a fresh keypair, which breaks
the cross-feed workflow (run on host A → capture pubkey → run on host B
with A's pubkey → A regenerates a different key on its next run).
Now reads /tmp/adr-111-stage/wg-key-<nodeId>.json if present, falls back
to fresh generation if not. Operator can delete the file to force
rotation.
Co-Authored-By: RuFlo <ruv@ruv.net>
* docs(federation): user guide + Phase 7 bringup procedure + README link
Adds:
- docs/federation/README.md — full user guide (when to use, MCP tools,
trust levels, breaker, ADR-111 mesh layer, claude -p headless usage)
- docs/federation/phase7-mesh-bringup.md — step-by-step cross-OS WG
mesh activation procedure with operator review checklist
- README.md — link to docs/federation/ from the existing Federation
section
Co-Authored-By: RuFlo <ruv@ruv.net>
---------
Co-authored-by: Reuven <cohen@ruv-mac-mini.local>
Three coordinated badge updates to the cluster between the project-meta
and the H1:
- new "Codex Plugin" badge linking to npmjs.com/package/@claude-flow/codex,
OpenAI brand purple #412991. Shields.io silently dropped `logo=openai`
after simple-icons removed it for trademark reasons, so the OpenAI
spirograph mark is embedded directly via `logo=data:image/svg+xml;base64,…`
(verified shields.io renders an <image> element with the logo).
- new "RuVector Agentic DB" badge linking to github.com/ruvnet/ruvector,
cyan #06b6d4 with the graphql logo (graph-themed mark for a graph-RAG
database).
- "Claude Code" badge: green → Anthropic brand orange #D97757, plus
`logoColor=white` so the anthropic mark stays legible on the warmer
background. Brings the badge in line with anthropic.com's identity
instead of using a generic Bootstrap green.
The cluster now reads top-to-bottom as: project meta (stars, license)
→ AI platform plugins (Claude Code, Codex) → underlying graph DB tech
(RuVector). Three semantic groups, clear hierarchy, no emoji noise in
the visible badge text.
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(init): #1744 honest defaults — --no-global, --minimal hooks gate, README install paths (v3.6.28)
Three fixes from @ruvnet's install-study (issue #1744) plus three
already-on-disk runtime honesty fixes:
#1744 changes (this commit):
1. **--no-global flag** for `init` (and codex/dual variants) — opt out
of the user-global ~/.claude/CLAUDE.md "Ruflo Integration" pointer
block append. Default behavior unchanged (block still appended once,
idempotent — but now opt-out is documented). #1744#2.
2. **--minimal hooks gate** in settings-generator. Before this commit,
`--minimal` wrote .claude/settings.json with 4 hook decls pointing at
.claude/helpers/hook-handler.cjs but didn't install the helpers
directory — so every hook fired and silently failed. Now the hooks
block only emits when components.helpers is true. Minimal stays
minimal AND functional. #1744#3.
3. **README Quick Start labeling** — added a head-to-head table
distinguishing Plugin install (lite, slash commands only, no MCP)
from CLI install (full Ruflo loop, MCP registered, hooks installed).
The previous README marketed both equally as "Recommended" which
surprised users who chose plugin install and found the MCP tools
missing. #1744#1.
Already-on-disk runtime honesty fixes (bundled in this release):
4. v3/@claude-flow/cli/src/commands/performance.ts — drop unverified
"Flash Attention 2.49x speedup" optimize/bottleneck recommendations.
Re-tagged as "in progress, currently JS reference impl".
5. v3/@claude-flow/cli/src/mcp-tools/neural-tools.ts — drop the silent
Tier 4 mock-fallback in embedding resolution. If Tier 1 (agentic-flow)
and Tier 3 (ONNX) both failed to import, leave realEmbeddings null
and let the explicit hash-fallback path run with a clear
_embeddingNote in stats. Silently substituting mock embeddings hid a
missing production dependency from callers.
6. v3/@claude-flow/embeddings/src/embedding-service.ts — throw on
unknown provider / no-real-provider-available paths instead of
silently substituting MockEmbeddingService. Mock embeddings are
tests-only; production code that lands on the mock path
accidentally is a bug, and silent fallback hid it.
All TypeScript clean (tsc --noEmit). Build verified — all 3 fixes
present in dist/.
Closes#1744 (3 of 5 papercuts; remaining 2 are doc-only, addressed
in PR #1751).
Co-Authored-By: RuFlo <ruv@ruv.net>
* chore(release): bump to 3.6.28 — published to npm
@claude-flow/cli@3.6.28 → tags: latest, alpha, v3alpha
claude-flow@3.6.28 → tags: latest, alpha, v3alpha
ruflo@3.6.28 → tags: latest, alpha, v3alpha
Includes: #1744 (--no-global, --minimal hooks gate, README install
labeling) + runtime honesty fixes (drop unverified Flash Attention
claim, drop silent mock embedding fallback, throw on missing real
embedding provider).
Co-Authored-By: RuFlo <ruv@ruv.net>
* chore(release): regen verification witness for 3.6.28
totalFixes: 27 → 55 (adds CAP-MCP capability inventory: 300 tools
across 28 source files)
releases: 3.6.24 → 3.6.28 (current published)
manifestHash + signature regenerated from current git commit
Includes the #1744 + runtime-honesty fixes shipped in 3.6.28.
Run `node scripts/regenerate-witness.mjs` after each release.
Co-Authored-By: RuFlo <ruv@ruv.net>
---------
Co-authored-by: Reuven <cohen@ruv-mac-mini.local>
* docs: fix init/MCP drift surfaced in #1744 install study
Two papercuts from @ruvnet's methodical install study (issue #1744):
1. README + USERGUIDE both show `npx ruflo@latest init --wizard` in
five places. Per the source (`v3/@claude-flow/cli/src/commands/init.ts:445-447`),
`wizard` is registered as a subcommand: `subcommands: [wizardCommand, ...]`.
The flag form may parse but the canonical form (and what `init --help`
surfaces) is `init wizard`. Updated all 5 occurrences.
2. README's MCP add command:
claude mcp add ruflo -- npx -y @claude-flow/cli@latest
uses the legacy package name. USERGUIDE.md and `ruflo mcp --help` use
the canonical:
claude mcp add ruflo -- npx ruflo@latest mcp start
Both still work, but having two different commands across docs is
the kind of "wait, what?" papercut #1744 logged. Updated README.
No CLI behavior changes. Pure doc consistency.
Closes part of #1744.
Co-Authored-By: RuFlo <ruv@ruv.net>
* docs(USERGUIDE): fix one straggler 'init --wizard' in command-mapping table
Companion to the previous commit on this branch. Caught the row at
USERGUIDE.md:7413 that the regex replace_all missed because the table
row had a different surrounding pattern.
Co-Authored-By: RuFlo <ruv@ruv.net>
---------
Co-authored-by: Reuven <cohen@ruv-mac-mini.local>
Three docs for three audiences instead of two:
- Status — what currently works (capability counts, test baselines,
recent fixes, what's next)
- User Guide — daily reference (deep, every-flag-documented)
- Verification — cryptographic witness, run `ruflo verify`
docs/STATUS.md (new):
Overview + Usage + Status in one document. Pulls live numbers
(300 MCP / 49 CLI / 32 plugins / 43 agents from inventory script
+ 1933/1933 vitest baseline + ADR-093/095/096/097 status).
Acts as the "is it ready?" doc — complements USERGUIDE.md
(the "how do I?" doc) and verification.md (the "trust but
verify" doc).
README.md:
Documentation section restructured to a 3-row table with
audience-keyed descriptions. User Guide section index kept
intact. No other content churn — the pitch + install +
feature sections are unchanged.
Closes the user request for a separate overview/usage/status doc
plus a README cross-link to verification.md.
- Commit v3/docs/assets/goal.png (1280×582, 163 KB) referenced by
the Goal Planner UI section in README; was overlooked in PR #1693
causing the banner to break on github.com.
- Add two badge links at top of README:
- 🎯 Goal Planner → https://goal.ruv.io/
- 🤖 Live Agents → https://goal.ruv.io/agents
Co-Authored-By: RuFlo <ruv@ruv.net>
* feat(goal_ui): import goap-ui-main (GOAP research UI)
Unzipped from v3/goal_ui/goap-ui-main.zip — a Vite + React + Supabase
research planning UI. 354 files including:
- React UI (src/, components.json, tailwind, vite config)
- Supabase Edge Functions (generate-action-items, generate-research-
goal, optimize-research-config, research-api, research-step)
- Widget build pipeline (build-widget.sh)
- Deployment notes (DEPLOYMENT*.md, netlify.toml)
.env is intentionally force-committed (overrides root .gitignore
entries on lines 61 + 161). It contains only Vite's VITE_* prefixed
client-public Supabase config:
VITE_SUPABASE_PROJECT_ID
VITE_SUPABASE_PUBLISHABLE_KEY ← Supabase publishable/anon key
VITE_SUPABASE_URL
These values ship in every Vite client bundle by design — RLS on the
Supabase project is the actual access gate, not key secrecy. No
sk-*, JWT, or service_role keys in the file (verified with secret
heuristic regex).
Co-Authored-By: RuFlo <ruv@ruv.net>
* refactor(goal_ui): flatten goap-ui-main/ into goal_ui/
The previous import nested everything under v3/goal_ui/goap-ui-main/
— two folders for one project. Move all 354 files up one level so
the layout reads:
v3/goal_ui/
.env
package.json
src/
supabase/
...
instead of the redundant v3/goal_ui/goap-ui-main/...
Build output dist/ removed (it was untracked and shouldn't ship —
generated by `npm run build`).
Co-Authored-By: RuFlo <ruv@ruv.net>
* feat(goal_ui): rebrand to RuFlo Research, clean up scaffolding, drop Lovable favicon
- Rebrand v3/goal_ui as RuFlo Research (https://goal.ruv.io/)
- package.json: name → @ruflo/research, drop lovable-tagger
- vite.config.ts: remove componentTagger / lovable-tagger import
- index.html: SEO/OG/Twitter meta retargeted to goal.ruv.io
- src/widget.tsx + Demo.tsx + WidgetCustomizer.tsx + widget-embed.html:
window.GOAPWidget* → window.RufloResearchWidget*
container id → ruflo-research-widget-container
- Replace Lovable purple-gradient favicon.ico (PNG) with RuFlo SVG mark
- Drop unreferenced placeholder.svg boilerplate
- Move docs into v3/goal_ui/docs/ (DEPLOYMENT, WIDGET-INTEGRATION, WIDGET_SETUP)
- Delete out-of-scope plans/agentic-flow/, duplicate WIDGET-INTEGRATION.md,
bun.lockb (npm-only project), TEMPLATE_README, SETUP_GUIDE, DEPLOYMENT-FIX,
nested CLAUDE.md, and stale .claude/.github duplicates from flatten residue
- Rewrite v3/goal_ui/README.md as RuFlo Research overview
- Update root README "What You Get": add RuFlo Research row pointing at goal.ruv.io
- Drop SEO keyword spam lines from Web UI and Goal Planner sections
Algorithm references to GOAP (goapPlanner.ts, GOAPPlanner, parseGoal) preserved
— GOAP is the algorithm name, not the brand.
Co-Authored-By: RuFlo <ruv@ruv.net>
---------
Co-authored-by: Reuven <cohen@ruv-mac-mini.local>
Redundant with the main ruVocal.png banner above the section, and
the welcome screenshot was hidden behind a <details> toggle most
readers don't open. The action-shot tells the story better.
Asset file v3/docs/assets/ruVocal-welcome.png stays on disk for
potential reuse.
Co-Authored-By: RuFlo <ruv@ruv.net>
Was linking to a non-existent github.com/ruvnet/ruvllm repo. Actual
location is https://github.com/ruvnet/RuVector/tree/main/examples/ruvLLM
— ruvLLM lives as an example/subproject within the RuVector repo.
Updated in both README.md (Web UI emoji table) and
RufloHelpModal.svelte (under-the-hood callout).
Co-Authored-By: RuFlo <ruv@ruv.net>
The MCP (n) pill in the chat input already opens a server manager
that supports adding any MCP endpoint (HTTP, SSE, stdio). README
didn't mention this — significant gap given that "MCP" is in the
section name.
New row sits between the built-in tool count and the parallel-call
capability, where "but can I add my own?" naturally lands.
Co-Authored-By: RuFlo <ruv@ruv.net>
Was treating the three URLs (flo.ruv.io / ruflo.ruv.io /
ruvocal.ruv.io) as if they were the product. They're just my
hosted demo — RuFlo's Web UI is meant to be self-hosted.
- Drop the "Three URLs, one app" row entirely (was a deployment
detail, not a user-facing feature).
- Replace with 🏠 "Self-hostable" row that points readers at the
Dockerfile and cloudbuild.yaml.
- Heading reads "Web UI (Beta) — self-hostable, hosted demo at
flo.ruv.io" instead of listing the three aliases.
- Closing call-to-action splits into "Try the hosted demo" and
"Run your own" with paths to the source + cloudbuild.
Co-Authored-By: RuFlo <ruv@ruv.net>
Was overselling the curated cloud lineup as "the" supported set.
Reality: any OpenAI-compatible endpoint works (vLLM, Ollama, LM
Studio, Together, Groq, self-hosted). Plus native ruvLLM — RuFlo's
self-learning local model layer with MicroLoRA + SONA — for
fully-offline / privacy-sensitive workflows.
README:
- Reframe the 🧠 row as "Any model, local or remote" — keep the 6
curated cloud models as defaults, list the supported runtimes
underneath.
- New 🦾 row dedicated to ruvLLM with link to ruvnet/ruvllm.
RufloHelpModal.svelte:
- Add a green callout below the Models table noting OpenAI-compat
support for vLLM/Ollama/LM Studio/Together/Groq/self-hosted, with
a ruvLLM blurb and link.
- Adjusts the existing "All models route through OpenRouter" note
to "Cloud models route through OpenRouter".
Co-Authored-By: RuFlo <ruv@ruv.net>
README:
- Web UI section now lives directly after the "What You Get"
capability table (was wedged between MCP Server install and What
You Get). Better narrative flow: install paths → what RuFlo gives
you → live demo to try it.
- Bullets replaced with a 7-row emoji table (🧠 / 🛠️ / ⚡ / 💾 / 📘 /
🌍 / 🚀) using friendly language: "Tools run in parallel" instead
of "fan out tool_calls"; "Memory that sticks" instead of
"persistent storage via AgentDB + HNSW".
- All three domain aliases listed in the heading (flo.ruv.io,
ruflo.ruv.io, ruvocal.ruv.io). Welcome screenshot moved into a
collapsed <details> block.
RufloHelpModal.svelte:
- Added "Tool-call cards — what the icons mean" legend (✓ Called
tool, Step N — M tools completed, Retry, AUTO green/gray).
- Added "Under the hood" 8-row stack table (SvelteKit, OpenRouter,
ruflo backend, ruvector backend, AgentDB+HNSW+ONNX, rvagent-wasm,
embedded MongoDB, Cloud Run).
- Added "Domain aliases" section listing all 4 URLs (3 custom + raw
Cloud Run) with notes on Cloudflare DNS and managed cert.
- Added "Keyboard shortcuts" mini-table (Enter, Shift+Enter, Esc,
?worker=1 flag).
- Two extra example prompts added (search+web_research parallel,
diff analysis on a real PR).
Co-Authored-By: RuFlo <ruv@ruv.net>
- Add ruVocal-welcome.png as the primary section banner (520px,
centered, clickable → flo.ruv.io). Smaller and more brand-focused
than ruVocal.png — better fit for GitHub's social card.
- Move ruVocal.png into a collapsed <details> block ("See it in
action") that shows the parallel-tool-calling indicator. Keeps the
scroll-tax low while still demonstrating real functionality.
- Rewrite the lead sentence to load with searchable keywords:
"multi-model AI chat with built-in Model Context Protocol (MCP)
tool calling".
- Bullet list now leads with model count, mentions HNSW vector
search and OpenRouter explicitly, and ties the parallel-tool
feature to the "Step N — M tools completed" indicator visible in
the screenshot.
- Both <img> tags carry SEO-rich alt text covering: multi-agent AI,
MCP, persistent vector memory, AgentDB + HNSW, parallel tool
calling, and the model lineup.
- Trailing keywords blockquote for index coverage on terms like
"Model Context Protocol", "agent swarms", "OpenRouter".
Co-Authored-By: RuFlo <ruv@ruv.net>