23f7624596
🔄 Automated Rollback Manager / 🔄 Execute Rollback (push) Blocked by required conditions
🔄 Automated Rollback Manager / ✅ Post-Rollback Verification (push) Blocked by required conditions
🔄 Automated Rollback Manager / 📊 Rollback Monitoring (push) Blocked by required conditions
🔄 Automated Rollback Manager / ⏳ Manual Rollback Approval (push) Blocked by required conditions
V3 CI/CD Pipeline / MCP protocol smoke / macos-latest (push) Waiting to run
V3 CI/CD Pipeline / Memory import smoke / macos-latest (push) Waiting to run
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / macos-latest (push) Waiting to run
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / windows-latest (push) Waiting to run
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / macos-latest (push) Waiting to run
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / windows-latest (push) Waiting to run
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / macos-latest (push) Waiting to run
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / windows-latest (push) Waiting to run
V3 CI/CD Pipeline / Witness verify (signed manifest) / macos-latest (push) Blocked by required conditions
V3 CI/CD Pipeline / Witness verify (signed manifest) / ubuntu-latest (push) Blocked by required conditions
V3 CI/CD Pipeline / Witness verify (signed manifest) / windows-latest (push) Blocked by required conditions
V3 CI/CD Pipeline / Publish to npm (alpha) (push) Blocked by required conditions
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / macos-latest / Node 22 (push) Waiting to run
V3 CI/CD Pipeline / Plugin hooks smoke / macos-latest / Node 22 (push) Waiting to run
ADR-166 MCP Bridge Security Lock / Static-source security lock (push) Failing after 0s
ADR-166 MCP Bridge Security Lock / Compose default binds loopback + Mongo has auth (push) Failing after 2s
CodeQL Advanced / Analyze (rust) (push) Failing after 0s
ADR-166 MCP Bridge Security Lock / plugin-agent-federation bindHost default (push) Failing after 1s
ADR-166 MCP Bridge Security Lock / Runtime behavior — 401 + terminal gate + fail-closed (push) Failing after 4s
business-pods-smoke / smoke (push) Failing after 1s
all-plugins-smoke / smoke-all (push) Failing after 2s
CI/CD Pipeline / Security & Code Quality (push) Failing after 1s
CI/CD Pipeline / Test Suite (ubuntu-latest) (push) Failing after 1s
CI/CD Pipeline / Build & Package (macos-latest) (push) Has been skipped
CI/CD Pipeline / Build & Package (ubuntu-latest) (push) Has been skipped
CI/CD Pipeline / Build & Package (windows-latest) (push) Has been skipped
CI/CD Pipeline / Deploy & Release (push) Waiting to run
CI/CD Pipeline / CI Status (push) Waiting to run
CI/CD Pipeline / Documentation & Examples (push) Failing after 1s
Clone Tracker (14-day rolling) / Snapshot clones for ruflo ecosystem (push) Failing after 1s
CodeQL Advanced / Analyze (actions) (push) Failing after 1s
CodeQL Advanced / Analyze (javascript-typescript) (push) Failing after 1s
federation-peer-rust / stable-noop (push) Failing after 1s
metaharness-ci / score (push) Failing after 1s
metaharness-ci / router-compat (push) Failing after 0s
metaharness-ci / similarity-tests (push) Failing after 0s
no-agentbbs-smoke / smoke-without-agentbbs (push) Failing after 1s
V3 CI/CD Pipeline / Build V3 (windows-latest) (push) Has been skipped
codex-integration-audit / Codex integration audit (push) Failing after 1s
helpers-manifest-guard / guard (push) Failing after 1s
🔗 Cross-Agent Integration Tests / 🤝 Agent Coordination Tests (push) Has been skipped
🔗 Cross-Agent Integration Tests / 🧠 Memory Sharing Integration (push) Has been skipped
🔗 Cross-Agent Integration Tests / 🛡️ Fault Tolerance Tests (push) Has been skipped
🔗 Cross-Agent Integration Tests / ⚡ Performance Integration Tests (push) Has been skipped
🔗 Cross-Agent Integration Tests / 📊 Integration Test Report (push) Waiting to run
metaharness-ci / mcp-scan (push) Failing after 1s
metaharness-ci / eject-dryrun (push) Failing after 1s
metaharness-ci / metaharness-real-data (push) Failing after 0s
no-cli-optdep-bloat-2561 / guard (push) Failing after 1s
no-metaharness-smoke / smoke-without-metaharness (push) Failing after 1s
no-phantom-agentic-flow-subpath / guard (push) Failing after 1s
🔄 Automated Rollback Manager / 🚨 Failure Detection (push) Failing after 1s
V3 CI/CD Pipeline / Plugin hooks smoke / ubuntu-latest / Node 22 (push) Failing after 1s
V3 CI/CD Pipeline / ruflo-graph-intelligence build + test smoke (#2044, ADR-123) (push) Failing after 1s
CVE Audit Gate / Audit root (critical-blocking) (push) Failing after 2s
cost-tracker-smoke / smoke (push) Failing after 3s
oia-audit-weekly / audit (push) Failing after 2s
ruflo-agent-smoke / ruflo-agent structural smoke (push) Failing after 1s
📊 Status Badges Update / 📊 Update Status Badges (push) Failing after 1s
V3 CI/CD Pipeline / Static regression guards (#2267 YAML + (push) Failing after 1s
V3 CI/CD Pipeline / Test V3 Packages (push) Failing after 0s
V3 CI/CD Pipeline / agent_execute provider routing smoke (#2042) (push) Failing after 0s
CVE Audit Gate / Audit v3 (critical-blocking) (push) Failing after 1s
federation-peer-rust / stable-native (push) Failing after 2s
🔗 Cross-Agent Integration Tests / 🚀 Integration Test Setup (push) Failing after 2s
neural-trader-smoke / runtime-smoke (push) Failing after 1s
🔄 Automated Rollback Manager / 🔍 Pre-Rollback Validation (push) Waiting to run
V3 CI/CD Pipeline / Build V3 (macos-latest) (push) Has been skipped
V3 CI/CD Pipeline / Build V3 (ubuntu-latest) (push) Has been skipped
V3 CI/CD Pipeline / Type Check V3 (push) Failing after 1s
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / ubuntu-latest / Node 24 (push) Failing after 1s
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / ubuntu-latest / Node 22 (push) Failing after 2s
V3 CI/CD Pipeline / browser rvf create flag smoke (#2015) (push) Failing after 0s
V3 CI/CD Pipeline / Dependency review (#2046) (push) Has been skipped
V3 CI/CD Pipeline / Supply-chain audit (#2046) (push) Failing after 0s
V3 CI/CD Pipeline / witness marker drift smoke (#2021) (push) Failing after 1s
V3 CI/CD Pipeline / neural-trader portfolio CG smoke (#2068, ADR-126 Phase 3) (push) Failing after 1s
V3 CI/CD Pipeline / neural-trader backtest signing smoke (#2068, ADR-126 Phase 4) (push) Failing after 1s
V3 CI/CD Pipeline / kg-extract type-import classification smoke (#2049) (push) Failing after 0s
V3 CI/CD Pipeline / witness verify precondition smoke (#1880) (push) Failing after 2s
V3 CI/CD Pipeline / neural-trader pipeline risk-gate smoke (#2068, ADR-126 Phase 5) (push) Failing after 0s
V3 CI/CD Pipeline / neural-trader feature attribution smoke (#2068, ADR-126 Phase 6) (push) Failing after 0s
V3 CI/CD Pipeline / plugin-registry signature verification smoke (#1922, CWE-347) (push) Failing after 4s
V3 CI/CD Pipeline / memory stats legacy-DB smoke (#2120) (push) Failing after 4s
V3 CI/CD Pipeline / github deprecated actions smoke (#2089, ADR-127 Phase 3) (push) Failing after 1s
V3 CI/CD Pipeline / graph query + pathfinder smoke (ADR-130 P2+P5) (push) Has been skipped
V3 CI/CD Pipeline / graph trajectory hooks smoke (ADR-130 P3) (push) Has been skipped
V3 CI/CD Pipeline / graph plugin adapter smoke (ADR-130 P4) (push) Has been skipped
V3 CI/CD Pipeline / graph benchmark (ADR-130 P6) (push) Has been skipped
V3 CI/CD Pipeline / statusline generator delegation smoke (#2195) (push) Failing after 1s
V3 CI/CD Pipeline / wizard init regression guard (#2206 (push) Failing after 1s
V3 CI/CD Pipeline / memory no-stray-db smoke (ADR-125 P7) (push) Failing after 1s
V3 CI/CD Pipeline / github-safe injection smoke (#2089, ADR-127 Phase 1) (push) Failing after 1s
V3 CI/CD Pipeline / github actions pin smoke (#2089, ADR-127 Phase 1) (push) Failing after 1s
V3 CI/CD Pipeline / github attribution opt-in smoke (#2089, ADR-127 Phase 4) (push) Failing after 1s
V3 CI/CD Pipeline / pre-bash hook safety smoke (#2017) (push) Failing after 1s
V3 CI/CD Pipeline / Memory import smoke / ubuntu-latest (push) Failing after 0s
V3 CI/CD Pipeline / MCP protocol smoke / ubuntu-latest (push) Failing after 2s
V3 CI/CD Pipeline / ruvllm WASM auto-init smoke (#2086) (push) Failing after 4s
V3 CI/CD Pipeline / MCP paired-tool round-trip smoke (#1889) (push) Failing after 1s
V3 CI/CD Pipeline / Plugin package install-safety (#1902/#1903/#1904) (push) Failing after 1s
V3 CI/CD Pipeline / Tool description discoverability (ADR-112) (push) Failing after 3s
V3 CI/CD Pipeline / CLI npx-install smoke (#1147 / (22) (push) Failing after 1s
V3 CI/CD Pipeline / CLI npx-install smoke (#1147 / (24) (push) Failing after 1s
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / ubuntu-latest (push) Failing after 2s
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / ubuntu-latest (push) Failing after 1s
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / ubuntu-latest (push) Failing after 1s
V3 CI/CD Pipeline / Vector-index dimension audit (#1947) (push) Failing after 0s
V3 CI/CD Pipeline / Hook-command install safety (#1921) (push) Failing after 1s
V3 CI/CD Pipeline / ToolOutputGuardrail smoke (ADR-131, (push) Failing after 1s
V3 CI/CD Pipeline / init-bundle invariants smoke (#2095, ADR-128 Phase 5) (push) Failing after 1s
V3 CI/CD Pipeline / wasm provider bridge smoke (ADR-129 P1) (push) Failing after 2s
V3 CI/CD Pipeline / wasm gallery CRUD smoke (ADR-129 P3) (push) Failing after 1s
V3 CI/CD Pipeline / wasm plugin bridge smoke (ADR-129 P4) (push) Failing after 0s
V3 CI/CD Pipeline / wasm compose smoke (ADR-129 P2) (push) Failing after 4s
V3 CI/CD Pipeline / graph schema smoke (ADR-130 P1) (push) Failing after 0s
Validate Marketplace / validate (push) Failing after 1s
🔍 Verification Pipeline / 🚀 Setup Verification (push) Failing after 1s
🔍 Verification Pipeline / 🛡️ Security Verification (push) Has been skipped
🔍 Verification Pipeline / 📝 Code Quality (push) Has been skipped
🔍 Verification Pipeline / 🧪 Test Verification (${{ matrix.os }}, Node ${{ matrix.node }}) (push) Has been skipped
🔍 Verification Pipeline / 🏗️ Build Verification (push) Has been skipped
🔍 Verification Pipeline / 📚 Documentation Verification (push) Has been skipped
🔍 Verification Pipeline / ⚡ Performance Verification (push) Waiting to run
🔍 Verification Pipeline / 📊 Verification Report (push) Waiting to run
CVE Audit Gate / High-severity report (warn only) (push) Has been cancelled
343 lines
13 KiB
JavaScript
343 lines
13 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* benchmark-multiagent.mjs — Multi-agent task-completion benchmark suite
|
||
*
|
||
* Implements ADR-163 (Dream Cycle 2026-06-20, issue #2427, PR #2428).
|
||
* Mirrors the structure of `benchmark-intelligence.mjs` so output is
|
||
* reproducible and machine-readable.
|
||
*
|
||
* MOTIVATION (from ADR-163)
|
||
* LangGraph / AutoGen / CrewAI publish task-completion-rate benchmarks
|
||
* (62 / 58 / 54 % on a 2,000-run independent 2026 benchmark). Ruflo
|
||
* has no comparable published number. This blocks data-driven tuning
|
||
* of the 3-tier routing thresholds and creates a credibility gap.
|
||
*
|
||
* WHAT THIS MEASURES (per run)
|
||
* - Task completion (pass/fail per the task's success criterion)
|
||
* - Wall-clock time (ms)
|
||
* - Estimated token count (input + output)
|
||
* - Estimated cost at standard API rates
|
||
*
|
||
* BACKENDS
|
||
* --backend mock — synthetic deterministic runner. No LLM calls.
|
||
* Use for: CI smoke, pipeline validation, $0 cost.
|
||
* --backend ruflo — real ruflo CLI invocation per task. Costs real $.
|
||
* Use for: publishable numbers. Gate behind explicit
|
||
* --confirm to prevent surprise bills (~$50-75 for full sweep).
|
||
*
|
||
* TASK CORPUS (5 tasks, matching the structure of the LangGraph/AutoGen/CrewAI
|
||
* independent 2026 benchmark)
|
||
* T1: Code generation single-agent Tier-2 routing
|
||
* T2: Multi-file refactor hierarchical 3 agents
|
||
* T3: Research synthesis mesh 4 agents
|
||
* T4: Security audit specialized reviewer + auditor
|
||
* T5: End-to-end feature full pipeline architect→coder→tester→reviewer
|
||
*
|
||
* RUN SHAPE
|
||
* --runs N — runs per task (default 1 = smoke; ADR-163 specifies 100 for full)
|
||
* --tasks A,B,C — subset of {T1,T2,T3,T4,T5} (default: all)
|
||
*
|
||
* USAGE
|
||
* # smoke (default — mock backend, 1 run/task, no LLM cost)
|
||
* node scripts/benchmark-multiagent.mjs
|
||
*
|
||
* # full publishable sweep (ADR-163: 100 runs × 5 tasks × Tier-3 ~= $50-75)
|
||
* node scripts/benchmark-multiagent.mjs --backend ruflo --runs 100 --confirm
|
||
*
|
||
* # subset
|
||
* node scripts/benchmark-multiagent.mjs --tasks T1,T4 --runs 10
|
||
*
|
||
* EXIT CODES
|
||
* 0 ran to completion (any task pass/fail counts as completion)
|
||
* 1 --alert-on-pass-rate-below threshold breached
|
||
* 2 config error or runner crash
|
||
*
|
||
* Created for the ruflo perf benchmark suite (ADR-163).
|
||
* Co-Authored-By: RuFlo <ruv@ruv.net>
|
||
*/
|
||
|
||
import { fileURLToPath } from 'node:url';
|
||
import path from 'node:path';
|
||
import fs from 'node:fs';
|
||
import { spawnSync } from 'node:child_process';
|
||
|
||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||
const REPO_ROOT = path.resolve(__dirname, '..');
|
||
|
||
// ─── Task corpus (mirrors ADR-163 §"Benchmark design") ────────────────────
|
||
|
||
const TASKS = [
|
||
{
|
||
id: 'T1',
|
||
title: 'Code generation',
|
||
type: 'single-agent',
|
||
tier: 2,
|
||
expectedSwarmSize: 1,
|
||
successCriterion: 'Correct output, ≤2 retries',
|
||
// For mock backend — deterministic per-task expected behavior
|
||
mock: { passRate: 0.78, avgLatencyMs: 2100, avgTokens: 1450, avgCostUsd: 0.0029 },
|
||
},
|
||
{
|
||
id: 'T2',
|
||
title: 'Multi-file refactor',
|
||
type: 'hierarchical',
|
||
tier: 3,
|
||
expectedSwarmSize: 3,
|
||
successCriterion: 'All target files modified, tests pass',
|
||
mock: { passRate: 0.66, avgLatencyMs: 18500, avgTokens: 9800, avgCostUsd: 0.0294 },
|
||
},
|
||
{
|
||
id: 'T3',
|
||
title: 'Research synthesis',
|
||
type: 'mesh',
|
||
tier: 3,
|
||
expectedSwarmSize: 4,
|
||
successCriterion: '≥5 cited sources, coherent output',
|
||
mock: { passRate: 0.71, avgLatencyMs: 12200, avgTokens: 7400, avgCostUsd: 0.0222 },
|
||
},
|
||
{
|
||
id: 'T4',
|
||
title: 'Security audit',
|
||
type: 'specialized',
|
||
tier: 3,
|
||
expectedSwarmSize: 2,
|
||
successCriterion: '≥3 findings categorized',
|
||
mock: { passRate: 0.83, avgLatencyMs: 8400, avgTokens: 5200, avgCostUsd: 0.0156 },
|
||
},
|
||
{
|
||
id: 'T5',
|
||
title: 'End-to-end feature',
|
||
type: 'full-pipeline',
|
||
tier: 3,
|
||
expectedSwarmSize: 4,
|
||
successCriterion: 'Feature works + tests green',
|
||
mock: { passRate: 0.59, avgLatencyMs: 32100, avgTokens: 16200, avgCostUsd: 0.0486 },
|
||
},
|
||
];
|
||
|
||
// ─── CLI arg parsing ──────────────────────────────────────────────────────
|
||
|
||
const ARGS = (() => {
|
||
const a = {
|
||
backend: 'mock',
|
||
runs: 1,
|
||
tasks: null, // null = all
|
||
confirm: false,
|
||
alertPassRateBelow: null,
|
||
seed: 42,
|
||
jsonOnly: false,
|
||
outDir: path.join(REPO_ROOT, 'docs/benchmarks/multi-agent'),
|
||
};
|
||
for (let i = 2; i < process.argv.length; i++) {
|
||
const v = process.argv[i];
|
||
if (v === '--backend') a.backend = process.argv[++i];
|
||
else if (v === '--runs') a.runs = parseInt(process.argv[++i], 10);
|
||
else if (v === '--tasks') a.tasks = process.argv[++i].split(',').map(t => t.trim().toUpperCase());
|
||
else if (v === '--confirm') a.confirm = true;
|
||
else if (v === '--alert-on-pass-rate-below') a.alertPassRateBelow = parseFloat(process.argv[++i]);
|
||
else if (v === '--seed') a.seed = parseInt(process.argv[++i], 10);
|
||
else if (v === '--json-only') a.jsonOnly = true;
|
||
else if (v === '--out-dir') a.outDir = process.argv[++i];
|
||
}
|
||
return a;
|
||
})();
|
||
|
||
// ─── Safety: real backend requires explicit --confirm ────────────────────
|
||
|
||
if (ARGS.backend === 'ruflo' && !ARGS.confirm) {
|
||
console.error('benchmark-multiagent: --backend ruflo requires --confirm.');
|
||
console.error(' Full sweep (100 runs × 5 tasks) costs ~$50-75 at Tier-3 rates.');
|
||
console.error(' Pass --confirm to proceed, or use --backend mock for $0 smoke.');
|
||
process.exit(2);
|
||
}
|
||
|
||
if (!['mock', 'ruflo'].includes(ARGS.backend)) {
|
||
console.error(`benchmark-multiagent: --backend must be mock|ruflo (got: ${ARGS.backend})`);
|
||
process.exit(2);
|
||
}
|
||
|
||
const selectedTasks = ARGS.tasks
|
||
? TASKS.filter(t => ARGS.tasks.includes(t.id))
|
||
: TASKS;
|
||
|
||
if (selectedTasks.length === 0) {
|
||
console.error(`benchmark-multiagent: --tasks ${ARGS.tasks?.join(',')} matched no tasks`);
|
||
console.error(` valid: ${TASKS.map(t => t.id).join(', ')}`);
|
||
process.exit(2);
|
||
}
|
||
|
||
// ─── Deterministic seeded RNG (matches benchmark-intelligence pattern) ────
|
||
|
||
function makeRng(seed) {
|
||
let state = seed >>> 0;
|
||
return () => {
|
||
// xorshift32
|
||
state ^= state << 13;
|
||
state ^= state >>> 17;
|
||
state ^= state << 5;
|
||
return (state >>> 0) / 0xffffffff;
|
||
};
|
||
}
|
||
|
||
// ─── Backends ─────────────────────────────────────────────────────────────
|
||
|
||
/**
|
||
* Mock backend — deterministic synthetic runner. No LLM calls.
|
||
*
|
||
* Uses each task's `mock.passRate` + jitter to produce a Bernoulli
|
||
* pass/fail and lightly perturbed latency/tokens/cost figures so the
|
||
* output looks like a real benchmark but costs $0 and runs in ms.
|
||
*
|
||
* Use case: CI smoke, pipeline shake-down, regression gate against
|
||
* benchmark-infrastructure breakage.
|
||
*/
|
||
function runOneMock(task, rng) {
|
||
const passed = rng() < task.mock.passRate;
|
||
const jitter = (mean, frac = 0.15) => Math.max(0, mean * (1 + (rng() - 0.5) * 2 * frac));
|
||
return {
|
||
passed,
|
||
latencyMs: Math.round(jitter(task.mock.avgLatencyMs)),
|
||
tokens: Math.round(jitter(task.mock.avgTokens)),
|
||
costUsd: Number(jitter(task.mock.avgCostUsd).toFixed(4)),
|
||
failureReason: passed ? null : 'mock-bernoulli',
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Ruflo backend — real CLI invocation. Spawns `node bin/cli.js task <T#>`
|
||
* (would need a task-runner binding — for now, this is stubbed and will
|
||
* exit 2 with a clear "not implemented" until the orchestration bridge is wired).
|
||
*
|
||
* This is the path to publishable numbers but requires:
|
||
* (a) a `ruflo bench-task <id>` subcommand that runs a single task end-to-end
|
||
* (b) cost tracking (which we already have via cost-tracker plugin)
|
||
* (c) explicit user --confirm because $50-75 per full sweep
|
||
*
|
||
* Tracked as follow-up — out of scope for the smoke-validation PR that
|
||
* lands this script. Until wired, --backend ruflo prints a clear error
|
||
* and exits 2 so CI can opt out cleanly.
|
||
*/
|
||
function runOneRuflo(_task, _rng) {
|
||
console.error('benchmark-multiagent: --backend ruflo is not yet wired to a real');
|
||
console.error(' task runner. The infrastructure (cost-tracker, hooks_route,');
|
||
console.error(' swarm_init) exists but needs a `ruflo bench-task <id>` subcommand');
|
||
console.error(' to drive it from this script. Tracked as a follow-up to ADR-163.');
|
||
console.error(' For now, use --backend mock for the pipeline smoke.');
|
||
process.exit(2);
|
||
}
|
||
|
||
const runOne = ARGS.backend === 'mock' ? runOneMock : runOneRuflo;
|
||
|
||
// ─── Main loop ────────────────────────────────────────────────────────────
|
||
|
||
function main() {
|
||
const startedAt = new Date().toISOString();
|
||
const rng = makeRng(ARGS.seed);
|
||
|
||
const perTask = [];
|
||
for (const task of selectedTasks) {
|
||
const runs = [];
|
||
for (let i = 0; i < ARGS.runs; i++) {
|
||
runs.push(runOne(task, rng));
|
||
}
|
||
const passes = runs.filter(r => r.passed).length;
|
||
const passRate = passes / runs.length;
|
||
const avg = (key) => runs.reduce((s, r) => s + r[key], 0) / runs.length;
|
||
|
||
perTask.push({
|
||
task: task.id,
|
||
title: task.title,
|
||
type: task.type,
|
||
runs: runs.length,
|
||
passes,
|
||
passRate,
|
||
avgLatencyMs: Math.round(avg('latencyMs')),
|
||
avgTokens: Math.round(avg('tokens')),
|
||
avgCostUsd: Number(avg('costUsd').toFixed(4)),
|
||
totalCostUsd: Number((avg('costUsd') * runs.length).toFixed(4)),
|
||
});
|
||
}
|
||
|
||
const overall = {
|
||
totalRuns: perTask.reduce((s, t) => s + t.runs, 0),
|
||
totalPasses: perTask.reduce((s, t) => s + t.passes, 0),
|
||
overallPassRate: perTask.reduce((s, t) => s + t.passes, 0) / perTask.reduce((s, t) => s + t.runs, 0),
|
||
totalCostUsd: Number(perTask.reduce((s, t) => s + t.totalCostUsd, 0).toFixed(4)),
|
||
avgLatencyMs: Math.round(perTask.reduce((s, t) => s + t.avgLatencyMs, 0) / perTask.length),
|
||
};
|
||
|
||
const report = {
|
||
schemaVersion: 'ruflo.multiagent.v1',
|
||
adr: 'ADR-163',
|
||
backend: ARGS.backend,
|
||
seed: ARGS.seed,
|
||
runsPerTask: ARGS.runs,
|
||
startedAt,
|
||
finishedAt: new Date().toISOString(),
|
||
perTask,
|
||
overall,
|
||
// ADR-163 §"Target": ≥65 % overall to beat LangGraph's 62 %
|
||
targets: {
|
||
langGraph: { passRate: 0.62, costPerTask: 0.08, source: 'Independent 2026 benchmark, Grade B' },
|
||
autoGen: { passRate: 0.58, costPerTask: 0.10, source: 'Same' },
|
||
crewAi: { passRate: 0.54, costPerTask: 0.12, source: 'Same' },
|
||
rufloTarget: { passRate: 0.65 },
|
||
},
|
||
note: ARGS.backend === 'mock'
|
||
? 'MOCK backend — synthetic deterministic Bernoulli. No LLM calls. Use --backend ruflo --confirm for publishable numbers.'
|
||
: 'REAL backend — values reflect actual measured runs.',
|
||
};
|
||
|
||
// Write artifact
|
||
if (!fs.existsSync(ARGS.outDir)) fs.mkdirSync(ARGS.outDir, { recursive: true });
|
||
const stamp = startedAt.replace(/[:.]/g, '-');
|
||
const artifactPath = path.join(ARGS.outDir, `multiagent-${ARGS.backend}-${stamp}.json`);
|
||
fs.writeFileSync(artifactPath, JSON.stringify(report, null, 2));
|
||
|
||
// Pretty-print
|
||
if (!ARGS.jsonOnly) {
|
||
console.log('');
|
||
console.log(`## Multi-agent benchmark — ${ARGS.backend} backend, ${ARGS.runs} run(s)/task, seed ${ARGS.seed}`);
|
||
console.log('');
|
||
console.log('| Task | Title | Pass | Pass-rate | Latency | Tokens | Cost USD |');
|
||
console.log('|------|-------|-----:|----------:|--------:|-------:|---------:|');
|
||
for (const t of perTask) {
|
||
console.log(`| ${t.task} | ${t.title} | ${t.passes}/${t.runs} | ${(t.passRate * 100).toFixed(1)}% | ${t.avgLatencyMs} ms | ${t.avgTokens} | $${t.avgCostUsd.toFixed(4)} |`);
|
||
}
|
||
console.log('');
|
||
console.log(`**Overall pass-rate: ${(overall.overallPassRate * 100).toFixed(1)}%** | Total cost: $${overall.totalCostUsd} | Total runs: ${overall.totalRuns}`);
|
||
console.log('');
|
||
console.log('**Comparison vs published 2026 competitor numbers:**');
|
||
console.log('| Framework | Pass-rate | Cost/Task | Source |');
|
||
console.log('|---|---:|---:|---|');
|
||
console.log(`| LangGraph | 62.0% | $0.08 | Independent 2026 benchmark, Grade B |`);
|
||
console.log(`| AutoGen | 58.0% | ~$0.10 | Same |`);
|
||
console.log(`| CrewAI | 54.0% | ~$0.12 | Same |`);
|
||
const ourCost = perTask.length > 0 ? (overall.totalCostUsd / overall.totalRuns).toFixed(4) : 'n/a';
|
||
console.log(`| **Ruflo (this run, ${ARGS.backend})** | **${(overall.overallPassRate * 100).toFixed(1)}%** | **$${ourCost}** | This run |`);
|
||
console.log('');
|
||
console.log(`Artifact: ${path.relative(REPO_ROOT, artifactPath)}`);
|
||
if (ARGS.backend === 'mock') {
|
||
console.log('');
|
||
console.log('> ⚠️ MOCK backend — synthetic Bernoulli, NOT publishable.');
|
||
console.log('> Run with --backend ruflo --runs 100 --confirm for publishable numbers (~$50-75).');
|
||
}
|
||
}
|
||
|
||
console.log('');
|
||
console.log('===BENCH_JSON===');
|
||
console.log(JSON.stringify(report));
|
||
|
||
if (ARGS.alertPassRateBelow !== null && overall.overallPassRate < ARGS.alertPassRateBelow) {
|
||
process.exit(1);
|
||
}
|
||
process.exit(0);
|
||
}
|
||
|
||
try {
|
||
main();
|
||
} catch (e) {
|
||
console.error(`benchmark-multiagent: ${e?.message ?? e}`);
|
||
process.exit(2);
|
||
}
|