mirror of
https://github.com/ruvnet/ruflo.git
synced 2026-11-09 22:13:39 +09:00
On a stock ruflo 3.42.4 install every metaharness_* MCP tool reached for the
npm registry at tool-call time, although the packages it needs are already on
disk. Two causes, in the same six helper files, which is why they ship
together: the helper PINS rejected what ruflo installs, and three code paths
never looked for an installed copy at all. Fixing only the pins leaves darwin
and the memory tools on npx; fixing only the lookups leaves them resolving a
pin that nothing on disk satisfies.
1. Pins (metaharness, @metaharness/darwin)
The metaharness_* tools resolve MetaHarness through the plugin helpers' own
tilde pins, not through package.json. Those pins were never bumped:
_harness.mjs stayed at ~0.3.0 after the CLI declared metaharness ^0.4.1
(b8ec03f34), and _darwin.mjs stayed at ~0.8.0 while the CLI moved
@metaharness/darwin to ~0.9.0 (#2958) and ~0.10.2 (#3262). On a stock install
(metaharness 0.4.2, darwin 0.10.2) findLocalPackageDir() therefore rejected
the bundled metaharness, and every score / genome / mcp_scan / threat_model /
oia_audit / learn call ran
`npm install --prefix ~/.ruflo/metaharness-cache-0.3.0 metaharness@~0.3.0`
at tool-call time — a download of the older 0.3.2 (180 s install budget inside
a 120 s MCP subprocess budget), degraded offline. The darwin tools fetched
darwin 0.8.x through npx while gepa imported the bundled 0.10.2.
- _harness.mjs: METAHARNESS_PIN_VERSION ~0.3.0 -> ~0.4.1 (= CLI ^0.4.1)
- _darwin.mjs: DARWIN_PIN_VERSION ~0.8.0 -> ~0.10.2 (= CLI ~0.10.2)
- scripts/check-metaharness-pins.mjs: the watcher compared only the CLI's
declared ranges (and MH_DARWIN_PIN), so it reported "all current" while the
pins that decide what runs were two minors behind. It now reads each
helper's *_PIN_VERSION: the pin must be a `~X.Y.Z` range (the only form
_invoke.satisfiesTildeRange accepts), and must cover the declared range, or
admit npm latest when the CLI declares nothing (redblue). An unreadable
constant is fatal. `satisfies` now shares a `bounds()` helper with the new
`covers()`; the results for the existing rows are unchanged.
- metaharness-pin-drift.yml also triggers on the three helper files.
- The two current-pin comments in TypeScript (distill-oracle.ts MH_DARWIN_PIN,
metaharness-tools.ts ADR-153 header) named the old ~0.8.0 plugin pin; they
now name ~0.10.2. Comments only, no behaviour.
2. No npm/npx at tool-call time (darwin, redblue, memory)
- _darwin.mjs ran `npx -y -p @metaharness/darwin@<pin> metaharness-darwin` on
every evolve / bench / security_bench call: npm resolved the range each
time, it ignored the optionalDependency the CLI ships, and it could run a
different darwin than the gepa import in the same plugin. It now resolves
like _harness.mjs — installed copy satisfying the pin, then the one-time
versioned darwin-cache-<pin> that gepa already shares, then
`process.execPath <abs bin>` with shell:false.
- _redblue.mjs always npm-installed into redblue-cache-<pin> on first use,
never looking at the installed copy (ruflo 3.42.4 carries 0.1.4). It now
checks the installed copy first. The chosen CLI path is realpath'd, because
redblue only dispatches when import.meta.url === file://argv[1]; a
pnpm-symlinked package dir (or a cache base under /tmp on macOS) otherwise
makes it exit 0 having done nothing. Its spawn moves to process.execPath
with shell:false: the argv carries MCP-supplied --config/--out/--in, which
under the previous `shell: win32` went through cmd.exe unquoted.
- audit-list / audit-trend / oia-audit / similarity ran
`npx @claude-flow/cli@latest memory ...` — an npm-registry resolution per
call (the check _harness.mjs already removed for its own npx path), and
possibly a different CLI version than the server that spawned the tool
(#3306; cold-cache npx cost #3145/#3154). Without registry access oia_audit
reported persisted:false and audit_list silently showed 0 records. They now
call _invoke.runRufloCli(): process.execPath + bin/cli.js of the
@claude-flow/cli that ships the plugin (<cli>/plugins/ruflo-metaharness, or
v3/@claude-flow/cli in a checkout). A candidate needs dist/src/index.js next
to bin/cli.js (mcp-launch.cjs's unbuilt-checkout guard) plus a package.json
named @claude-flow/cli (a new check, so an unrelated parent directory can
never qualify). CLI_CORE=1 still opts into `npx @claude-flow/cli-core@alpha`,
and a layout with no usable local CLI still falls back to
`npx @claude-flow/cli@latest`.
- drift-from-history.mjs: drop its unused CLI_PKG constant.
The two new lookups end in a spawn of a package bin with MCP-supplied argv, so
they pass findLocalPackageDir(..., { fromCwd: false }) and search only the
ruflo install that owns the plugin — never the tool's cwd or its ancestors.
The pre-existing metaharness lookup keeps its cwd walk unchanged.
Tests (both hermetic, published layout, stub packages, npm/npx PATH trap):
- test-pin-alignment.mjs (+ smoke 17z74a): 3 passed / 9 failed on main,
12 / 0 here.
- test-no-tool-time-npx.mjs (+ smoke 17r2): 7 passed / 19 failed on main,
26 / 0 here. It covers both darwin success branches (installed copy and the
versioned cache), the memory round trip through the shipping CLI, the kept
contracts (CLI_CORE, npx fallback, a bin/cli.js that is not
@claude-flow/cli, darwin absent -> degraded exit 0), and the hardening
above: a darwin/redblue in the tool's own cwd node_modules is never
executed, a cache dir with package.json but no bin gets one reinstall
instead of degrading forever, and neither helper spawns the resolved bin
through a shell.
ADR-150 is unchanged: no static @metaharness import is added, no new
dependency, and every unresolvable path still returns the same
{degraded:true, reason} with exit 0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KTedGmPKpEdArVBMMn3JaP