Files
wehub-resource-sync 23f7624596
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 / 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
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
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
CVE Audit Gate / High-severity report (warn only) (push) Has been cancelled
🔄 Automated Rollback Manager / 🔄 Execute Rollback (push) Has been cancelled
🔄 Automated Rollback Manager / ✅ Post-Rollback Verification (push) Has been cancelled
🔄 Automated Rollback Manager / 📊 Rollback Monitoring (push) Has been cancelled
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / windows-latest (push) Has been cancelled
🔄 Automated Rollback Manager / ⏳ Manual Rollback Approval (push) Has been cancelled
V3 CI/CD Pipeline / MCP protocol smoke / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Memory import smoke / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / ubuntu-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Publish to npm (alpha) (push) Has been cancelled
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / macos-latest / Node 22 (push) Has been cancelled
V3 CI/CD Pipeline / Plugin hooks smoke / macos-latest / Node 22 (push) Has been cancelled
CI/CD Pipeline / Deploy & Release (push) Has been cancelled
CI/CD Pipeline / CI Status (push) Has been cancelled
🔗 Cross-Agent Integration Tests / 📊 Integration Test Report (push) Has been cancelled
🔄 Automated Rollback Manager / 🔍 Pre-Rollback Validation (push) Has been cancelled
🔍 Verification Pipeline / ⚡ Performance Verification (push) Has been cancelled
🔍 Verification Pipeline / 📊 Verification Report (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:02:19 +08:00

405 lines
17 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
// evolve.mjs — wrapper around `metaharness-darwin evolve <repo>`.
//
// ADR-153: Darwin Mode is the WRITE layer that closes the loop ADR-150's
// READ layer (score / genome / mcp-scan / threat-model / oia-audit) opens.
// score+genome tell you where the harness IS; darwin evolve tells you which
// mutation makes it provably better, without retraining the foundation model.
//
// SAFETY (ADR-153 §"Safety model"):
// - Variant generation + sandbox happen under <repo>/.metaharness/variants/,
// never in the repo root. Upstream `inspectVariant()` rejects nested dirs,
// symlinks, secret-shaped strings, shell-out / network / dynamic-eval
// before any variant runs. Exit code 99 is reserved for "safety-disqualified".
// - --confirm is REQUIRED. Without it the script prints a plan and exits 0
// (mirrors mint.mjs convention). This is in addition to upstream's safety
// layer — defense in depth at the ruflo boundary.
// - Default --generations 3 --children 3 (small) — anything larger is opt-in.
// Real evolutions are minutes-to-hours; ruflo's default sandbox config
// errs toward "show me the mechanism works" over "find a winner today".
//
// USAGE
// node scripts/evolve.mjs --repo . # dry-run plan
// node scripts/evolve.mjs --repo . --confirm # actually evolve
// node scripts/evolve.mjs --repo . --confirm --generations 5 --children 3
// node scripts/evolve.mjs --repo . --confirm --sandbox mock # no real tests
// node scripts/evolve.mjs --repo . --confirm --selection pareto
// node scripts/evolve.mjs --repo . --confirm --diagnose # + GEPA failure diagnosis
//
// --diagnose (GEPA failure diagnosis — the natural-language-diagnosis trick
// from GEPA, scoped modestly): after the evolve run completes, the losing /
// failed variants' transcripts are run through darwin's gepa
// `analyzeTranscript` + `classifyFailure` ops and a `diagnosis` section
// (failure classes + counts + dominant class per variant) is appended to the
// emitted JSON report. UPSTREAM SHAPE NOTE (verified against darwin 0.8.0):
// `metaharness-darwin evolve --json` emits a TEXT leaderboard on stdout — no
// JSON, no transcripts. The per-variant run records live at
// `<repo>/.metaharness/runs/<id>.json` as sandbox exec traces
// ({taskId, exitCode, stdout, stderr}), which are NOT gepa {actionRaw, obs}
// transcripts. So the diagnosis layer (a) uses gepa-shaped transcripts when
// a run record embeds them (agent-sandbox / future upstream), (b) falls back
// to the champion's transcript, and (c) otherwise emits
// `diagnosis: {available: false, reason, traceSummary}` with a mechanical
// per-variant trace summary — it NEVER fails the run.
//
// EXIT CODES
// 0 evolved OK (or dry-run, or degraded — MetaHarness Darwin not available)
// 1 --alert-on-no-improvement and champion did not beat parent
// 2 config error or evolution failure
// 99 reserved — upstream "safety-disqualified" (propagated)
import { runDarwinAsync, emitDarwinDegradedJsonAndExit, importGepa } from './_darwin.mjs';
import { existsSync, readdirSync, readFileSync } from 'node:fs';
import { join, resolve } from 'node:path';
const ARGS = (() => {
const a = {
repo: '.',
generations: 3,
children: 3,
concurrency: 2,
seed: null,
sandbox: 'real',
selection: null,
crossover: false,
epistasis: false,
curriculum: false,
riskBudget: null,
fdr: null,
tie: null,
bench: null,
mutator: 'deterministic',
ruvllmUrl: null,
ruvllmModel: null,
confirm: false,
alertOnNoImprovement: false,
diagnose: false,
format: 'json',
timeoutMs: null, // computed below if unset
};
for (let i = 2; i < process.argv.length; i++) {
const v = process.argv[i];
if (v === '--repo') a.repo = process.argv[++i];
else if (v === '--generations') a.generations = parseInt(process.argv[++i], 10);
else if (v === '--children') a.children = parseInt(process.argv[++i], 10);
else if (v === '--concurrency') a.concurrency = parseInt(process.argv[++i], 10);
else if (v === '--seed') a.seed = parseInt(process.argv[++i], 10);
else if (v === '--sandbox') a.sandbox = process.argv[++i];
else if (v === '--selection') a.selection = process.argv[++i];
else if (v === '--crossover') a.crossover = true;
else if (v === '--epistasis') a.epistasis = true;
else if (v === '--curriculum') a.curriculum = true;
else if (v === '--risk-budget') a.riskBudget = parseInt(process.argv[++i], 10);
else if (v === '--fdr') a.fdr = parseFloat(process.argv[++i]);
else if (v === '--tie') a.tie = process.argv[++i];
else if (v === '--bench') a.bench = process.argv[++i];
else if (v === '--mutator') a.mutator = process.argv[++i];
else if (v === '--ruvllm-url') a.ruvllmUrl = process.argv[++i];
else if (v === '--ruvllm-model') a.ruvllmModel = process.argv[++i];
else if (v === '--confirm') a.confirm = true;
else if (v === '--alert-on-no-improvement') a.alertOnNoImprovement = true;
else if (v === '--diagnose') a.diagnose = true;
else if (v === '--format') a.format = process.argv[++i];
else if (v === '--timeout-ms') a.timeoutMs = parseInt(process.argv[++i], 10);
}
return a;
})();
function safetyChecks() {
const repoPath = resolve(ARGS.repo);
if (!existsSync(repoPath)) {
console.error(`evolve: repo path does not exist: ${repoPath}`);
process.exit(2);
}
if (ARGS.generations < 1 || ARGS.generations > 50) {
console.error('evolve: --generations must be 1..50 (ruflo cap; upstream supports more)');
process.exit(2);
}
if (ARGS.children < 1 || ARGS.children > 20) {
console.error('evolve: --children must be 1..20 (ruflo cap)');
process.exit(2);
}
if (ARGS.concurrency < 1 || ARGS.concurrency > 8) {
console.error('evolve: --concurrency must be 1..8 (ruflo cap)');
process.exit(2);
}
if (!['real', 'mock', 'agent'].includes(ARGS.sandbox)) {
console.error(`evolve: --sandbox must be real|mock|agent (got: ${ARGS.sandbox})`);
process.exit(2);
}
if (ARGS.selection && !['quality-diversity', 'behavioral-diversity', 'niche-steering', 'clade', 'pareto'].includes(ARGS.selection)) {
console.error(`evolve: --selection must be one of quality-diversity|behavioral-diversity|niche-steering|clade|pareto`);
process.exit(2);
}
if (!['deterministic', 'ruvllm'].includes(ARGS.mutator)) {
console.error(`evolve: --mutator must be deterministic|ruvllm`);
process.exit(2);
}
return repoPath;
}
// ---------------------------------------------------------------------------
// --diagnose support (GEPA failure diagnosis)
// ---------------------------------------------------------------------------
/** True when `arr` looks like a gepa transcript: [{actionRaw?, obs?}, ...]. */
function looksLikeGepaTranscript(arr) {
return Array.isArray(arr) && arr.length > 0 &&
arr.every((e) => e && typeof e === 'object' && ('actionRaw' in e || 'obs' in e));
}
/**
* Pull every gepa-shaped transcript out of one run record. Upstream darwin
* 0.8.0 run records carry sandbox exec traces (NOT gepa transcripts), but a
* record MAY embed `transcript` arrays (agent sandbox / future upstream) —
* accept `record.transcript`, `record.traces[i].transcript`, or `traces`
* itself when its entries are {actionRaw, obs}-shaped.
*/
function extractGepaTranscripts(record) {
const out = [];
if (looksLikeGepaTranscript(record?.transcript)) {
out.push({ taskId: null, exitCode: null, entries: record.transcript });
}
const traces = Array.isArray(record?.traces) ? record.traces : [];
if (looksLikeGepaTranscript(traces)) {
out.push({ taskId: null, exitCode: null, entries: traces });
} else {
for (const t of traces) {
if (looksLikeGepaTranscript(t?.transcript)) {
out.push({ taskId: t.taskId ?? null, exitCode: t.exitCode ?? null, entries: t.transcript });
}
}
}
return out;
}
/** Mechanical per-variant summary of sandbox exec traces (the always-available fallback signal). */
function summarizeTraces(record) {
const traces = Array.isArray(record?.traces) ? record.traces : [];
return {
tasks: traces.length,
failed: traces.filter((t) => t?.exitCode !== 0).length,
timedOut: traces.filter((t) => t?.timedOut === true).length,
blockedActions: traces.reduce((n, t) => n + (Array.isArray(t?.blockedActions) ? t.blockedActions.length : 0), 0),
};
}
/**
* Build the `diagnosis` section for the emitted report. Never throws and
* never affects the run's exit code — any internal failure degrades to
* `{available: false, reason}`.
*/
async function buildDiagnosis(repoPath) {
try {
const metaDir = join(repoPath, '.metaharness');
const runsDir = join(metaDir, 'runs');
if (!existsSync(runsDir)) {
return { available: false, reason: 'no-run-records: <repo>/.metaharness/runs does not exist' };
}
let winnerId = null;
try {
winnerId = JSON.parse(readFileSync(join(metaDir, 'reports', 'winner.json'), 'utf-8'))?.variant?.id ?? null;
} catch { /* winner unknown — treat all variants as candidates */ }
const records = [];
for (const f of readdirSync(runsDir).filter((f) => f.endsWith('.json')).slice(0, 100)) {
try {
records.push({ id: f.replace(/\.json$/, ''), rec: JSON.parse(readFileSync(join(runsDir, f), 'utf-8')) });
} catch { /* skip unreadable record */ }
}
if (records.length === 0) {
return { available: false, reason: 'no-run-records: <repo>/.metaharness/runs contains no parseable records' };
}
// Losing/failed variants are the primary diagnosis targets; the champion
// is the fallback when no loser exposes a transcript.
const losers = records.filter((r) =>
r.id !== winnerId &&
(r.rec?.score?.promoted === false ||
(Array.isArray(r.rec?.traces) && r.rec.traces.some((t) => t?.exitCode !== 0 || t?.timedOut))));
const champion = records.filter((r) => r.id === winnerId);
let scope = 'losing-variants';
let pool = losers.map((r) => ({ ...r, transcripts: extractGepaTranscripts(r.rec) }))
.filter((r) => r.transcripts.length > 0);
if (pool.length === 0) {
scope = 'champion-fallback';
pool = champion.map((r) => ({ ...r, transcripts: extractGepaTranscripts(r.rec) }))
.filter((r) => r.transcripts.length > 0);
}
if (pool.length === 0) {
// Verified upstream shape (darwin 0.8.0): run records are sandbox exec
// traces, not gepa transcripts. Emit the mechanical trace summary so
// --diagnose still yields signal.
const target = losers.length ? losers : records;
return {
available: false,
reason: 'no-gepa-transcripts: run records contain sandbox exec traces ({taskId, exitCode, stdout, stderr}), not gepa {actionRaw, obs} transcripts',
traceSummary: Object.fromEntries(target.map((r) => [r.id, summarizeTraces(r.rec)])),
};
}
const gepa = await importGepa();
if (!gepa || typeof gepa.analyzeTranscript !== 'function') {
return { available: false, reason: 'metaharness-darwin-gepa-not-available' };
}
const classLabel = (n) => {
const raw = gepa.FAILURE_CLASSES?.[n];
return raw ? String(raw).split(' (')[0] : `class-${n}`;
};
const totals = {};
const variants = [];
for (const r of pool) {
const classes = {};
for (const t of r.transcripts) {
const analysis = gepa.analyzeTranscript(t.entries);
const cls = typeof gepa.classifyFailure === 'function'
? gepa.classifyFailure({ goldResolved: t.exitCode === 0, analysis })
: -1;
const label = classLabel(cls);
classes[label] = (classes[label] || 0) + 1;
totals[label] = (totals[label] || 0) + 1;
}
const dominantClass = Object.entries(classes).sort((a, b) => b[1] - a[1])[0]?.[0] ?? null;
variants.push({ id: r.id, transcripts: r.transcripts.length, failureClasses: classes, dominantClass });
}
return { available: true, scope, variants, totals };
} catch (e) {
return { available: false, reason: `diagnosis-failed: ${e?.message ?? e}` };
}
}
// Compute a sensible timeout from the search shape if caller didn't specify.
// Rough budget: each variant ~= 30s (sandbox test command + safety inspect),
// total ~= generations × children × per-variant / concurrency.
function defaultTimeoutMs() {
const perVariantMs = ARGS.sandbox === 'mock' ? 2_000 : 60_000;
const variants = ARGS.generations * ARGS.children;
const parallelism = Math.min(ARGS.concurrency, variants);
const wall = Math.ceil(variants / parallelism) * perVariantMs;
// Add 30s overhead for npm install + initial profile + final report.
return Math.max(60_000, wall + 30_000);
}
async function main() {
const repoPath = safetyChecks();
const plan = {
binary: 'metaharness-darwin evolve',
repo: repoPath,
generations: ARGS.generations,
children: ARGS.children,
concurrency: ARGS.concurrency,
sandbox: ARGS.sandbox,
selection: ARGS.selection,
crossover: ARGS.crossover,
epistasis: ARGS.epistasis,
curriculum: ARGS.curriculum,
mutator: ARGS.mutator,
diagnose: ARGS.diagnose,
estVariants: ARGS.generations * ARGS.children,
timeoutMs: ARGS.timeoutMs ?? defaultTimeoutMs(),
output: `${repoPath}/.metaharness/{archive.json, lineage.json, variants/, runs/, reports/winner.json}`,
};
if (!ARGS.confirm) {
const payload = {
success: true,
data: { plan, dryRun: true, message: 'Pass --confirm to run the evolution.' },
generatedAt: new Date().toISOString(),
};
console.log(JSON.stringify(payload, null, 2));
process.exit(0);
}
const cliArgs = ['evolve', repoPath,
'--generations', String(ARGS.generations),
'--children', String(ARGS.children),
'--concurrency', String(ARGS.concurrency),
'--sandbox', ARGS.sandbox,
'--mutator', ARGS.mutator,
];
if (ARGS.seed != null) cliArgs.push('--seed', String(ARGS.seed));
if (ARGS.selection) cliArgs.push('--selection', ARGS.selection);
if (ARGS.crossover) cliArgs.push('--crossover');
if (ARGS.epistasis) cliArgs.push('--epistasis');
if (ARGS.curriculum) cliArgs.push('--curriculum');
if (ARGS.riskBudget != null) cliArgs.push('--risk-budget', String(ARGS.riskBudget));
if (ARGS.fdr != null) cliArgs.push('--fdr', String(ARGS.fdr));
if (ARGS.tie) cliArgs.push('--tie', ARGS.tie);
if (ARGS.bench) cliArgs.push('--bench', ARGS.bench);
if (ARGS.ruvllmUrl) cliArgs.push('--ruvllm-url', ARGS.ruvllmUrl);
if (ARGS.ruvllmModel) cliArgs.push('--ruvllm-model', ARGS.ruvllmModel);
// Forward progress lines to stderr so the user sees per-generation activity
// (subprocess-of-an-MCP-tool case: this still surfaces in the agent log).
const r = await runDarwinAsync(cliArgs, {
timeoutMs: plan.timeoutMs,
onProgress: (line) => { if (line.trim()) process.stderr.write(`[evolve] ${line}\n`); },
});
if (r.degraded) {
emitDarwinDegradedJsonAndExit(r.reason);
return;
}
// Upstream exit code 99 = safety-disqualified — propagate verbatim so
// CI gates can distinguish "evolution failed" from "evolution surfaced
// a safety-tripping mutation". This is a designed-in tripwire, not an
// error the ruflo layer should remap.
if (r.exitCode === 99) {
const payload = {
success: false,
data: { safetyDisqualified: true, hint: 'A variant tripped the safety inspection layer. See <repo>/.metaharness/runs/ for which surface and pattern.' },
stderrTail: r.stderr.slice(-400),
durationMs: r.durationMs,
generatedAt: new Date().toISOString(),
};
console.log(JSON.stringify(payload, null, 2));
process.exit(99);
}
if (r.exitCode !== 0) {
const payload = {
success: false,
data: { exitCode: r.exitCode, stderrTail: r.stderr.slice(-400) },
generatedAt: new Date().toISOString(),
};
console.log(JSON.stringify(payload, null, 2));
process.exit(2);
}
const champion = r.json || {};
const noImprovement = champion.parentScore != null && champion.championScore != null &&
champion.championScore <= champion.parentScore;
const payload = {
success: true,
data: {
...champion,
plan,
durationMs: r.durationMs,
improved: !noImprovement,
},
generatedAt: new Date().toISOString(),
};
// GEPA failure diagnosis — opt-in, additive, never fails the run.
if (ARGS.diagnose) {
payload.data.diagnosis = await buildDiagnosis(repoPath);
}
console.log(JSON.stringify(payload, null, 2));
if (ARGS.alertOnNoImprovement && noImprovement) process.exit(1);
process.exit(0);
}
main().catch((e) => {
console.error(`evolve: unexpected failure: ${e?.message ?? e}`);
process.exit(2);
});