Skip to content

Instantly share code, notes, and snippets.

@ruvnet
Created August 12, 2026 02:09
Show Gist options
  • Select an option

  • Save ruvnet/ca6e5e63dce4fece0d7dafa60c77808b to your computer and use it in GitHub Desktop.

Select an option

Save ruvnet/ca6e5e63dce4fece0d7dafa60c77808b to your computer and use it in GitHub Desktop.
ADR-382 — Init scaffold content drift remediation (ruflo v3.38.0)

ADR-382 — Init Scaffold Content Drift Remediation

A full writeup of the investigation, fix, and shipped release for ruvnet/ruflo's two-install-track divergence (npm vs. the Claude Code marketplace plugin).

The premise

ruflo ships two independent install tracks:

  1. npm install @claude-flow/cli (or the ruflo/claude-flow wrappers) — pinned, versioned tarball.
  2. The Claude Code marketplace plugin — installed via git clone/git pull of this repo, no build step, always resolves whatever's on main.

An investigation into where those two tracks silently diverge surfaced four independently-verified defects. All four were re-verified directly against main at HEAD (through commit 4ac1ab9ff, v3.37.0) before any fix landed — none of the numbers below were trusted from the originating report without a fresh grep/ls/diff against the live tree.

Evidence ledger

Gap 1 — ADR-128's own promised mitigation was never implemented

ADR-128 Phase 2 deleted 9 forked agent files from the init template — coder.md, researcher.md, reviewer.md, tester.md, memory-specialist.md, security-auditor.md, sparc-orchestrator.md, goal-planner.md, adr-architect.md — making each owning plugin (ruflo-core, ruflo-testgen, ruflo-rag-memory, ruflo-security-audit, ruflo-sparc, ruflo-goals, ruflo-adr) canonical instead.

$ ls v3/@claude-flow/cli/.claude/agents/ | grep -E 'coder|researcher|reviewer|tester'
(empty)

ADR-128's own Consequences section named the fix: "Mitigation: ruflo migrate should detect removed agents and print install suggestions." Reading migrate.ts in full (783 lines) showed five subcommands (status/run/verify/rollback/breaking) — all scoped to the unrelated v2→v3 config-format migration. None referenced agent files, AGENTS_MAP, or plugin install suggestions. The promised mitigation simply didn't exist.

Gap 2 — Bundled scaffold content references dead CLI/tool forms

$ grep -rl 'npx claude-flow' v3/@claude-flow/cli/.claude | wc -l
172
$ grep -rlE 'sparc_mode|task_orchestrate|memory_usage' v3/@claude-flow/cli/.claude | wc -l
96

172 files referenced the stale npx claude-flow invocation form (superseded by npx @claude-flow/cli@latest). 96 referenced MCP tool names (sparc_mode, task_orchestrate, memory_usage, and more) absent from the live mcp__claude-flow__* registry. A fresh ruflo init shipped hundreds of files whose worked examples silently fail if copied verbatim.

Gap 3 — The marketplace-plugin install track pins nothing

// plugins/ruflo-core/.mcp.json (before)
{
  "mcpServers": {
    "ruflo": {
      "command": "npx",
      "args": ["-y", "@claude-flow/cli@latest"]
    }
  }
}

Every marketplace-plugin user's MCP server re-resolved to whatever was newest on the npm registry at each launch — independent of, and often diverging from, whatever version they'd separately npm install-ed. .claude/helpers/hook-handler.cjs already solved this exact problem for its own hook-refresh spawn path (resolveCliBinForHook() — a local-bin-candidate walk with a dist/src/index.js existence guard against source-only marketplace checkouts). The plugin's own MCP launch path had no equivalent.

Gap 4 — The marketplace registry is missing 3 of 37 plugins

$ ls plugins/ | wc -l          # 37 (36 plugin dirs + README.md)
$ jq '.plugins | length' .claude-plugin/marketplace.json   # 35

ruflo-agntcy, ruflo-bbs-federation, and ruflo-business-pods existed fully built in plugins/ but were absent from the marketplace manifest — uninstallable via ruflo plugins install <name> despite shipping in the repo.

The fix — three parallel worktree agents

Rather than one sequential PR, ADR-382's three parts were dispatched as independent background agents, each in its own isolated git worktree (avoiding write conflicts on shared files), each responsible for its own branch → PR → tests → CI:

Part Scope PR
A Local-bin-first resolver for plugins/ruflo-core/.mcp.json (mirrors resolveCliBinForHook()), + registered the 3 missing plugins in marketplace.json #2975
B Implemented ADR-128's named mitigation — migrate.ts now detects the 9 removed agents against a project's .claude/agents/** tree and Claude Code's real plugin-install manifest, printing the exact ruflo plugins install <plugin> command per gap #2973
C scripts/smoke-init-scaffold-references.mjs — a warn-only CI guard deriving live MCP-tool and CLI-subcommand sets (not hand-maintained lists) and flagging drift; a deterministic remap of all 701 npx claude-flow occurrences across 142 files; 410 dead-tool-name occurrences left deliberately FLAGged rather than guessed at #2974

Part C's guard was scoped up mid-flight to cover two more regression classes: an unpinned-@latest check across every plugins/*/.mcp.json (not just ruflo-core), and a plugins/*marketplace.json completeness check — so the exact regression class that hid 3 plugins can't recur silently.

Validation

Each PR ran the full CI matrix (Test Suite, Type Check V3, CodeQL, ~140 smoke jobs spanning ADR-102/112/125/127/128/129/130/131, Windows/macOS/Linux hook execution, plugin install-safety, witness-manifest verification) before merge. Merge order was A → B → C: Part C's own guard checks (plugin pin regression + marketplace completeness) depend on Part A's fix being present on main to report clean rather than warn-only-red, so after A and B landed, C's branch was merged with main and its guard re-run to confirm 0 violations on those two checks before merging.

FAIL: check 2 (dead MCP tool references) — 410 violation(s)   ← expected, tracked follow-up
total: 410 violation(s) across 4 checks                        ← checks 1/3/4 clean

Ship

Backward-compatible additions, no breaking changes → minor bump, 3.37.03.38.0, all three packages in lockstep (@claude-flow/cli, claude-flow, ruflo), all three dist-tags (latest/alpha/v3alpha) verified against the live registry post-publish.

Post-publish, the actual published tarball was pulled and inspected — not just the dist-tag metadata:

$ npm pack @claude-flow/cli@3.38.0 && tar -xzf claude-flow-cli-3.38.0.tgz
$ grep -c "REMOVED_AGENTS" package/dist/src/commands/migrate.js
3
$ grep -rn 'npx claude-flow[^-@/]' package/.claude/ | grep -v '@claude-flow@\|claude-flow-codex'
(0 matches — was 172)

Part A's fix lives on the git-based marketplace-plugin track directly (already effective the moment it merged to main) — it isn't part of the npm tarball's files array, so no publish was required for that piece specifically.

Links

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment