Files
ruv a4c2755c8d feat(metaharness): Phase 1 MVP plugin — 5 skills + ADR-150 constraint enforcement (iter 1)
ADR-150 Phase 1 MVP shipped. Adds plugins/ruflo-metaharness/ with the
five most-leverage skills from the integration matrix:

  harness-score        — 5-dim readiness scorecard
  harness-genome       — 7-section categorical report
  harness-mcp-scan     — static MCP security findings
  harness-threat-model — enterprise-grade threat model
  harness-mint         — scaffold custom harness (DRY-RUN by default)

ARCHITECTURE
- Every skill shells out to `npx metaharness ...` / `npx -p metaharness
  harness ...` via the `_harness.mjs` shared helper. NO library imports
  on ruflo's boot path. 60s hard timeout per subprocess.
- `_harness.mjs` is the single bridge — every skill imports from it.
  Centralizes spawnSync, timeout handling, JSON parse, and graceful
  degradation (`emitDegradedJsonAndExit`).
- Graceful degradation per ADR-150 architectural constraint rule #3:
  when `npx` can't fetch metaharness (offline, no network, unreachable
  registry), each script emits `{degraded:true, reason:'metaharness-
  not-available', hint:...}` and exits 0. Ruflo continues to function.

PHASE-0 BASELINE CAPTURED (ruflo's own scorecard, 2026-06-16):
  harnessFit          82/100
  compileConfidence   100
  taskCoverage        79
  toolSafety          100
  memoryUsefulness    40    ← weakest dimension, track over time
  estCostPerRunUsd    $0.048
  recommendedMode     CLI + MCP
  archetype           typescript-sdk-harness
  template            vertical:coding
  scaffoldReady       true
  risk_score          0.27  ← low
  publish_readiness   0.9

SAFETY (harness-mint)
- DRY-RUN by default; --confirm required to actually write
- Refuses to write to project root or any path inside the calling repo
- Defaults --target to /tmp/ruflo-mint-<ts>-<name>/
- Refuses existing target dirs (no overwrites)

DEPENDENCIES (ADR-150 constraint rule #2: optional only, never required)
- `metaharness@~0.1.11` added to @claude-flow/cli optionalDependencies
- `@metaharness/router` re-pinned from `^0.3.2` to `~0.3.2` (tilde, not
  caret) per ADR-150 review-round-1 — the upstream had 5 releases in
  2.7h on 2026-06-15, the caret was too permissive

SMOKE
- 14 structural invariants in plugins/ruflo-metaharness/scripts/smoke.sh
- Pin: graceful degradation must be present in _harness.mjs (rule #3)
- Pin: every script must use the shared _harness.mjs (not duplicate
  spawnSync — single audit point)
- Pin: mint.mjs MUST contain the "refusing to write to project root"
  string (safety regression guard)
- Pin: no SKILL.md grants wildcard tools (security)

FLEET
- 32 → 33 plugins, 402 → 422 structural steps passing
- All three audits (exit-bypass, frontmatter, manifest) green
- Five new MCP-tool exposures live via the plugin

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-06-16 13:09:07 -04:00

93 lines
3.6 KiB
JavaScript
Executable File

#!/usr/bin/env node
// score.mjs — `cost-style` wrapper around `metaharness score <path>`.
//
// Returns the 5-dimension scorecard (harnessFit / compileConfidence /
// taskCoverage / toolSafety / memoryUsefulness) + estCostPerRunUsd +
// scaffoldReady boolean. Reads-only; no side effects.
//
// USAGE
// node scripts/score.mjs # current dir
// node scripts/score.mjs --path <dir> # specific dir
// node scripts/score.mjs --alert-on-fit-below 70 # exit 1 if harnessFit < 70
// node scripts/score.mjs --format json
//
// EXIT CODES
// 0 scored OK (or degraded — MetaHarness not available)
// 1 --alert-on-fit-below threshold breached
// 2 config error or scoring failure
import { runMetaharness, emitDegradedJsonAndExit } from './_harness.mjs';
const ARGS = (() => {
const a = { path: '.', format: 'json', alertFitBelow: null };
for (let i = 2; i < process.argv.length; i++) {
const v = process.argv[i];
if (v === '--path') a.path = process.argv[++i];
else if (v === '--alert-on-fit-below') a.alertFitBelow = parseFloat(process.argv[++i]);
else if (v === '--format') a.format = process.argv[++i];
}
return a;
})();
function main() {
const r = runMetaharness(['score', ARGS.path]);
if (r.degraded) {
emitDegradedJsonAndExit(r.reason);
return;
}
if (r.exitCode !== 0 || !r.json) {
console.error(`score: metaharness exited ${r.exitCode}`);
if (r.stderr) console.error(r.stderr.slice(0, 400));
process.exit(2);
}
const payload = { ...r.json, durationMs: r.durationMs };
if (ARGS.alertFitBelow !== null) {
if (!isFinite(ARGS.alertFitBelow)) {
console.error(`score: --alert-on-fit-below must be a finite number`);
process.exit(2);
}
payload.alert = {
threshold: ARGS.alertFitBelow,
triggered: typeof payload.harnessFit === 'number' && payload.harnessFit < ARGS.alertFitBelow,
reason: typeof payload.harnessFit === 'number' && payload.harnessFit < ARGS.alertFitBelow
? `harnessFit ${payload.harnessFit} < ${ARGS.alertFitBelow}`
: `harnessFit ${payload.harnessFit ?? 'unknown'} ≥ ${ARGS.alertFitBelow} — OK`,
};
}
if (ARGS.format === 'json') {
console.log(JSON.stringify(payload, null, 2));
} else {
console.log(`# harness-score — ${payload.repo || ARGS.path}`);
console.log('');
console.log(`| Dimension | Value |`);
console.log(`|---|---:|`);
console.log(`| harnessFit | ${payload.harnessFit ?? '—'} |`);
console.log(`| compileConfidence | ${payload.compileConfidence ?? '—'} |`);
console.log(`| taskCoverage | ${payload.taskCoverage ?? '—'} |`);
console.log(`| toolSafety | ${payload.toolSafety ?? '—'} |`);
console.log(`| memoryUsefulness | ${payload.memoryUsefulness ?? '—'} |`);
console.log(`| estCostPerRunUsd | $${payload.estCostPerRunUsd ?? '—'} |`);
console.log(`| recommendedMode | ${payload.recommendedMode ?? '—'} |`);
console.log(`| archetype | ${payload.archetype ?? '—'} |`);
console.log(`| template | ${payload.template ?? '—'} |`);
console.log(`| scaffoldReady | ${payload.scaffoldReady ?? '—'} |`);
console.log(`| hardConstraints | ${payload.hardConstraints ?? '—'} |`);
console.log(`| **duration** | ${payload.durationMs}ms |`);
console.log('');
if (payload.alert) {
if (payload.alert.triggered) {
console.log(`⚠ **ALERT**: ${payload.alert.reason}`);
} else {
console.log(`✓ ${payload.alert.reason}`);
}
console.log('');
}
}
if (payload.alert?.triggered) process.exit(1);
}
main();