v3/docs/adr/ADR-321-metaharness-hard-dependency.md
metaharness and @metaharness/routerADR-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.
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.v3/@claude-flow/cli/package.json dependencies:
metaharness: ^0.4.1@metaharness/router: ^0.3.2optionalDependencies (no dual declaration).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.metaharness-ci.yml still exercises the integration.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.@metaharness/router (+ their
transitives) are now always fetched. Acceptable per the decision.@metaharness/[email protected] 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.optionalDependencies restores the
ADR-150 model with no code changes, because the graceful-degradation fallbacks
were kept.metaharness. Rejected for the same reason.