@claude-flow/cli 3.11.0 → 3.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/commands/doctor.d.ts.map +1 -1
- package/dist/src/commands/doctor.js +175 -1
- package/dist/src/commands/doctor.js.map +1 -1
- package/dist/src/commands/eject.d.ts +33 -0
- package/dist/src/commands/eject.d.ts.map +1 -0
- package/dist/src/commands/eject.js +195 -0
- package/dist/src/commands/eject.js.map +1 -0
- package/dist/src/commands/hooks.d.ts.map +1 -1
- package/dist/src/commands/hooks.js +2 -1
- package/dist/src/commands/hooks.js.map +1 -1
- package/dist/src/commands/index.d.ts.map +1 -1
- package/dist/src/commands/index.js +4 -0
- package/dist/src/commands/index.js.map +1 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +3 -0
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/metaharness.d.ts +39 -0
- package/dist/src/commands/metaharness.d.ts.map +1 -0
- package/dist/src/commands/metaharness.js +194 -0
- package/dist/src/commands/metaharness.js.map +1 -0
- package/dist/src/mcp-client.d.ts.map +1 -1
- package/dist/src/mcp-client.js +4 -0
- package/dist/src/mcp-client.js.map +1 -1
- package/dist/src/mcp-tools/index.d.ts +1 -0
- package/dist/src/mcp-tools/index.d.ts.map +1 -1
- package/dist/src/mcp-tools/index.js +2 -0
- package/dist/src/mcp-tools/index.js.map +1 -1
- package/dist/src/mcp-tools/metaharness-tools.d.ts +31 -0
- package/dist/src/mcp-tools/metaharness-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/metaharness-tools.js +372 -0
- package/dist/src/mcp-tools/metaharness-tools.js.map +1 -0
- package/dist/src/plugins/store/discovery.d.ts.map +1 -1
- package/dist/src/plugins/store/discovery.js +3 -0
- package/dist/src/plugins/store/discovery.js.map +1 -1
- package/dist/src/plugins/store/types.d.ts +6 -1
- package/dist/src/plugins/store/types.d.ts.map +1 -1
- package/dist/src/ruvector/model-router.d.ts.map +1 -1
- package/dist/src/ruvector/model-router.js +37 -0
- package/dist/src/ruvector/model-router.js.map +1 -1
- package/dist/src/ruvector/router-parallel-recorder.d.ts +127 -0
- package/dist/src/ruvector/router-parallel-recorder.d.ts.map +1 -0
- package/dist/src/ruvector/router-parallel-recorder.js +183 -0
- package/dist/src/ruvector/router-parallel-recorder.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +13 -10
- package/plugins/ruflo-metaharness/.claude-flow/data/pending-insights.jsonl +1 -0
- package/plugins/ruflo-metaharness/.claude-flow/neural/stats.json +6 -0
- package/plugins/ruflo-metaharness/.claude-plugin/plugin.json +32 -0
- package/plugins/ruflo-metaharness/README.md +64 -0
- package/plugins/ruflo-metaharness/agents/metaharness-architect.md +58 -0
- package/plugins/ruflo-metaharness/commands/ruflo-metaharness.md +46 -0
- package/plugins/ruflo-metaharness/scripts/_harness.mjs +261 -0
- package/plugins/ruflo-metaharness/scripts/_similarity.mjs +161 -0
- package/plugins/ruflo-metaharness/scripts/_spike-similarity.mjs +223 -0
- package/plugins/ruflo-metaharness/scripts/audit-list.mjs +158 -0
- package/plugins/ruflo-metaharness/scripts/audit-trend.mjs +272 -0
- package/plugins/ruflo-metaharness/scripts/bench-parse-mcp-scan.mjs +146 -0
- package/plugins/ruflo-metaharness/scripts/bench-recordpair-overhead.mjs +186 -0
- package/plugins/ruflo-metaharness/scripts/bench-similarity.mjs +177 -0
- package/plugins/ruflo-metaharness/scripts/drift-from-history.mjs +363 -0
- package/plugins/ruflo-metaharness/scripts/genome.mjs +80 -0
- package/plugins/ruflo-metaharness/scripts/mcp-scan.mjs +111 -0
- package/plugins/ruflo-metaharness/scripts/mint.mjs +119 -0
- package/plugins/ruflo-metaharness/scripts/oia-audit.mjs +228 -0
- package/plugins/ruflo-metaharness/scripts/router-parallel-analyze.mjs +250 -0
- package/plugins/ruflo-metaharness/scripts/score.mjs +92 -0
- package/plugins/ruflo-metaharness/scripts/similarity.mjs +158 -0
- package/plugins/ruflo-metaharness/scripts/smoke.sh +2268 -0
- package/plugins/ruflo-metaharness/scripts/test-graceful-degradation.mjs +141 -0
- package/plugins/ruflo-metaharness/scripts/test-mcp-tools.mjs +437 -0
- package/plugins/ruflo-metaharness/scripts/test-parallel-pipeline.mjs +204 -0
- package/plugins/ruflo-metaharness/scripts/test-pipeline-roundtrip.mjs +586 -0
- package/plugins/ruflo-metaharness/scripts/test-similarity.mjs +334 -0
- package/plugins/ruflo-metaharness/scripts/test-with-openrouter.mjs +229 -0
- package/plugins/ruflo-metaharness/scripts/threat-model.mjs +59 -0
- package/plugins/ruflo-metaharness/skills/harness-drift-from-history/SKILL.md +65 -0
- package/plugins/ruflo-metaharness/skills/harness-genome/SKILL.md +54 -0
- package/plugins/ruflo-metaharness/skills/harness-mcp-scan/SKILL.md +47 -0
- package/plugins/ruflo-metaharness/skills/harness-mint/SKILL.md +72 -0
- package/plugins/ruflo-metaharness/skills/harness-oia-audit/SKILL.md +79 -0
- package/plugins/ruflo-metaharness/skills/harness-score/SKILL.md +66 -0
- package/plugins/ruflo-metaharness/skills/harness-similarity/SKILL.md +67 -0
- package/plugins/ruflo-metaharness/skills/harness-threat-model/SKILL.md +39 -0
- package/dist/src/benchmarks/capability-tasks.d.ts +0 -126
- package/dist/src/benchmarks/capability-tasks.d.ts.map +0 -1
- package/dist/src/benchmarks/capability-tasks.js +0 -147
- package/dist/src/benchmarks/capability-tasks.js.map +0 -1
- package/dist/src/benchmarks/gaia-agent-contrastive.d.ts +0 -53
- package/dist/src/benchmarks/gaia-agent-contrastive.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-agent-contrastive.js +0 -177
- package/dist/src/benchmarks/gaia-agent-contrastive.js.map +0 -1
- package/dist/src/benchmarks/gaia-agent-gemini.d.ts +0 -51
- package/dist/src/benchmarks/gaia-agent-gemini.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-agent-gemini.js +0 -352
- package/dist/src/benchmarks/gaia-agent-gemini.js.map +0 -1
- package/dist/src/benchmarks/gaia-claude-p.d.ts +0 -105
- package/dist/src/benchmarks/gaia-claude-p.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-claude-p.js +0 -269
- package/dist/src/benchmarks/gaia-claude-p.js.map +0 -1
- package/dist/src/benchmarks/gaia-claude-p.smoke.d.ts +0 -20
- package/dist/src/benchmarks/gaia-claude-p.smoke.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-claude-p.smoke.js +0 -168
- package/dist/src/benchmarks/gaia-claude-p.smoke.js.map +0 -1
- package/dist/src/benchmarks/gaia-codeagent-runner.py +0 -556
- package/dist/src/benchmarks/gaia-codeagent.d.ts +0 -129
- package/dist/src/benchmarks/gaia-codeagent.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-codeagent.js +0 -590
- package/dist/src/benchmarks/gaia-codeagent.js.map +0 -1
- package/dist/src/benchmarks/gaia-codeagent.smoke.d.ts +0 -33
- package/dist/src/benchmarks/gaia-codeagent.smoke.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-codeagent.smoke.js +0 -241
- package/dist/src/benchmarks/gaia-codeagent.smoke.js.map +0 -1
- package/dist/src/benchmarks/gaia-dag-pilot.d.ts +0 -22
- package/dist/src/benchmarks/gaia-dag-pilot.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-dag-pilot.js +0 -158
- package/dist/src/benchmarks/gaia-dag-pilot.js.map +0 -1
- package/dist/src/benchmarks/gaia-dag.d.ts +0 -110
- package/dist/src/benchmarks/gaia-dag.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-dag.js +0 -658
- package/dist/src/benchmarks/gaia-dag.js.map +0 -1
- package/dist/src/benchmarks/gaia-dag.smoke.d.ts +0 -21
- package/dist/src/benchmarks/gaia-dag.smoke.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-dag.smoke.js +0 -290
- package/dist/src/benchmarks/gaia-dag.smoke.js.map +0 -1
- package/dist/src/benchmarks/gaia-ensemble-pilot.d.ts +0 -17
- package/dist/src/benchmarks/gaia-ensemble-pilot.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-ensemble-pilot.js +0 -106
- package/dist/src/benchmarks/gaia-ensemble-pilot.js.map +0 -1
- package/dist/src/benchmarks/gaia-ensemble.d.ts +0 -103
- package/dist/src/benchmarks/gaia-ensemble.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-ensemble.js +0 -462
- package/dist/src/benchmarks/gaia-ensemble.js.map +0 -1
- package/dist/src/benchmarks/gaia-gemini-pilot.d.ts +0 -17
- package/dist/src/benchmarks/gaia-gemini-pilot.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-gemini-pilot.js +0 -168
- package/dist/src/benchmarks/gaia-gemini-pilot.js.map +0 -1
- package/dist/src/benchmarks/gaia-mode-router.d.ts +0 -67
- package/dist/src/benchmarks/gaia-mode-router.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-mode-router.js +0 -126
- package/dist/src/benchmarks/gaia-mode-router.js.map +0 -1
- package/dist/src/benchmarks/gaia-mode-router.smoke.d.ts +0 -15
- package/dist/src/benchmarks/gaia-mode-router.smoke.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-mode-router.smoke.js +0 -131
- package/dist/src/benchmarks/gaia-mode-router.smoke.js.map +0 -1
- package/dist/src/benchmarks/gaia-tools/grounded_query.smoke.d.ts +0 -24
- package/dist/src/benchmarks/gaia-tools/grounded_query.smoke.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-tools/grounded_query.smoke.js +0 -276
- package/dist/src/benchmarks/gaia-tools/grounded_query.smoke.js.map +0 -1
- package/dist/src/benchmarks/gaia-tools/image_describe.d.ts +0 -45
- package/dist/src/benchmarks/gaia-tools/image_describe.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-tools/image_describe.js +0 -222
- package/dist/src/benchmarks/gaia-tools/image_describe.js.map +0 -1
- package/dist/src/benchmarks/gaia-tools/image_describe.smoke.d.ts +0 -24
- package/dist/src/benchmarks/gaia-tools/image_describe.smoke.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-tools/image_describe.smoke.js +0 -169
- package/dist/src/benchmarks/gaia-tools/image_describe.smoke.js.map +0 -1
- package/dist/src/benchmarks/gaia-tools/pdf_read.d.ts +0 -27
- package/dist/src/benchmarks/gaia-tools/pdf_read.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-tools/pdf_read.js +0 -226
- package/dist/src/benchmarks/gaia-tools/pdf_read.js.map +0 -1
- package/dist/src/benchmarks/gaia-tools/python_exec.d.ts +0 -31
- package/dist/src/benchmarks/gaia-tools/python_exec.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-tools/python_exec.js +0 -94
- package/dist/src/benchmarks/gaia-tools/python_exec.js.map +0 -1
- package/dist/src/benchmarks/gaia-tools/python_exec.smoke.d.ts +0 -13
- package/dist/src/benchmarks/gaia-tools/python_exec.smoke.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-tools/python_exec.smoke.js +0 -98
- package/dist/src/benchmarks/gaia-tools/python_exec.smoke.js.map +0 -1
- package/dist/src/benchmarks/gaia-tools/visit_webpage.d.ts +0 -52
- package/dist/src/benchmarks/gaia-tools/visit_webpage.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-tools/visit_webpage.js +0 -304
- package/dist/src/benchmarks/gaia-tools/visit_webpage.js.map +0 -1
- package/dist/src/benchmarks/gaia-tools/web_browse.d.ts +0 -61
- package/dist/src/benchmarks/gaia-tools/web_browse.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-tools/web_browse.js +0 -217
- package/dist/src/benchmarks/gaia-tools/web_browse.js.map +0 -1
- package/dist/src/benchmarks/gaia-tools/web_browse.smoke.d.ts +0 -26
- package/dist/src/benchmarks/gaia-tools/web_browse.smoke.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-tools/web_browse.smoke.js +0 -152
- package/dist/src/benchmarks/gaia-tools/web_browse.smoke.js.map +0 -1
- package/dist/src/benchmarks/gaia-tools/web_search.smoke.d.ts +0 -24
- package/dist/src/benchmarks/gaia-tools/web_search.smoke.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-tools/web_search.smoke.js +0 -311
- package/dist/src/benchmarks/gaia-tools/web_search.smoke.js.map +0 -1
- package/dist/src/benchmarks/gaia-visit-webpage.smoke.d.ts +0 -25
- package/dist/src/benchmarks/gaia-visit-webpage.smoke.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-visit-webpage.smoke.js +0 -304
- package/dist/src/benchmarks/gaia-visit-webpage.smoke.js.map +0 -1
- package/dist/src/commands/performance-capability.d.ts +0 -30
- package/dist/src/commands/performance-capability.d.ts.map +0 -1
- package/dist/src/commands/performance-capability.js +0 -349
- package/dist/src/commands/performance-capability.js.map +0 -1
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// router-parallel-analyze.mjs — ADR-150 Phase 2 SelfEvolvingRouter
|
|
3
|
+
// promotion gate.
|
|
4
|
+
//
|
|
5
|
+
// Reads paired routing decisions (Thompson bandit pick + SelfEvolvingRouter
|
|
6
|
+
// pick, both with measured outcomes) from a JSONL trajectory file and
|
|
7
|
+
// computes the THREE PROMOTION CRITERIA from ADR-150 review-round-1:
|
|
8
|
+
//
|
|
9
|
+
// (a) qualityScore improvement > 2% (relative)
|
|
10
|
+
// (b) usdPerDecision increase < 1% (relative)
|
|
11
|
+
// (c) p95 routing-decision latency increase < 5% (relative)
|
|
12
|
+
//
|
|
13
|
+
// ALL THREE must hold for promotion. AND, not OR — the OR form let
|
|
14
|
+
// quality gains mask cost regressions, exactly the failure mode ADR-149's
|
|
15
|
+
// Pareto framing was built to prevent.
|
|
16
|
+
//
|
|
17
|
+
// INPUT FORMAT (.swarm/router-parallel.jsonl, one JSON record per line):
|
|
18
|
+
// {
|
|
19
|
+
// "ts": "<iso>",
|
|
20
|
+
// "task": { ... },
|
|
21
|
+
// "bandit": { "pick": "<modelId>", "predictedQuality": 0.84, "predictedCostUsd": 0.003 },
|
|
22
|
+
// "ser": { "pick": "<modelId>", "predictedQuality": 0.87, "predictedCostUsd": 0.004 },
|
|
23
|
+
// "outcome": { "actualModel": "<modelId>", "actualQuality": 0.91, "actualUsd": 0.0035, "actualLatencyMs": 1240 }
|
|
24
|
+
// }
|
|
25
|
+
//
|
|
26
|
+
// USAGE
|
|
27
|
+
// node scripts/router-parallel-analyze.mjs --input .swarm/router-parallel.jsonl
|
|
28
|
+
// node scripts/router-parallel-analyze.mjs --input <file> --format json
|
|
29
|
+
// node scripts/router-parallel-analyze.mjs --input <file> --strict # exit 1 if NOT promotable
|
|
30
|
+
//
|
|
31
|
+
// EXIT CODES
|
|
32
|
+
// 0 analysis complete; in --strict mode → all three criteria passed
|
|
33
|
+
// 1 --strict requested AND at least one criterion failed (NOT promotable)
|
|
34
|
+
// 2 config error or input file missing/malformed
|
|
35
|
+
|
|
36
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
37
|
+
|
|
38
|
+
const ARGS = (() => {
|
|
39
|
+
const a = { input: '.swarm/router-parallel.jsonl', format: 'table', strict: false };
|
|
40
|
+
for (let i = 2; i < process.argv.length; i++) {
|
|
41
|
+
const v = process.argv[i];
|
|
42
|
+
if (v === '--input') a.input = process.argv[++i];
|
|
43
|
+
else if (v === '--format') a.format = process.argv[++i];
|
|
44
|
+
else if (v === '--strict') a.strict = true;
|
|
45
|
+
}
|
|
46
|
+
return a;
|
|
47
|
+
})();
|
|
48
|
+
|
|
49
|
+
function median(sorted) {
|
|
50
|
+
if (sorted.length === 0) return 0;
|
|
51
|
+
const n = sorted.length;
|
|
52
|
+
return n % 2 === 0 ? (sorted[n / 2 - 1] + sorted[n / 2]) / 2 : sorted[(n - 1) / 2];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function pctile(sorted, q) {
|
|
56
|
+
if (sorted.length === 0) return 0;
|
|
57
|
+
const idx = Math.floor(q * (sorted.length - 1));
|
|
58
|
+
return sorted[idx];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function mean(arr) {
|
|
62
|
+
if (arr.length === 0) return 0;
|
|
63
|
+
return arr.reduce((s, x) => s + x, 0) / arr.length;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function main() {
|
|
67
|
+
if (!existsSync(ARGS.input)) {
|
|
68
|
+
if (ARGS.format === 'json') {
|
|
69
|
+
console.log(JSON.stringify({
|
|
70
|
+
ok: true,
|
|
71
|
+
sufficient: false,
|
|
72
|
+
reason: 'input-file-not-found',
|
|
73
|
+
input: ARGS.input,
|
|
74
|
+
hint: 'Enable parallel logging (CLAUDE_FLOW_ROUTER_PARALLEL_LOG=1) and run a workload first.',
|
|
75
|
+
}, null, 2));
|
|
76
|
+
} else {
|
|
77
|
+
console.log(`# router-parallel-analyze`);
|
|
78
|
+
console.log('');
|
|
79
|
+
console.log(`_Input file not found: ${ARGS.input}_`);
|
|
80
|
+
console.log('');
|
|
81
|
+
console.log('Enable parallel logging by setting `CLAUDE_FLOW_ROUTER_PARALLEL_LOG=1`');
|
|
82
|
+
console.log('and running a real workload, then re-run this analyzer.');
|
|
83
|
+
}
|
|
84
|
+
process.exit(ARGS.strict ? 1 : 0);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let rows = [];
|
|
88
|
+
try {
|
|
89
|
+
rows = readFileSync(ARGS.input, 'utf-8')
|
|
90
|
+
.split('\n')
|
|
91
|
+
.filter((l) => l.trim())
|
|
92
|
+
.map((l) => JSON.parse(l));
|
|
93
|
+
} catch (e) {
|
|
94
|
+
console.error(`router-parallel-analyze: cannot parse ${ARGS.input}: ${e.message}`);
|
|
95
|
+
process.exit(2);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Filter to records with both predictions + outcome present.
|
|
99
|
+
const usable = rows.filter((r) => r.bandit && r.ser && r.outcome);
|
|
100
|
+
|
|
101
|
+
if (usable.length < 30) {
|
|
102
|
+
const payload = {
|
|
103
|
+
ok: true,
|
|
104
|
+
sufficient: false,
|
|
105
|
+
reason: `n=${usable.length} < 30 (insufficient sample for AND-gate evaluation)`,
|
|
106
|
+
hint: 'Continue collecting parallel-routing data; statistical significance needs ≥30 paired decisions per arm.',
|
|
107
|
+
sampleSize: usable.length,
|
|
108
|
+
generatedAt: new Date().toISOString(),
|
|
109
|
+
};
|
|
110
|
+
if (ARGS.format === 'json') console.log(JSON.stringify(payload, null, 2));
|
|
111
|
+
else {
|
|
112
|
+
console.log(`# router-parallel-analyze`);
|
|
113
|
+
console.log('');
|
|
114
|
+
console.log(`Sample size: **${usable.length}** paired decisions`);
|
|
115
|
+
console.log('');
|
|
116
|
+
console.log(`_Insufficient — need ≥30 for the 3-criteria AND-gate._`);
|
|
117
|
+
}
|
|
118
|
+
process.exit(ARGS.strict ? 1 : 0);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// For each record, attribute the outcome to BOTH arms when they
|
|
122
|
+
// agreed on the model pick; when they disagreed, the actual outcome
|
|
123
|
+
// belongs only to the bandit (which was the executing arm). The SER
|
|
124
|
+
// arm gets its PREDICTED metrics for the counterfactual.
|
|
125
|
+
const banditOutcomes = usable.map((r) => ({
|
|
126
|
+
qualityActual: r.outcome.actualQuality,
|
|
127
|
+
usdActual: r.outcome.actualUsd,
|
|
128
|
+
latencyActual: r.outcome.actualLatencyMs,
|
|
129
|
+
}));
|
|
130
|
+
|
|
131
|
+
// SER counterfactual: use ser.predictedQuality / predictedCostUsd
|
|
132
|
+
// when picks disagreed; use actual when they agreed. Latency: when
|
|
133
|
+
// they agreed, both saw the same actual latency; when they
|
|
134
|
+
// disagreed, the SER pick's latency is unknown — fall back to the
|
|
135
|
+
// mean of agreed-decision latencies for that model.
|
|
136
|
+
const agreedLatencyByModel = {};
|
|
137
|
+
for (const r of usable) {
|
|
138
|
+
if (r.bandit.pick === r.ser.pick) {
|
|
139
|
+
const m = r.outcome.actualModel || r.bandit.pick;
|
|
140
|
+
(agreedLatencyByModel[m] = agreedLatencyByModel[m] || []).push(r.outcome.actualLatencyMs);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const modelMeanLatency = {};
|
|
144
|
+
for (const [m, arr] of Object.entries(agreedLatencyByModel)) {
|
|
145
|
+
modelMeanLatency[m] = mean(arr);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const serOutcomes = usable.map((r) => {
|
|
149
|
+
const agreed = r.bandit.pick === r.ser.pick;
|
|
150
|
+
return {
|
|
151
|
+
qualityActual: agreed ? r.outcome.actualQuality : r.ser.predictedQuality,
|
|
152
|
+
usdActual: agreed ? r.outcome.actualUsd : r.ser.predictedCostUsd,
|
|
153
|
+
latencyActual: agreed
|
|
154
|
+
? r.outcome.actualLatencyMs
|
|
155
|
+
: (modelMeanLatency[r.ser.pick] ?? r.outcome.actualLatencyMs),
|
|
156
|
+
};
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Compute the three criteria.
|
|
160
|
+
const banditQuality = mean(banditOutcomes.map((o) => o.qualityActual));
|
|
161
|
+
const serQuality = mean(serOutcomes.map((o) => o.qualityActual));
|
|
162
|
+
const qualityImprovementPct =
|
|
163
|
+
banditQuality > 0 ? ((serQuality - banditQuality) / banditQuality) * 100 : 0;
|
|
164
|
+
|
|
165
|
+
const banditUsd = mean(banditOutcomes.map((o) => o.usdActual));
|
|
166
|
+
const serUsd = mean(serOutcomes.map((o) => o.usdActual));
|
|
167
|
+
const usdIncreasePct = banditUsd > 0 ? ((serUsd - banditUsd) / banditUsd) * 100 : 0;
|
|
168
|
+
|
|
169
|
+
const banditLatencyP95 = pctile(banditOutcomes.map((o) => o.latencyActual).sort((a, b) => a - b), 0.95);
|
|
170
|
+
const serLatencyP95 = pctile(serOutcomes.map((o) => o.latencyActual).sort((a, b) => a - b), 0.95);
|
|
171
|
+
const latencyIncreasePct =
|
|
172
|
+
banditLatencyP95 > 0 ? ((serLatencyP95 - banditLatencyP95) / banditLatencyP95) * 100 : 0;
|
|
173
|
+
|
|
174
|
+
const passes = {
|
|
175
|
+
quality: qualityImprovementPct > 2,
|
|
176
|
+
cost: usdIncreasePct < 1,
|
|
177
|
+
latency: latencyIncreasePct < 5,
|
|
178
|
+
};
|
|
179
|
+
const allPass = passes.quality && passes.cost && passes.latency;
|
|
180
|
+
|
|
181
|
+
// Disagreement rate (informational; not part of the gate).
|
|
182
|
+
const disagreements = usable.filter((r) => r.bandit.pick !== r.ser.pick).length;
|
|
183
|
+
const disagreementPct = (disagreements / usable.length) * 100;
|
|
184
|
+
|
|
185
|
+
const payload = {
|
|
186
|
+
ok: true,
|
|
187
|
+
sufficient: true,
|
|
188
|
+
sampleSize: usable.length,
|
|
189
|
+
disagreement: {
|
|
190
|
+
count: disagreements,
|
|
191
|
+
pct: Math.round(disagreementPct * 100) / 100,
|
|
192
|
+
},
|
|
193
|
+
bandit: {
|
|
194
|
+
meanQuality: Math.round(banditQuality * 10000) / 10000,
|
|
195
|
+
meanUsd: Math.round(banditUsd * 1e6) / 1e6,
|
|
196
|
+
p95LatencyMs: Math.round(banditLatencyP95),
|
|
197
|
+
},
|
|
198
|
+
ser: {
|
|
199
|
+
meanQuality: Math.round(serQuality * 10000) / 10000,
|
|
200
|
+
meanUsd: Math.round(serUsd * 1e6) / 1e6,
|
|
201
|
+
p95LatencyMs: Math.round(serLatencyP95),
|
|
202
|
+
},
|
|
203
|
+
criteria: {
|
|
204
|
+
qualityImprovementPct: Math.round(qualityImprovementPct * 100) / 100,
|
|
205
|
+
qualityThresholdPct: 2,
|
|
206
|
+
qualityPasses: passes.quality,
|
|
207
|
+
usdIncreasePct: Math.round(usdIncreasePct * 100) / 100,
|
|
208
|
+
usdThresholdPct: 1,
|
|
209
|
+
costPasses: passes.cost,
|
|
210
|
+
latencyIncreasePct: Math.round(latencyIncreasePct * 100) / 100,
|
|
211
|
+
latencyThresholdPct: 5,
|
|
212
|
+
latencyPasses: passes.latency,
|
|
213
|
+
},
|
|
214
|
+
verdict: {
|
|
215
|
+
promotable: allPass,
|
|
216
|
+
reason: allPass
|
|
217
|
+
? 'All three criteria met (quality > 2% AND cost < 1% AND latency < 5%)'
|
|
218
|
+
: `Blocked by: ${[
|
|
219
|
+
!passes.quality && `quality only +${(qualityImprovementPct).toFixed(2)}% (need >2%)`,
|
|
220
|
+
!passes.cost && `cost +${(usdIncreasePct).toFixed(2)}% (need <1%)`,
|
|
221
|
+
!passes.latency && `latency +${(latencyIncreasePct).toFixed(2)}% (need <5%)`,
|
|
222
|
+
].filter(Boolean).join(', ')}`,
|
|
223
|
+
},
|
|
224
|
+
generatedAt: new Date().toISOString(),
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
if (ARGS.format === 'json') {
|
|
228
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
229
|
+
} else {
|
|
230
|
+
console.log('# router-parallel-analyze (ADR-150 SelfEvolvingRouter promotion gate)');
|
|
231
|
+
console.log('');
|
|
232
|
+
console.log(`Sample size: **${payload.sampleSize}** paired decisions`);
|
|
233
|
+
console.log(`Disagreement rate: ${payload.disagreement.pct}% (${payload.disagreement.count} / ${payload.sampleSize})`);
|
|
234
|
+
console.log('');
|
|
235
|
+
console.log(`| Metric | Bandit | SER | Delta | Threshold | Passes |`);
|
|
236
|
+
console.log(`|---|---:|---:|---:|---:|:---:|`);
|
|
237
|
+
console.log(`| Mean quality | ${payload.bandit.meanQuality} | ${payload.ser.meanQuality} | ${payload.criteria.qualityImprovementPct >= 0 ? '+' : ''}${payload.criteria.qualityImprovementPct}% | >+2% | ${passes.quality ? '✓' : '⚠'} |`);
|
|
238
|
+
console.log(`| Mean $/decision | $${payload.bandit.meanUsd.toFixed(6)} | $${payload.ser.meanUsd.toFixed(6)} | ${payload.criteria.usdIncreasePct >= 0 ? '+' : ''}${payload.criteria.usdIncreasePct}% | <+1% | ${passes.cost ? '✓' : '⚠'} |`);
|
|
239
|
+
console.log(`| p95 latency | ${payload.bandit.p95LatencyMs}ms | ${payload.ser.p95LatencyMs}ms | ${payload.criteria.latencyIncreasePct >= 0 ? '+' : ''}${payload.criteria.latencyIncreasePct}% | <+5% | ${passes.latency ? '✓' : '⚠'} |`);
|
|
240
|
+
console.log('');
|
|
241
|
+
console.log(`**Verdict**: ${allPass ? '✓ PROMOTABLE' : '⚠ NOT promotable'}`);
|
|
242
|
+
console.log('');
|
|
243
|
+
console.log(`_${payload.verdict.reason}_`);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (ARGS.strict && !allPass) process.exit(1);
|
|
247
|
+
process.exit(0);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
main();
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// score.mjs — `cost-style` wrapper around `metaharness score <path>`.
|
|
3
|
+
//
|
|
4
|
+
// Returns the 5-dimension scorecard (harnessFit / compileConfidence /
|
|
5
|
+
// taskCoverage / toolSafety / memoryUsefulness) + estCostPerRunUsd +
|
|
6
|
+
// scaffoldReady boolean. Reads-only; no side effects.
|
|
7
|
+
//
|
|
8
|
+
// USAGE
|
|
9
|
+
// node scripts/score.mjs # current dir
|
|
10
|
+
// node scripts/score.mjs --path <dir> # specific dir
|
|
11
|
+
// node scripts/score.mjs --alert-on-fit-below 70 # exit 1 if harnessFit < 70
|
|
12
|
+
// node scripts/score.mjs --format json
|
|
13
|
+
//
|
|
14
|
+
// EXIT CODES
|
|
15
|
+
// 0 scored OK (or degraded — MetaHarness not available)
|
|
16
|
+
// 1 --alert-on-fit-below threshold breached
|
|
17
|
+
// 2 config error or scoring failure
|
|
18
|
+
|
|
19
|
+
import { runMetaharness, emitDegradedJsonAndExit } from './_harness.mjs';
|
|
20
|
+
|
|
21
|
+
const ARGS = (() => {
|
|
22
|
+
const a = { path: '.', format: 'json', alertFitBelow: null };
|
|
23
|
+
for (let i = 2; i < process.argv.length; i++) {
|
|
24
|
+
const v = process.argv[i];
|
|
25
|
+
if (v === '--path') a.path = process.argv[++i];
|
|
26
|
+
else if (v === '--alert-on-fit-below') a.alertFitBelow = parseFloat(process.argv[++i]);
|
|
27
|
+
else if (v === '--format') a.format = process.argv[++i];
|
|
28
|
+
}
|
|
29
|
+
return a;
|
|
30
|
+
})();
|
|
31
|
+
|
|
32
|
+
function main() {
|
|
33
|
+
const r = runMetaharness(['score', ARGS.path]);
|
|
34
|
+
if (r.degraded) {
|
|
35
|
+
emitDegradedJsonAndExit(r.reason);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (r.exitCode !== 0 || !r.json) {
|
|
39
|
+
console.error(`score: metaharness exited ${r.exitCode}`);
|
|
40
|
+
if (r.stderr) console.error(r.stderr.slice(0, 400));
|
|
41
|
+
process.exit(2);
|
|
42
|
+
}
|
|
43
|
+
const payload = { ...r.json, durationMs: r.durationMs };
|
|
44
|
+
|
|
45
|
+
if (ARGS.alertFitBelow !== null) {
|
|
46
|
+
if (!isFinite(ARGS.alertFitBelow)) {
|
|
47
|
+
console.error(`score: --alert-on-fit-below must be a finite number`);
|
|
48
|
+
process.exit(2);
|
|
49
|
+
}
|
|
50
|
+
payload.alert = {
|
|
51
|
+
threshold: ARGS.alertFitBelow,
|
|
52
|
+
triggered: typeof payload.harnessFit === 'number' && payload.harnessFit < ARGS.alertFitBelow,
|
|
53
|
+
reason: typeof payload.harnessFit === 'number' && payload.harnessFit < ARGS.alertFitBelow
|
|
54
|
+
? `harnessFit ${payload.harnessFit} < ${ARGS.alertFitBelow}`
|
|
55
|
+
: `harnessFit ${payload.harnessFit ?? 'unknown'} ≥ ${ARGS.alertFitBelow} — OK`,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (ARGS.format === 'json') {
|
|
60
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
61
|
+
} else {
|
|
62
|
+
console.log(`# harness-score — ${payload.repo || ARGS.path}`);
|
|
63
|
+
console.log('');
|
|
64
|
+
console.log(`| Dimension | Value |`);
|
|
65
|
+
console.log(`|---|---:|`);
|
|
66
|
+
console.log(`| harnessFit | ${payload.harnessFit ?? '—'} |`);
|
|
67
|
+
console.log(`| compileConfidence | ${payload.compileConfidence ?? '—'} |`);
|
|
68
|
+
console.log(`| taskCoverage | ${payload.taskCoverage ?? '—'} |`);
|
|
69
|
+
console.log(`| toolSafety | ${payload.toolSafety ?? '—'} |`);
|
|
70
|
+
console.log(`| memoryUsefulness | ${payload.memoryUsefulness ?? '—'} |`);
|
|
71
|
+
console.log(`| estCostPerRunUsd | $${payload.estCostPerRunUsd ?? '—'} |`);
|
|
72
|
+
console.log(`| recommendedMode | ${payload.recommendedMode ?? '—'} |`);
|
|
73
|
+
console.log(`| archetype | ${payload.archetype ?? '—'} |`);
|
|
74
|
+
console.log(`| template | ${payload.template ?? '—'} |`);
|
|
75
|
+
console.log(`| scaffoldReady | ${payload.scaffoldReady ?? '—'} |`);
|
|
76
|
+
console.log(`| hardConstraints | ${payload.hardConstraints ?? '—'} |`);
|
|
77
|
+
console.log(`| **duration** | ${payload.durationMs}ms |`);
|
|
78
|
+
console.log('');
|
|
79
|
+
if (payload.alert) {
|
|
80
|
+
if (payload.alert.triggered) {
|
|
81
|
+
console.log(`⚠ **ALERT**: ${payload.alert.reason}`);
|
|
82
|
+
} else {
|
|
83
|
+
console.log(`✓ ${payload.alert.reason}`);
|
|
84
|
+
}
|
|
85
|
+
console.log('');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (payload.alert?.triggered) process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
main();
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// similarity.mjs — ADR-152 §3.1 CLI skill.
|
|
3
|
+
//
|
|
4
|
+
// Computes weighted similarity between two harness fingerprints (genome
|
|
5
|
+
// + score JSON). Production-grade companion to _spike-similarity.mjs.
|
|
6
|
+
//
|
|
7
|
+
// USAGE
|
|
8
|
+
// node scripts/similarity.mjs --a a.json --b b.json
|
|
9
|
+
// node scripts/similarity.mjs --a-key harness-X --b-key harness-Y # memory lookup
|
|
10
|
+
// node scripts/similarity.mjs --a a.json --b b.json --per-dimension # breakdown
|
|
11
|
+
// node scripts/similarity.mjs --a a.json --b b.json --format json
|
|
12
|
+
// node scripts/similarity.mjs --a a.json --b b.json --alert-below 0.5 # exit 1 if low
|
|
13
|
+
//
|
|
14
|
+
// EXIT CODES
|
|
15
|
+
// 0 ok (or overall ≥ alert-below threshold)
|
|
16
|
+
// 1 --alert-below AND overall < threshold
|
|
17
|
+
// 2 config / input error (missing args, file not found, malformed JSON)
|
|
18
|
+
//
|
|
19
|
+
// ADR-150 ARCHITECTURAL CONSTRAINTS PRESERVED
|
|
20
|
+
// - Pure-TS function (`_similarity.mjs`), no `@metaharness/*` import
|
|
21
|
+
// - No new dep; uses node:fs + node:child_process only for memory lookup
|
|
22
|
+
// - Graceful degradation: missing keys / malformed JSON → exit 2 with
|
|
23
|
+
// a structured `{ degraded: true, reason: ... }` payload on stdout
|
|
24
|
+
// - CI-gate ready: smoke step 17y locks this contract
|
|
25
|
+
|
|
26
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
27
|
+
import { spawnSync } from 'node:child_process';
|
|
28
|
+
import { similarity } from './_similarity.mjs';
|
|
29
|
+
|
|
30
|
+
const NS = process.env.HARNESS_SIMILARITY_NAMESPACE || 'metaharness-audit';
|
|
31
|
+
const CLI_PKG = process.env.CLI_CORE === '1'
|
|
32
|
+
? '@claude-flow/cli-core@alpha'
|
|
33
|
+
: '@claude-flow/cli@latest';
|
|
34
|
+
|
|
35
|
+
const ARGS = (() => {
|
|
36
|
+
const a = {
|
|
37
|
+
aFile: null, bFile: null,
|
|
38
|
+
aKey: null, bKey: null,
|
|
39
|
+
perDimension: false,
|
|
40
|
+
format: 'table',
|
|
41
|
+
alertBelow: null,
|
|
42
|
+
};
|
|
43
|
+
for (let i = 2; i < process.argv.length; i++) {
|
|
44
|
+
const v = process.argv[i];
|
|
45
|
+
if (v === '--a') a.aFile = process.argv[++i];
|
|
46
|
+
else if (v === '--b') a.bFile = process.argv[++i];
|
|
47
|
+
else if (v === '--a-key') a.aKey = process.argv[++i];
|
|
48
|
+
else if (v === '--b-key') a.bKey = process.argv[++i];
|
|
49
|
+
else if (v === '--per-dimension') a.perDimension = true;
|
|
50
|
+
else if (v === '--format') a.format = process.argv[++i];
|
|
51
|
+
else if (v === '--alert-below') a.alertBelow = Number(process.argv[++i]);
|
|
52
|
+
}
|
|
53
|
+
return a;
|
|
54
|
+
})();
|
|
55
|
+
|
|
56
|
+
function emitDegradedAndExit(reason, code = 2) {
|
|
57
|
+
const payload = {
|
|
58
|
+
degraded: true,
|
|
59
|
+
reason,
|
|
60
|
+
adr: 'ADR-152',
|
|
61
|
+
skill: 'harness-similarity',
|
|
62
|
+
generatedAt: new Date().toISOString(),
|
|
63
|
+
};
|
|
64
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
65
|
+
process.exit(code);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function memRetrieve(key) {
|
|
69
|
+
const r = spawnSync('npx', [
|
|
70
|
+
CLI_PKG, 'memory', 'retrieve',
|
|
71
|
+
'--namespace', NS, '--key', key,
|
|
72
|
+
], { stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf-8', shell: process.platform === 'win32' });
|
|
73
|
+
if (r.status !== 0) return null;
|
|
74
|
+
const m = /\{[\s\S]*\}/.exec(r.stdout || '');
|
|
75
|
+
if (!m) return null;
|
|
76
|
+
try { return JSON.parse(m[0]); } catch { return null; }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function loadHarness(label, filePath, memKey) {
|
|
80
|
+
if (filePath) {
|
|
81
|
+
if (!existsSync(filePath)) emitDegradedAndExit(`${label}: file not found: ${filePath}`);
|
|
82
|
+
try { return JSON.parse(readFileSync(filePath, 'utf-8')); }
|
|
83
|
+
catch (e) { emitDegradedAndExit(`${label}: invalid JSON: ${e.message}`); }
|
|
84
|
+
}
|
|
85
|
+
if (memKey) {
|
|
86
|
+
const rec = memRetrieve(memKey);
|
|
87
|
+
if (!rec) emitDegradedAndExit(`${label}: key not found in namespace ${NS}: ${memKey}`);
|
|
88
|
+
return rec;
|
|
89
|
+
}
|
|
90
|
+
emitDegradedAndExit(`${label}: --${label} or --${label}-key required`);
|
|
91
|
+
return null; // unreachable; emitDegradedAndExit exits
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function main() {
|
|
95
|
+
if (!ARGS.aFile && !ARGS.aKey) emitDegradedAndExit('--a <file> or --a-key <memkey> required');
|
|
96
|
+
if (!ARGS.bFile && !ARGS.bKey) emitDegradedAndExit('--b <file> or --b-key <memkey> required');
|
|
97
|
+
|
|
98
|
+
const a = loadHarness('a', ARGS.aFile, ARGS.aKey);
|
|
99
|
+
const b = loadHarness('b', ARGS.bFile, ARGS.bKey);
|
|
100
|
+
|
|
101
|
+
const result = similarity(a, b, { perDimension: ARGS.perDimension });
|
|
102
|
+
|
|
103
|
+
const payload = {
|
|
104
|
+
adr: 'ADR-152',
|
|
105
|
+
skill: 'harness-similarity',
|
|
106
|
+
inputs: {
|
|
107
|
+
a: ARGS.aFile ?? `mem:${ARGS.aKey}`,
|
|
108
|
+
b: ARGS.bFile ?? `mem:${ARGS.bKey}`,
|
|
109
|
+
},
|
|
110
|
+
...result,
|
|
111
|
+
alert: ARGS.alertBelow != null ? {
|
|
112
|
+
threshold: ARGS.alertBelow,
|
|
113
|
+
triggered: result.overall < ARGS.alertBelow,
|
|
114
|
+
reason: result.overall < ARGS.alertBelow
|
|
115
|
+
? `overall ${result.overall} < threshold ${ARGS.alertBelow}`
|
|
116
|
+
: `overall ${result.overall} ≥ threshold ${ARGS.alertBelow}`,
|
|
117
|
+
} : null,
|
|
118
|
+
generatedAt: new Date().toISOString(),
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
if (ARGS.format === 'json') {
|
|
122
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
123
|
+
} else {
|
|
124
|
+
console.log(`# harness similarity (ADR-152)\n`);
|
|
125
|
+
console.log(`Inputs:`);
|
|
126
|
+
console.log(` a: ${payload.inputs.a}`);
|
|
127
|
+
console.log(` b: ${payload.inputs.b}`);
|
|
128
|
+
console.log('');
|
|
129
|
+
console.log(`| Component | Score | Weight | Contribution |`);
|
|
130
|
+
console.log(`|-------------|-------:|-------:|-------------:|`);
|
|
131
|
+
const { weights, components } = payload;
|
|
132
|
+
console.log(`| cosine | ${components.cosine.toFixed(4)} | ${weights.cosine} | ${(components.cosine * weights.cosine).toFixed(4)} |`);
|
|
133
|
+
console.log(`| categorical | ${components.categorical.toFixed(4)} | ${weights.categorical} | ${(components.categorical * weights.categorical).toFixed(4)} |`);
|
|
134
|
+
console.log(`| jaccard | ${components.jaccard.toFixed(4)} | ${weights.jaccard} | ${(components.jaccard * weights.jaccard).toFixed(4)} |`);
|
|
135
|
+
console.log('');
|
|
136
|
+
console.log(`**Overall:** ${payload.overall.toFixed(4)}`);
|
|
137
|
+
if (payload.alert) {
|
|
138
|
+
console.log('');
|
|
139
|
+
console.log(payload.alert.triggered ? `⚠ ALERT: ${payload.alert.reason}` : `✓ ${payload.alert.reason}`);
|
|
140
|
+
}
|
|
141
|
+
if (ARGS.perDimension && payload.perDimension) {
|
|
142
|
+
console.log('');
|
|
143
|
+
console.log('## Per-dimension breakdown');
|
|
144
|
+
console.log('');
|
|
145
|
+
console.log('| Dimension | a | b | contribution |');
|
|
146
|
+
console.log('|---|---|---|---:|');
|
|
147
|
+
for (const [k, v] of Object.entries(payload.perDimension)) {
|
|
148
|
+
const av = Array.isArray(v.a) ? `[${v.a.join(',')}]` : String(v.a ?? '-');
|
|
149
|
+
const bv = Array.isArray(v.b) ? `[${v.b.join(',')}]` : String(v.b ?? '-');
|
|
150
|
+
console.log(`| ${k} | ${av} | ${bv} | ${Number(v.contribution).toFixed(4)} |`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (payload.alert?.triggered) process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
main();
|