build(deps): promote metaharness + @metaharness/router to hard dependencies (ADR-321)

Per decision to guarantee metaharness is always present rather than an
opt-in the platform may skip. Adds to @claude-flow/cli dependencies:
  - metaharness ^0.4.1
  - @metaharness/router ^0.3.2
(removed from optionalDependencies — no dual declaration).

ADR-321 supersedes ADR-150 rule #2 (optional-only) + rule #1 (removable)
for these two packages, and records the honest tradeoff: the MCP
metaharness tools invoke via subprocess (zero static imports), so a hard
dep is functionally a no-op for them (only a cache-warm win); the one
load-bearing consumer of a declared dep is neural-router.ts's
@metaharness/router import behind CLAUDE_FLOW_ROUTER_NEURAL=1. Graceful
-degradation fallbacks are KEPT (cheap insurance + trivial reversibility).

Note: no-metaharness-smoke.yml's ADR-150-rule-#4 enforcement is now
neutralized (npm --no-optional can't remove a hard dep); left in place
for plugin-fleet structural coverage, flagged in ADR-321 for follow-up.

Build: tsc clean; deps resolve in the workspace.

Refs: ADR-321, ADR-150.

Co-Authored-By: RuFlo <ruv@ruv.net>
This commit is contained in:
ruv
2026-07-27 23:44:15 -04:00
parent 7b5e7d8468
commit b8ec03f349
2 changed files with 75 additions and 0 deletions
+2
View File
@@ -104,8 +104,10 @@
"@claude-flow/mcp": "3.0.0-alpha.8",
"@claude-flow/neural": "3.0.0-alpha.9",
"@claude-flow/shared": "3.0.0-alpha.7",
"@metaharness/router": "^0.3.2",
"@noble/ed25519": "2.3.0",
"@ruvector/rabitq-wasm": "0.1.0",
"metaharness": "^0.4.1",
"semver": "7.7.3",
"sql.js": "^1.13.0",
"yaml": "^2.8.0"
@@ -0,0 +1,73 @@
# ADR-321: Promote metaharness to a hard runtime dependency
- **Status**: Accepted
- **Date**: 2026-07-27
- **Supersedes**: ADR-150 §"Architectural constraint" rule #2 (optional-only) and rule #1 (removable) as they apply to `metaharness` and `@metaharness/router`
- **Related**: ADR-150 (metaharness integration surfaces), ADR-148/149 (router integration)
## Context
ADR-150 established metaharness as a *removable augmentation*: `metaharness`
and every `@metaharness/*` package MUST live in `optionalDependencies` (never
`dependencies`), every code path that touches them MUST catch `MODULE_NOT_FOUND`
and degrade gracefully, and a CI gate (`.github/workflows/no-metaharness-smoke.yml`)
installs ruflo with `--no-optional` and asserts the plugin fleet still passes.
The project has decided to make metaharness a **hard runtime dependency** so it
is always installed alongside ruflo rather than being an opt-in the platform may
skip.
### Material fact (recorded for honesty)
At the time of this decision, the two packages are consumed very differently:
- **`metaharness`** — invoked exclusively via **subprocess** (`npx metaharness …`)
from `v3/@claude-flow/cli/src/mcp-tools/metaharness-tools.ts`, which carries
**zero static `@metaharness/*` imports**. For these MCP tools a hard dependency
changes nothing functionally; they shell out regardless of whether the package
is declared. The practical benefit is that the package is present locally, so
the first `npx` invocation is a cache hit instead of a fetch.
- **`@metaharness/router`** — imported statically by `neural-router.ts` behind the
`CLAUDE_FLOW_ROUTER_NEURAL=1` triple-gate. This is the one consumer for which a
*declared* dependency is load-bearing; it was previously expected to be an
`optionalDependency` (ADR-150) though not consistently declared.
## Decision
1. Add to `v3/@claude-flow/cli/package.json` `dependencies`:
- `metaharness`: `^0.4.1`
- `@metaharness/router`: `^0.3.2`
2. Remove them from `optionalDependencies` (no dual declaration).
3. This supersedes ADR-150's optional-only + removable constraints **for these two
packages only**. The other three ADR-150 rules still hold where they make sense:
- **Graceful degradation** — code paths keep their `MODULE_NOT_FOUND` /
subprocess-failure fallbacks. A hard dep should always resolve, but the
defensive fallbacks are cheap insurance against a broken install and are NOT
removed.
- **CI coverage** — `metaharness-ci.yml` still exercises the integration.
## Consequences
- **`no-metaharness-smoke.yml` is neutralized in intent.** `npm install --no-optional`
only skips `optionalDependencies`; a hard `dependencies` entry is installed
regardless, so the "works without metaharness" smoke test can no longer actually
remove metaharness and passes trivially. The workflow is left in place (it still
validates the plugin fleet's structural contract) but its ADR-150-rule-#4
enforcement no longer applies to these two packages. A follow-up may repurpose or
retire it.
- **Install size / surface grows.** metaharness + `@metaharness/router` (+ their
transitives) are now always fetched. Acceptable per the decision.
- **Version-pin risk.** Both packages are 0.x and ship rapid patches. A breaking
change in `@metaharness/router@0.4.x` now breaks a hard-dep install, not just an
opt-in path. Mitigation: the caret ranges (`^0.4.1` / `^0.3.2`) stay within the
current minor; bump deliberately and re-run `metaharness-ci.yml` on upgrade.
- **Reversibility.** Moving both entries back to `optionalDependencies` restores the
ADR-150 model with no code changes, because the graceful-degradation fallbacks
were kept.
## Alternatives considered
- **optionalDependency (ADR-150-compliant).** Zero constraint change, same runtime
behavior for the subprocess tools. Rejected per the decision to guarantee presence.
- **Subprocess-only, no declared dep.** Lowest footprint; the status quo for
`metaharness`. Rejected for the same reason.