mirror of
https://github.com/proffesor-for-testing/agentic-qe.git
synced 2026-09-19 08:45:47 +08:00
0a8254f7a9
Adopts the receipt-gated self-improvement discipline from the 2026-07 cross-repo analysis of ruflo (flywheel), MetaHarness (gateReExecutes), and retort (two-gate spec conformance + DoE). All DI'd and unit-tested (178 tests); run end-to-end on real qe_patterns; every limitation honestly scoped. - ADR-117 frozen oracle anchor: hash-pinned, constant-denominator eval set that no promotion may regress; loader throws on drift (verification/anchors/qe-anchor-v1.json). - ADR-118 receipt-gated QE-policy flywheel: generational loop over qe_patterns, conjunctive frozen accept(), Ed25519-signed receipts + compounding lineage, durable persistence (flywheel_receipts), corpus + semantic-retriever coupling. - ADR-119 two-gate quality verdicts: mechanical gate + frontier judge, three-valued (pass/fail/inconclusive) with preflight; new `aqe quality-gate` CLI + `qe/quality/gate` MCP tool. - ADR-120 gateReExecutes: re-execute a versioned frozen rule on sealed inputs; catches the A8-EXT forged-promote class; retrofit onto pattern-promote + dream-apply. - ADR-121 provenance tiers: only oracle:test-exec evidence changes fleet behavior; additive, conservative-backfill migration. - ADR-122 DoE screen: model x prompt x retrieval x scaffold, run on real data (25/27 cells, $1.25) — scaffolding (esp. reflexion) and retrieval degrade reliability (the retort `beads` signature); simplest+cheapest stack wins. - safe-db: AQE_DISABLE_WAL / AQE_JOURNAL_MODE env-gate (macOS virtiofs bind-mount WAL corruption mitigation; default behavior unchanged). Honest nulls documented: the flywheel lift is on a self-supervised proxy with the anchor held flat; making the coupled anchor move needs harder items + a relevant corpus (an ADR-117 re-freeze), not a retriever change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
323 lines
14 KiB
JavaScript
323 lines
14 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* ADR-122 designed-experiment (DoE) screen — DRY-RUN scaffold.
|
|
*
|
|
* WHAT THIS IS
|
|
* A pure-Node, zero-dependency, NO-NETWORK planner for the ADR-122 harness
|
|
* screen. It prints the factor model, the fractional-factorial design matrix,
|
|
* the replicate plan, and a token/cost estimate. It NEVER runs a live model
|
|
* and NEVER spends money.
|
|
*
|
|
* SAFETY MODEL (why this file is safe to run)
|
|
* - Default mode is DRY-RUN: print the design + cost estimate, then STOP.
|
|
* - `--smoke` exercises the whole pipeline (design -> run -> aggregate ->
|
|
* summarize) with a LocalStubRunner that returns deterministic $0 fake
|
|
* results. Nothing hits the network; total cost is provably $0. This mirrors
|
|
* retort's `LocalStubRunner` pattern (retort_metaharness/runner.py).
|
|
* - `--confirm-spend` is the human gate. Even WITH the flag this scaffold does
|
|
* NOT itself call a model — it refuses and prints the exact hand-off command
|
|
* for the real AQE/retort harness. Real spend happens only in that harness,
|
|
* after the user has seen and approved the dollar estimate below.
|
|
*
|
|
* GROUNDING
|
|
* Factor model + fraction/ANOVA approach mirror retort's DoE engine
|
|
* (retort/src/retort/design, retort_metaharness/{factors,design,runner}.py) and
|
|
* ADR-122. Response = pass-proportion over N replicates via the ADR-113 oracle
|
|
* (src/validation/oracle-eval.ts) whose grading is mutation-based (real
|
|
* `node --test`) and therefore costs $0 in tokens — only generation is metered.
|
|
*
|
|
* USAGE
|
|
* node scripts/doe-screen-scaffold.mjs # dry-run: design + cost, then STOP
|
|
* node scripts/doe-screen-scaffold.mjs --smoke # $0 pipeline smoke test (stub runner)
|
|
* node scripts/doe-screen-scaffold.mjs --full # cost the FULL factorial (no fraction)
|
|
* node scripts/doe-screen-scaffold.mjs --replicates 3 # override N replicates (default 5)
|
|
* node scripts/doe-screen-scaffold.mjs --confirm-spend # gate: refuses, prints hand-off command
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Factor model (ADR-122 §2; retort_metaharness/factors.py analog)
|
|
// A factor's `feature` flag marks it as a harness FEATURE under screen (per
|
|
// ADR-122 the gate admits a feature only on a significant, non-negative effect).
|
|
// model/prompt are context/blocking factors; retrieval/scaffold are the features
|
|
// whose reliability contribution is on trial (the "beads" question).
|
|
// ---------------------------------------------------------------------------
|
|
const FACTORS = {
|
|
model: {
|
|
feature: false,
|
|
levels: [
|
|
{ id: 'qwen3-coder:30b', label: 'local (Ollama)', priceIn: 0, priceOut: 0 },
|
|
{ id: 'haiku-4.5', label: 'cheap cloud', priceIn: 1.0, priceOut: 5.0 },
|
|
{ id: 'opus-4.8', label: 'frontier', priceIn: 5.0, priceOut: 25.0 },
|
|
],
|
|
},
|
|
prompt: {
|
|
feature: false,
|
|
levels: [{ id: 'neutral' }, { id: 'TDD' }, { id: 'ATDD' }],
|
|
},
|
|
retrieval: {
|
|
feature: true, // ADR-118 flywheel policy — the feature most analogous to `beads`
|
|
levels: [
|
|
{ id: 'off', inputMult: 1.0 },
|
|
{ id: 'on', inputMult: 1.2 }, // retrieved patterns add ~20% input context
|
|
],
|
|
},
|
|
scaffold: {
|
|
feature: true,
|
|
levels: [
|
|
{ id: 'none', passMult: 1.0 },
|
|
{ id: 'plan-and-solve', passMult: 1.3 },
|
|
{ id: 'reflexion', passMult: 2.0 }, // two-pass self-critique ~2x tokens
|
|
],
|
|
},
|
|
};
|
|
|
|
// Prices are per MILLION tokens (input / output). Grounded: Opus 4.8 = $5/$25
|
|
// (retort README, Opus 4.8 announcement); Haiku 4.5 ~= $1/$5; local = $0.
|
|
// Token assumptions per single test-generation run against one anchor fixture:
|
|
const TOKENS = {
|
|
baseIn: 8000, // fixture source + prompt + scaffold instructions (+retrieval)
|
|
baseOut: 4000, // generated tests + reasoning
|
|
};
|
|
|
|
const DEFAULT_REPLICATES = 5; // ADR-122 §2: pass-proportion needs N; single run is noise
|
|
const DEFAULT_FRACTION = 0.5; // half-fraction screen (Res IV target: mains clear of 2FI)
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Arg parsing (tiny, no deps)
|
|
// ---------------------------------------------------------------------------
|
|
function parseArgs(argv) {
|
|
const a = {
|
|
smoke: false, full: false, confirmSpend: false,
|
|
replicates: DEFAULT_REPLICATES, fraction: DEFAULT_FRACTION,
|
|
};
|
|
for (let i = 0; i < argv.length; i++) {
|
|
const t = argv[i];
|
|
if (t === '--smoke') a.smoke = true;
|
|
else if (t === '--full') { a.full = true; a.fraction = 1.0; }
|
|
else if (t === '--confirm-spend') a.confirmSpend = true;
|
|
else if (t === '--replicates') a.replicates = Number(argv[++i]);
|
|
else if (t === '--fraction') a.fraction = Number(argv[++i]);
|
|
else if (t === '--help' || t === '-h') a.help = true;
|
|
}
|
|
return a;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Design generation
|
|
// Full factorial = cartesian product of all factor levels.
|
|
// Fractional screen = deterministic balanced subset (sum-of-indices mod k).
|
|
// NOTE: this modular selector is a TRANSPARENT SCAFFOLD APPROXIMATION of a
|
|
// real resolution-IV fractional design. For the authoritative aliasing-aware
|
|
// matrix, generate it with retort:
|
|
// retort design generate --phase characterization --fraction 0.5 --config <factors>
|
|
// or retort-metaharness design ... --fraction 0.5
|
|
// and feed that CSV to the real runner. This scaffold's job is cost + pipeline
|
|
// shape, not the final aliasing structure.
|
|
// ---------------------------------------------------------------------------
|
|
function fullFactorial() {
|
|
const names = Object.keys(FACTORS);
|
|
let cells = [{}];
|
|
for (const name of names) {
|
|
const next = [];
|
|
FACTORS[name].levels.forEach((lvl, idx) => {
|
|
for (const partial of cells) next.push({ ...partial, [name]: idx });
|
|
});
|
|
cells = next;
|
|
}
|
|
return cells; // each cell = { model: idx, prompt: idx, retrieval: idx, scaffold: idx }
|
|
}
|
|
|
|
function fractionSelect(cells, fraction) {
|
|
if (fraction >= 1.0) return cells;
|
|
const k = Math.round(1 / fraction); // 0.5 -> keep 1 of every 2
|
|
const names = Object.keys(FACTORS);
|
|
return cells.filter((c) => {
|
|
const s = names.reduce((acc, n) => acc + c[n], 0);
|
|
return s % k === 0;
|
|
});
|
|
}
|
|
|
|
function describeCell(c) {
|
|
const names = Object.keys(FACTORS);
|
|
return names.map((n) => `${n}=${FACTORS[n].levels[c[n]].id}`).join(' ');
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Cost model — per cell, from its factor levels. Grading (oracle mutation kill)
|
|
// is compute-only, $0 tokens, so cost is generation-only.
|
|
// ---------------------------------------------------------------------------
|
|
function cellCostUsd(c, replicates) {
|
|
const model = FACTORS.model.levels[c.model];
|
|
const retr = FACTORS.retrieval.levels[c.retrieval];
|
|
const scaf = FACTORS.scaffold.levels[c.scaffold];
|
|
|
|
const tokMult = scaf.passMult; // scaffold multiplies token volume (extra passes)
|
|
const inTok = TOKENS.baseIn * retr.inputMult * tokMult;
|
|
const outTok = TOKENS.baseOut * tokMult;
|
|
|
|
const perRun =
|
|
(inTok / 1e6) * model.priceIn + (outTok / 1e6) * model.priceOut;
|
|
return { perRun, perCell: perRun * replicates, inTok, outTok };
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// LocalStubRunner — $0 deterministic fake results (retort's smoke pattern).
|
|
// Returns a plausible pass-proportion purely from factor indices, so the
|
|
// aggregate/summarize pipeline can be exercised end-to-end at zero cost.
|
|
// The numbers are a FIXTURE, not a benchmark.
|
|
// ---------------------------------------------------------------------------
|
|
function localStubRun(c, replicates) {
|
|
// Deterministic pseudo pass signal: frontier model + no over-scaffolding does
|
|
// best; ATDD on weak model and heavy scaffold drag it down (encodes the
|
|
// hypotheses, so the summary visibly reflects them — but it is FAKE).
|
|
const modelBoost = [0.55, 0.7, 0.95][c.model];
|
|
const promptPenalty = c.prompt === 2 && c.model === 0 ? -0.4 : 0; // ATDD x local
|
|
const retrievalPenalty = c.retrieval === 1 && c.model === 0 ? -0.15 : 0;
|
|
const scaffoldPenalty = c.scaffold === 2 ? -0.1 : 0; // reflexion over-iterates
|
|
let p = modelBoost + promptPenalty + retrievalPenalty + scaffoldPenalty;
|
|
p = Math.max(0, Math.min(1, p));
|
|
const passes = Math.round(p * replicates);
|
|
return { passProportion: passes / replicates, passes, replicates, cost: 0 };
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Reporting
|
|
// ---------------------------------------------------------------------------
|
|
function printFactorTable() {
|
|
console.log('\nFACTOR MODEL (ADR-122 §2)');
|
|
console.log('-'.repeat(72));
|
|
for (const [name, def] of Object.entries(FACTORS)) {
|
|
const tag = def.feature ? ' [FEATURE under screen]' : ' [context/blocking]';
|
|
console.log(` ${name}${tag}`);
|
|
console.log(` levels: ${def.levels.map((l) => l.id).join(', ')}`);
|
|
}
|
|
}
|
|
|
|
function printDesign(cells, full, args) {
|
|
console.log('\nDESIGN');
|
|
console.log('-'.repeat(72));
|
|
console.log(` factors: ${Object.keys(FACTORS).length}`);
|
|
console.log(` full factorial: ${full.length} cells (3x3x2x3)`);
|
|
console.log(` fraction: ${args.fraction} ${args.fraction < 1 ? '(screen)' : '(FULL)'}`);
|
|
console.log(` screened cells: ${cells.length}`);
|
|
console.log(` replicates / cell: ${args.replicates} (pass-proportion, not single run)`);
|
|
console.log(` total runs: ${cells.length * args.replicates}`);
|
|
console.log('\n cells:');
|
|
cells.forEach((c, i) => console.log(` ${String(i + 1).padStart(2)}. ${describeCell(c)}`));
|
|
}
|
|
|
|
function printCost(cells, args) {
|
|
let total = 0;
|
|
const perModel = {};
|
|
for (const c of cells) {
|
|
const { perCell } = cellCostUsd(c, args.replicates);
|
|
total += perCell;
|
|
const mid = FACTORS.model.levels[c.model].id;
|
|
perModel[mid] = (perModel[mid] || 0) + perCell;
|
|
}
|
|
console.log('\nCOST ESTIMATE (generation only; oracle grading is $0 — mutation kill via node --test)');
|
|
console.log('-'.repeat(72));
|
|
console.log(' Assumptions:');
|
|
console.log(` tokens/run: ${TOKENS.baseIn} in / ${TOKENS.baseOut} out (base; x retrieval x scaffold)`);
|
|
console.log(' prices/Mtok: local $0/$0 | haiku-4.5 $1/$5 | opus-4.8 $5/$25');
|
|
console.log(` replicates: ${args.replicates}`);
|
|
console.log(' Cost by model tier:');
|
|
for (const [mid, usd] of Object.entries(perModel)) {
|
|
console.log(` ${mid.padEnd(18)} $${usd.toFixed(2)}`);
|
|
}
|
|
console.log(' ' + '-'.repeat(40));
|
|
console.log(` TOTAL $${total.toFixed(2)}`);
|
|
return total;
|
|
}
|
|
|
|
function printStopBanner(total, args) {
|
|
console.log('\n' + '='.repeat(72));
|
|
console.log('STOP: REQUIRES USER SPEND CONFIRMATION');
|
|
console.log('='.repeat(72));
|
|
console.log(` This screen would spend ~$${total.toFixed(2)} in live model calls.`);
|
|
console.log(' This scaffold is DRY-RUN and has spent $0.');
|
|
console.log('');
|
|
console.log(' To smoke-test the pipeline at $0 (recommended first):');
|
|
console.log(' node scripts/doe-screen-scaffold.mjs --smoke');
|
|
console.log('');
|
|
console.log(' To actually run the screen, a human must (a) approve the $ above,');
|
|
console.log(' then (b) hand the design to the real harness — this scaffold will');
|
|
console.log(' NOT spend even with --confirm-spend:');
|
|
console.log(' retort-metaharness design --fraction 0.5 ... -o design.csv');
|
|
console.log(' retort-metaharness run -d design.csv --replicates ' + args.replicates + ' \\');
|
|
console.log(' --runner metaharness --runner-cmd "<AQE test-gen + oracle-eval binding>"');
|
|
console.log(' retort-metaharness analyze -r results.csv # Type-II ANOVA');
|
|
console.log('='.repeat(72));
|
|
}
|
|
|
|
function printSmoke(cells, args) {
|
|
console.log('\nSMOKE RUN (LocalStubRunner — $0, deterministic FAKE results, NOT a benchmark)');
|
|
console.log('-'.repeat(72));
|
|
let totalCost = 0;
|
|
const rows = cells.map((c) => {
|
|
const r = localStubRun(c, args.replicates);
|
|
totalCost += r.cost;
|
|
return { cell: describeCell(c), ...r };
|
|
});
|
|
rows
|
|
.sort((a, b) => b.passProportion - a.passProportion)
|
|
.forEach((r) =>
|
|
console.log(` pass=${r.passProportion.toFixed(2)} (${r.passes}/${r.replicates}) ${r.cell}`)
|
|
);
|
|
console.log(' ' + '-'.repeat(40));
|
|
console.log(` pipeline exercised: design(${cells.length}) -> run -> aggregate -> summarize`);
|
|
console.log(` total spend: $${totalCost.toFixed(2)} (LocalStubRunner is free by construction)`);
|
|
console.log(' NOTE: these pass-proportions are a fixture to prove wiring, not model quality.');
|
|
}
|
|
|
|
function printHelp() {
|
|
console.log(`ADR-122 DoE screen scaffold (DRY-RUN, no spend, no network)
|
|
|
|
node scripts/doe-screen-scaffold.mjs [options]
|
|
|
|
(no flag) Dry-run: print factor model + design + cost, then STOP.
|
|
--smoke $0 pipeline smoke test using LocalStubRunner.
|
|
--full Cost the FULL factorial instead of the half-fraction screen.
|
|
--fraction <f> Fraction to screen (default 0.5).
|
|
--replicates <n> Replicates per cell (default 5).
|
|
--confirm-spend Human gate. Refuses to spend here; prints hand-off command.
|
|
--help This message.`);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Main
|
|
// ---------------------------------------------------------------------------
|
|
function main() {
|
|
const args = parseArgs(process.argv.slice(2));
|
|
if (args.help) return printHelp();
|
|
|
|
console.log('ADR-122 DESIGNED-EXPERIMENT (DoE) HARNESS SCREEN — SCAFFOLD');
|
|
console.log('mode: ' + (args.smoke ? 'SMOKE ($0)' : 'DRY-RUN (no spend, no network)'));
|
|
|
|
const full = fullFactorial();
|
|
const cells = fractionSelect(full, args.fraction);
|
|
|
|
printFactorTable();
|
|
printDesign(cells, full, args);
|
|
const total = printCost(cells, args);
|
|
|
|
if (args.smoke) {
|
|
printSmoke(cells, args);
|
|
console.log('\nSmoke complete. $0 spent. Real screen still requires user confirmation (see below).');
|
|
printStopBanner(total, args);
|
|
return;
|
|
}
|
|
|
|
if (args.confirmSpend) {
|
|
console.log('\n--confirm-spend received, but THIS SCAFFOLD NEVER SPENDS.');
|
|
console.log('It has no model binding and no network by design. Hand off to the real');
|
|
console.log('harness after approving the estimate:');
|
|
}
|
|
|
|
printStopBanner(total, args);
|
|
}
|
|
|
|
main();
|