Repository navigation
fix(metaharness): use the @metaharness/* and ruflo CLI already installed instead of npm-installing at tool-call time (#3366) - #3367
Conversation
|
Note on the red Test Suite (ubuntu-latest) check — I don't believe it comes from this PR, and here is the evidence rather than an assertion. The ratchet flags two files: Neither appears in this PR's diff (25 files, all under
Happy to rebase or re-run if you'd rather see it green before reviewing. |
…led instead of npm-installing at tool-call time (ruvnet#3366) 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 (b8ec03f), and _darwin.mjs stayed at ~0.8.0 while the CLI moved @metaharness/darwin to ~0.9.0 (ruvnet#2958) and ~0.10.2 (ruvnet#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 (ruvnet#3306; cold-cache npx cost ruvnet#3145/ruvnet#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
f2d6545 to
0a2df2b
Compare
Hi @ruvnet and @proffesor-for-testing,
This finishes the job
_harness.mjs's own SECURITY + PERF block started: pin the version, resolve what is installed, and stop touching the registry per call. Three paths in the same plugin never got that treatment, and the two pins that decide what "installed" means had drifted two minors behind the CLI. @proffesor-for-testing — both regression tests are hermetic (stub packages, anode_moduleslayout built in a temp dir, annpm/npxPATH trap), and the Tests section lists what survives deleting each new guard, including the four assertions that also pass onmain.What this fixes
Fixes #3366. On a stock ruflo 3.42.4 install, 14 of the 15
metaharness_*MCP tools that run a subprocess reach the npm registry at tool-call time, and offline they degrade or return silently empty results — although ruflo shipsmetaharness0.4.2,@metaharness/darwin0.10.2 and@metaharness/redblue0.1.4, and the@claude-flow/clithe memory tools need is the one that spawned them.Why the two halves ship together. They are the same six files, and each alone leaves the bug half-fixed:
metaharnessresolve locally, but darwin still runsnpx -y -p @metaharness/darwin@~0.10.2on every call, redblue still npm-installs, and the memory tools still shell tonpx @claude-flow/cli@latest._darwin.mjs/_redblue.mjsto prefer an installed copy — but with the old~0.8.0pin the shipped darwin 0.10.2 does not satisfy it (satisfiesTildeRange('0.10.2', '~0.8.0')is false), so darwin's "installed copy" branch is dead on a stock install and every call attempts adarwin-cache-0.8.0install instead. The pin fix is what makes the new resolution path resolvable.Measured end to end through the real compiled MCP handlers, offline: 15 npm/npx spawns → 0, with all 15 tools returning real results.
The fix
1. Pins (
_harness.mjs,_darwin.mjs,check-metaharness-pins.mjs)METAHARNESS_PIN_VERSION'~0.3.0'→'~0.4.1'. For 0.x,~0.4.1and the CLI's^0.4.1are the same set (>=0.4.1 <0.5.0), so every copy a clean install can produce is accepted: 0.4.2 (the ruflo tree), 0.4.1 (v3/pnpm-lock.yaml), 0.4.16 (npmlatest).DARWIN_PIN_VERSION'~0.8.0'→'~0.10.2', identical to the CLI's optionalDependency.gepa.mjsderives its cache pin from this, so gepa's fallback moves with it; on a stock install gepa's bare import (0.10.2) and the bin resolution now agree.REDBLUE_PIN_VERSIONis unchanged:~0.1.4still admits npmlatest(0.1.6), and the CLI does not declare redblue.check-metaharness-pins.mjsnow also reads each helper's*_PIN_VERSION. Three rules: the pin must be a~X.Y.Zrange —_invoke.satisfiesTildeRange(), the predicate the helpers actually apply, parses nothing else, so a^/exact/latestpin would reject every installed copy whilecovers()called the row green; when the CLI declares the package the helper range must cover the declared range (outer.lo <= inner.lo && inner.hi <= outer.hi, no network, so it runs under--offline); otherwise it must admit npmlatest, like the existing rows. An unreadable constant stays fatal.satisfies()now shares abounds()helper withcovers(), and the existing rows print byte-identically.metaharness-pin-drift.ymlalso triggers on the three helper files.2. No npm/npx at tool-call time (
_darwin.mjs,_redblue.mjs, the four memory scripts,_invoke.mjs)_invoke.mjsgains three shared helpers:resolvePackageBin(pkgDir, binName)(the package's ownbinmap entry, realpath'd),packageBinRelPath(), andrunRufloCli(args)=spawnSync(process.execPath, [<cli>/bin/cli.js, ...args], { shell: false }).<cli>is the@claude-flow/clithat owns the plugin:<scripts>/../../..published, or<scripts>/../../../v3/@claude-flow/cliin a checkout, both derived fromimport.meta.url— never from cwd orPATH. A candidate counts only ifdist/src/index.jsexists next tobin/cli.js(mcp-launch.cjsL42-L47's unbuilt-checkout guard) and itspackage.jsonis named@claude-flow/cli— a check this PR adds, so an unrelated directory two levels up can never qualify. In the published layout that is the same CLI whose MCP server spawned the tool, becauselocatePluginScripts()L78-L95 finds the plugin by walking up from that server's owndist/._darwin.mjsresolvesmetaharness-darwinthe way_harness.mjsresolves its bins: installed copy satisfying the pin → the one-timedarwin-cache-<pin>that gepa already shares →process.execPath <abs bin>withshell:false. If neither resolves, the result is the same{degraded:true, reason:'metaharness-darwin-not-available'}the npx failure produced._redblue.mjschecks the installed copy first and realpaths the CLI path for both the local and the cache copy (the isMain guard above). Its spawn moves from'node'+shell: win32toprocess.execPath+shell:false: that argv carries MCP-supplied--config/--out/--in, and undershell:trueNode hands cmd.exe one unquoted string, sox.yaml & whoamiwould run as a command (and an installed path containing a space would break).audit-list/audit-trend/oia-audit/similaritycallrunRufloCli(['memory', …]). Argument lists and result handling are unchanged.drift-from-historydrops its unusedCLI_PKG. Kept:CLI_CORE=1still meansnpx @claude-flow/cli-core@alpha(the ADR-100 opt-in), and a layout with no usable local CLI still falls back tonpx @claude-flow/cli@latest.npx -y @metaharness/darwin@~0.8.0shell-out now describe the resolution; five more skills/agents/commands docs get the new current-pin numbers.3. Where the installed-copy lookup may search (please sanity-check this call)
findLocalPackageDir()at base walks upnode_modulesfrom the plugin's own directory and fromprocess.cwd()(L106-L132), and its only caller is_harness.mjs. The MCP handler spawns the scripts with nocwd, so they inherit the server's cwd — the user's project. Wiring_darwin.mjsand_redblue.mjsinto the same helper would have extended that cwd walk to two paths that execute a package bin with MCP-supplied argv. That is reachable whenever the plugin-directory walk finds nothing in range — which, with the old darwin pin, is every stock install. It is covered by the new test: revert the option and Phase 5 fails withdarwin in the tool's cwd node_modules is NOT executed (got stub cwd-darwin)andredblue in the tool's cwd node_modules is NOT executed, i.e. the planted stub runs.So
findLocalPackageDirkeeps its name, its signature and its default, and takes an option: the two new callers pass{ fromCwd: false }and search only the ruflo install that owns the plugin, falling back to the pinned cache install. This costs nothing measurable — the 15 → 0 spawn result, the plugin suites and smoke are all measured with it in place — because on a stock install the shipped copies are ancestors of the plugin directory anyway._harness.mjsis deliberately not changed:metaharness_score/genome/mcp_scan/threat_modelhave resolved a project-localmetaharnesssince the pinned-cache refactor,RUFLO_METAHARNESS_SKIP_LOCAL=1still disables it, and narrowing it is a behaviour change beyond this fix. If you want that one narrowed too, say so and I will push it as a follow-up.Alternatives considered
@claude-flow/cli/package.jsonat runtime instead of pinning in the helpers. Single source of truth, but the helpers would need caret support (satisfiesTildeRangeis tilde-only), a different relative path per layout, and a policy for peer vs optional ranges — and it drops the explicit "only bumped deliberately" posture_harness.mjsdocuments for supply-chain reasons. Explicit pins plus a guard that fails on divergence keep that posture.>=0.3.0 <0.5.0). Accepts releases CI never exercises, and the matcher only supports~.RUFLO_CLI_BIN). Exact for MCP calls, but it needs TypeScript changes in both spawners (metaharness-tools.ts,commands/metaharness.ts) and does nothing for directnode scripts/x.mjsruns. The location-derived CLI is that same package in the published layout and covers both entry points with a plugin-only change. An env override can still be added later.npx @claude-flow/cli@<version>or add--prefer-offline. Still npx: the first call per version downloads the whole CLI and it still depends on npx cache state ([verification] MEDIUM: doctor health check output not captured — concurrent npx runs corrupt shared npm cache #3190, ruflo@3.41.2 reports latest while executing cached @claude-flow/cli@3.33.0: wrapper dependency permits stale runtime #3306).--prefer-offline. Stays inconsistent with_harness.mjs/gepa and can still run a darwin other than the one gepa imports.Tests
Two new hermetic tests, both wired into
smoke.sh(so they run inmetaharness-ci.yml→ "Plugin structural smoke"). Neither needs the network or a real MetaHarness package; both skip on win32, because thenpm/npxtrap is a POSIXshscript. Every run below used a scratchHOME, a scratchRUFLO_METAHARNESS_CACHE_BASEand that trap — never~/.ruflo.test-pin-alignment.mjs(smoke step17z74a) — each helper pin accepts the floor of the CLI-declared range and a later patch, using the runtime predicatesatisfiesTildeRange; then, in a published layout (<p>/node_modules/@claude-flow/cli/plugins/…/scriptsnext to a stub<p>/node_modules/metaharnessat the declared floor),metaharnessResolution()must reportsource: 'local',score.mjsmust return the installed copy's output, and no npm/npx may be spawned.On
main(the test copied ontoe558f0c0f, everything else base) —3 passed, 9 failed:On this branch:
12 passed, 0 failed.test-no-tool-time-npx.mjs(smoke step17r2) — builds a published layout with a stub@claude-flow/cli(a file-backedmemory store|list|retrievethat logs its calls), stubmetaharnessand@metaharness/darwinat the pin floors, and a stub@metaharness/redbluesymlinked from a pnpm-like store that keeps upstream's isMain guard. Phase 1 darwin (both success branches: the installed copy, and the versioned cache with no installed copy); Phase 2 redblue through the symlink; Phase 3 the oia-audit → audit-list → audit-trend/similarity round trip through the shipping CLI; Phase 4 the kept contracts; Phase 5 the scoping and repair guards.On
main(same treatment) —7 passed, 19 failed. The 19:On this branch:
26 passed, 0 failed.What survives deleting each guard (each mutation applied to a scratch copy of
scripts/, thentest-no-tool-time-npx.mjsre-run):findLocalPackageDir'sfromCwdoption ignored (cwd walk always on)_redblue.mjsspawn back toshell: process.platform === 'win32'_redblue.mjsspawns the resolved bin with shell:false"realpathSynccalls removedpj.name === '@claude-flow/cli'check droppedHonest gaps in that table. Four assertions pass on
mainas well as here, so they are guards against this PR's new surface rather than regression tests for the old bug: the two "cwdnode_modulesis NOT executed" assertions (base has no installed-copy lookup for darwin/redblue at all, so nothing local ever runs), "a bin/cli.js whose package.json is not @claude-flow/cli is rejected" (base always goes to npx), and "…still degrades with exit 0" (base degrades too). The mutation table above is what shows they are load-bearing.Pin-drift guard, run four ways:
End to end through the real compiled MCP handlers. A copy of an installed ruflo 3.42.4 tree with only
plugins/ruflo-metaharnessswapped, scratch HOME/cwd/cache base, npm/npx trapped (offline). All 15 subprocess-backed tools, in one process:e558f0c0fnpm install metaharness@~0.3.0each (4)checkout-requirednpx @claude-flow/cli@latest memory …each (3)npx … @metaharness/darwin@~0.8.0each (3)npm install @metaharness/redblue@~0.1.4npx … memory list+npm install metaharness@~0.3.0)End-to-end harness used for that table (
mcp-e2e.mjs)--ruflois any directory whosenode_modules/holds an installed ruflo tree:$(npm root -g)/ruflofor a global install, or the prefix ofnpm i --prefix <dir> ruflo@3.42.4. The tree is only read.--pluginis a checkout'splugins/ruflo-metaharness(upstream or this branch).Neighbouring suites, upstream vs this branch, in the same environment: a plain
git archivecheckout of each side (no build), scratch HOME, an empty scratch cache base and the npm/npx trap. Nothing was seeded, so these numbers reproduce on a cold machine:smoke.sh: upstream 107 passed / 16 failed, this branch 109 / 16. The failing-step sets are identical (diffof the two FAIL lists is empty); the +2 are the new17r2and17z74a. Trapped npm/npx calls: 37 on both — in a bare checkout nothing is installed on the walk-up path, so both sides cache-install (the only difference in the log ismetaharness@~0.3.0→metaharness@~0.4.1), and the memory tools fall back to npx because the checkout has no built CLI.test-graceful-degradation16/16,test-similarity102/0,_spike-similarity✓,test-parallel-pipeline25/25 — identical on both.test-pipeline-roundtripneeds a real metaharness and exits 2 offline on both.test-mcp-tools(the same checkouts withv3/@claude-flow/cli/distbuilt, so the memory path can resolve a local CLI): 168 passed / 0 failed on both, and its npm/npx spawns drop 21 → 15. Upstream: 12 ×metaharness@~0.3.0, 6 ×npx @claude-flow/cli@latest memory …(3 list, 3 retrieve), 1 ×npx -y -p @metaharness/darwin@~0.8.0, 1 × darwin cache install, 1 × redblue cache install. This branch: 12 ×metaharness@~0.4.1, 2 × darwin cache install, 1 × redblue cache install. The six memory calls are gone; darwin's per-call npx becomes a cache install. The pinned cache installs remain because a bare checkout has nothing installed on the walk-up path.no-metaharness-smoke.yml(7 skills,SKIP_LOCAL=1, empty cache, unreachable registry): identical on both — every skill emits its degraded payload and exits 0, exceptdrift-from-history, which exits 3 by design.tsc --noEmit -p v3/@claude-flow/cli: 4 errors, all pre-existingTS6305from the unbuilt@claude-flow/swarmproject references. Byte-identical to the same run with the two touched TypeScript files reverted toe558f0c0f.--no-file-parallelism) fordistill-tuning,harness-verify,neural-router,guidance-brain,agenticow-tools: 59 passed / 8 skipped / 5 failed. All five failures are environmental or pre-existing and unrelated: four indistill-tuningarebetter-sqlite3failing to load its native binding (no Node 26 prebuild on this machine), and the fifth is the knownguidance-brain › classifies every live registered MCP tool without duplicates. This PR changes no TypeScript behaviour — only two comments.Performance / compatibility
npm install(up to 180 s) or a per-call npx resolution when ruflo's own copies are present. In the offline end-to-end run above,metaharness_audit_listtakes 648 ms (local CLI start plus the list) against the 90 s CI timeout the cold-npx path is documented to hit. I did not measure the old online path, because that means running npx against the registry.~/.ruflo/metaharness-cache-0.3.0anddarwin-cache-0.8.0directories are left alone; they are simply never selected again. If no installed copy qualifies (a stripped install, or a CI checkout), the one-time cache install now fetches~0.4.1/~0.10.2.package.jsonbut not the bin — npm killed by the 180 s install timeout, or two first calls racing into the same prefix — used to count as installed forever, becauseensureCachedInstall's default probe ispackage.json. npx repaired that by re-resolving;_darwin.mjsnow re-runs the install once, probing the bin file itself, and then degrades as before.process.execPathwithshell:false, sooia-audit's JSON--valueand redblue's--config/--out/--inno longer go throughcmd.exequoting. The npx fallbacks keepshell: win32, as before. This is not tested on Windows — the new tests skip on win32 because the trap is POSIXsh.@metaharness/*import is added, no new dependency, and every unresolvable path returns the same degraded payload with exit 0 (drill above).How it was found
A cross-CLI check of the MCP tool surface on a real three-client setup (Claude Code, Codex CLI and Grok Build CLI sharing one global ruflo 3.42.4). The
metaharness_*tools downloaded packages on first use whileruflo doctorreported metaharness 0.4.2 installed. Pointing the versioned cache directories at an installed metaharness made the pin-backed tools work with nothing downloaded — and an offline end-to-end run of all 15 tools through the real MCP handlers still logged spawns, which is how the second half surfaced: darwin's per-call npx, redblue's cache install, andnpx @claude-flow/cli@latest memory.Notes for the maintainer
CI changes metaharness version.
metaharness-ci.yml'sscoreandmcp-scanjobs check out the repo with no install step, so the plugin has no local copy to resolve and always takes the cache-install path — today that is 0.3.2, the same release users get; after this PR it is the newest 0.4.x (0.4.16). The same applies to the drift round-trip'soia-audit.mjs, and tooia-audit-weekly.yml, which also runs on the post-merge push because its push paths include_harness.mjs. I ran those gates on both trees, with the pinned caches really installed from npm (~0.3.0→ 0.3.2,~0.4.1→ 0.4.16) andSKIP_LOCAL=1to mimic a runner:score --alert-on-fit-below 70genome --alert-on-risk-above 0.5mcp-scan --fail-on highlow, 1 finding, exit 0medium, 3 findings, exit 0The mcp-scan delta is two new
risky-bash-allowmedium findings forBash(node .claude/*)andBash(node:*)in.claude/settings.json, a rule that landed after 0.4.2.--fail-on highstill exits 0, so nothing goes red, but the uploaded artifact'sworstmoveslow→medium, and so does the oia-audit composite. The first post-merge weekly drift step compares against a 0.3.2-era artifact. The cross-check that CI really runs 0.3.2 today is that my 0.3.2 column above reproduces the artifacts of main's last greenmetaharness-cirun at this exact base (run 35277579682,head_sha e558f0c0f, conclusionsuccess):harnessFit 82, and oneunpinned-depslowfrom mcp-scan.The cwd question in "The fix" §3 is the one judgement call I would like confirmed: I scoped the two new lookups to ruflo's own tree and left
_harness.mjs's existing cwd walk alone. The opposite choice (let all three search cwd, and document it) is one line.Two comments in TypeScript are updated because this PR is what makes them wrong:
distill-oracle.ts'sMH_DARWIN_PINblock andmetaharness-tools.ts's ADR-153 header both named the plugin's~0.8.0pin. Comment-only;tscoutput is byte-identical.Still stale, deliberately left: the cache-dir examples in
_invoke.mjsnow name the new pins, but historical mentions ("metaharness@0.3.0 / darwin@0.8.0" in CLAUDE.md, MCP tool descriptions, gepa/learn headers) record when a feature landed and are left as history.skills/harness-oia-audit/SKILL.mdstill tells people to runnpx @claude-flow/cli@latest memory listby hand — that is a user-facing example, not a code path.Found while testing, not changed here:
similarity.mjs --a-key/--b-keyhands the whole oia-audit record tosimilarity(), which readsscore/genomeat the top level, so identical records score 0.15 while their.fingerprintscores 1.0. It was invisible while the memory path failed.audit-trendanddrift-from-historyuse the fingerprint correctly.Separate follow-up, not changed here:
runScript()inmetaharness-tools.tsmaps a subprocess killed by its 120 s timer (code === null) toexitCode: 0, success: true(L144-L164). After this PR a stock install no longer hits it on first call, but any slow tool still would.metaharness pin drift — a pinned dependency is behind npm latest #3219 (the guard's tracking issue for the
~0.9.0darwin row) was fixed by fix(metaharness): bump the darwin pin to 0.10.2 (unblocks pin-drift + clean-install on main) #3262 and is still open.Overlap with open PRs: only fix: validate skill frontmatter as YAML #3075 touches a file here. It edits the frontmatter
descriptionofskills/harness-threat-model/SKILL.md(line 3, +1/-1) andscripts/audit-skill-frontmatter.mjs, which this PR does not touch; this PR edits line 16 of that SKILL.md, so there is no conflict. fix(metaharness): honor genome verdict exit codes instead of treating them as failure #2644 and fix(metaharness): oia-audit wrappers preserve findings on non-zero exit (#2750) #2751 edit the genome/mcp-scan/threat-model scripts only.ci.ymlis flaky on main at this exact base: of the threeci.ymlruns ate558f0c0f, the push run and the 2026-09-18 scheduled run passed and the 2026-09-19 scheduled run (35415064637) failed — its ratchet reportedreasoningbank-adapter.test.tsandmemory-search-scores-3327.test.ts. This PR adds no vitest-collected file, so a redci.ymlhere is not from this change.Checklist
test-pin-alignment.mjs3/9 → 12/0;test-no-tool-time-npx.mjs7/19 → 26/0), with a mutation table for every new guardtsc: same results as upstream)#3366markers for the issue number, resolution order mirrored from_harness.mjs)🤖 Generated with Claude Code
https://claude.ai/code/session_01KTedGmPKpEdArVBMMn3JaP