v1/mode: model-aware enrichment downgrade + 3 corpora + variance harness
lakehouse/auditor 1 blocking issue: todo!() macro call in tests/real-world/scrum_master_pipeline.ts
lakehouse/auditor 1 blocking issue: todo!() macro call in tests/real-world/scrum_master_pipeline.ts
Pass 5 (5 reps × 4 conditions × 1 file on grok-4.1-fast) showed composing matrix corpora is anti-additive on strong models — composed lakehouse_arch + symbols LOST 5/5 head-to-head vs codereview_isolation (Δ −1.8 grounded findings, p=0.031). Default flips to isolation; matrix path now auto- downgrades when the resolved model is strong. Mode runner: - matrix_corpus is Vec<String> (string OR array via deserialize_string_or_vec) - top_k=6 from each corpus, merge by score, take top 8 globally - chunk tag prefers doc_id over source so reviewer sees [adr:009] vs [lakehouse_arch] - is_weak_model() gate auto-downgrades codereview_lakehouse → codereview_isolation for strong models (default-strong; weak = :free suffix or local last-resort) - LH_FORCE_FULL_ENRICHMENT=1 bypasses for diagnostic runs - EnrichmentSources.downgraded_from records when the gate fires Three corpora indexed via /vectors/index (5849 chunks total): - lakehouse_arch_v1 — ADRs + phases + PRD + scrum spec (93 docs, 2119 chunks) - scrum_findings_v1 — past scrum_reviews.jsonl (168 docs, 1260 chunks; EXCLUDED from defaults — 24% out-of-bounds line citations from cross-file drift) - lakehouse_symbols_v1 — regex-extracted pub items + /// docs (656 docs, 2470 chunks) Experiment infra: - scripts/build_*_corpus.ts — re-runnable when source content changes - scripts/mode_pass5_variance_paid.ts — N reps × M conditions on one file - scripts/mode_pass5_summarize.ts — mean ± σ + head-to-head, parser handles numbered + path-with-line + path-with-symbol finding tables - scripts/mode_compare.ts — groups by mode|corpus when sweeps span corpora - scripts/mode_experiment.ts — default model bumped to x-ai/grok-4.1-fast, --corpus flag for per-call override Decisions + open follow-ups: docs/MODE_RUNNER_TUNING_PLAN.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+47
-16
@@ -227,13 +227,37 @@ function main() {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Group by file → mode
|
||||
// Group by file → mode (with corpus appended when matrix-bearing modes
|
||||
// were swept across multiple corpora — otherwise lakehouse_arch_v1
|
||||
// would clobber scrum_findings_v1 etc). matrix_corpus is now a Vec
|
||||
// on the wire (multi-corpus support); legacy rows have either a
|
||||
// string or null. Coerce to a stable key.
|
||||
const matrixCorpus = (r: Row): string => {
|
||||
const c = (r.sources as any)?.matrix_corpus;
|
||||
if (!c) return "";
|
||||
if (typeof c === "string") return c;
|
||||
if (Array.isArray(c)) {
|
||||
if (c.length === 0) return "";
|
||||
if (c.length === 1) return c[0];
|
||||
// Stable join: sort then "+"-separate so order doesn't matter.
|
||||
return [...c].sort().join("+");
|
||||
}
|
||||
return "";
|
||||
};
|
||||
const corporaInPlay = new Set(rows.map(matrixCorpus).filter(c => c));
|
||||
const showCorpus = corporaInPlay.size > 1;
|
||||
const keyOf = (r: Row): string => {
|
||||
const c = matrixCorpus(r);
|
||||
return showCorpus && c ? `${r.mode}|${c}` : r.mode;
|
||||
};
|
||||
|
||||
const byFile: Record<string, Record<string, Row>> = {};
|
||||
const allModes = new Set<string>();
|
||||
for (const r of rows) {
|
||||
byFile[r.file_path] ??= {};
|
||||
byFile[r.file_path][r.mode] = r; // last-write-wins per mode per file
|
||||
allModes.add(r.mode);
|
||||
const k = keyOf(r);
|
||||
byFile[r.file_path][k] = r; // last-write-wins per (mode,corpus) per file
|
||||
allModes.add(k);
|
||||
}
|
||||
const modesSorted = [...allModes].sort();
|
||||
|
||||
@@ -246,13 +270,13 @@ function main() {
|
||||
const fileLines = fileContent ? fileContent.split("\n").length : 0;
|
||||
console.log(` (file: ${fileLines} lines${fileContent === null ? ", NOT READABLE — grounding skipped" : ""})`);
|
||||
console.log(
|
||||
` ${pad("mode", 30)} ${pad("resp", 6, true)} ${pad("find", 5, true)} ${pad("ground", 9, true)} ${pad("hallu", 6, true)} ${pad("OOB", 4, true)} ${pad("path", 5, true)} ${pad("ms", 7, true)} ${pad("bug_fp", 6, true)}`
|
||||
` ${pad("mode", 56)} ${pad("resp", 6, true)} ${pad("find", 5, true)} ${pad("ground", 9, true)} ${pad("hallu", 6, true)} ${pad("OOB", 4, true)} ${pad("path", 5, true)} ${pad("ms", 7, true)} ${pad("bug_fp", 6, true)}`
|
||||
);
|
||||
console.log(` ${"─".repeat(30)} ${"─".repeat(6)} ${"─".repeat(5)} ${"─".repeat(9)} ${"─".repeat(6)} ${"─".repeat(4)} ${"─".repeat(5)} ${"─".repeat(7)} ${"─".repeat(6)}`);
|
||||
console.log(` ${"─".repeat(56)} ${"─".repeat(6)} ${"─".repeat(5)} ${"─".repeat(9)} ${"─".repeat(6)} ${"─".repeat(4)} ${"─".repeat(5)} ${"─".repeat(7)} ${"─".repeat(6)}`);
|
||||
for (const mode of modesSorted) {
|
||||
const r = byFile[file][mode];
|
||||
if (!r) {
|
||||
console.log(` ${pad(modeLabel(mode), 30)} ${pad("—", 6, true)}`);
|
||||
console.log(` ${pad(modeLabel(mode), 56)} ${pad("—", 6, true)}`);
|
||||
continue;
|
||||
}
|
||||
const findings = countFindings(r.response);
|
||||
@@ -261,7 +285,7 @@ function main() {
|
||||
const grounding = checkGrounding(r.response, fileContent);
|
||||
const groundedStr = grounding.total === 0 ? "—" : `${grounding.grounded}/${grounding.total}`;
|
||||
console.log(
|
||||
` ${pad(modeLabel(mode), 30)} ${pad(r.response_chars, 6, true)} ${pad(findings, 5, true)} ${pad(groundedStr, 9, true)} ${pad(grounding.hallucinated, 6, true)} ${pad(grounding.out_of_bounds_lines, 4, true)} ${pad(cits, 5, true)} ${pad(r.latency_ms, 7, true)} ${pad(bf, 6, true)}`
|
||||
` ${pad(modeLabel(mode), 56)} ${pad(r.response_chars, 6, true)} ${pad(findings, 5, true)} ${pad(groundedStr, 9, true)} ${pad(grounding.hallucinated, 6, true)} ${pad(grounding.out_of_bounds_lines, 4, true)} ${pad(cits, 5, true)} ${pad(r.latency_ms, 7, true)} ${pad(bf, 6, true)}`
|
||||
);
|
||||
}
|
||||
console.log("");
|
||||
@@ -271,11 +295,11 @@ function main() {
|
||||
// avg_groundedness is the rate at which findings cite real symbols
|
||||
// within file bounds. Modes with low groundedness are confabulating.
|
||||
console.log("═══ PER-MODE AGGREGATE ═══\n");
|
||||
console.log(` ${pad("mode", 30)} ${pad("n", 3, true)} ${pad("avg find", 9, true)} ${pad("avg grnd", 9, true)} ${pad("grnd %", 7, true)} ${pad("avg hallu", 10, true)} ${pad("avg ms", 7, true)}`);
|
||||
console.log(` ${"─".repeat(30)} ${"─".repeat(3)} ${"─".repeat(9)} ${"─".repeat(9)} ${"─".repeat(7)} ${"─".repeat(10)} ${"─".repeat(7)}`);
|
||||
console.log(` ${pad("mode", 56)} ${pad("n", 3, true)} ${pad("avg find", 9, true)} ${pad("avg grnd", 9, true)} ${pad("grnd %", 7, true)} ${pad("avg hallu", 10, true)} ${pad("avg ms", 7, true)}`);
|
||||
console.log(` ${"─".repeat(56)} ${"─".repeat(3)} ${"─".repeat(9)} ${"─".repeat(9)} ${"─".repeat(7)} ${"─".repeat(10)} ${"─".repeat(7)}`);
|
||||
const fileCache: Record<string, string | null> = {};
|
||||
for (const mode of modesSorted) {
|
||||
const modeRows = rows.filter(r => r.mode === mode);
|
||||
const modeRows = rows.filter(r => keyOf(r) === mode);
|
||||
if (modeRows.length === 0) continue;
|
||||
const n = modeRows.length;
|
||||
let totFind = 0, totGround = 0, totHallu = 0;
|
||||
@@ -292,7 +316,7 @@ function main() {
|
||||
const avgHallu = (totHallu / n).toFixed(1);
|
||||
const avgMs = Math.round(modeRows.reduce((s, r) => s + r.latency_ms, 0) / n);
|
||||
console.log(
|
||||
` ${pad(modeLabel(mode), 30)} ${pad(n, 3, true)} ${pad(avgFind, 9, true)} ${pad(avgGround, 9, true)} ${pad(grndPct, 7, true)} ${pad(avgHallu, 10, true)} ${pad(avgMs, 7, true)}`
|
||||
` ${pad(modeLabel(mode), 56)} ${pad(n, 3, true)} ${pad(avgFind, 9, true)} ${pad(avgGround, 9, true)} ${pad(grndPct, 7, true)} ${pad(avgHallu, 10, true)} ${pad(avgMs, 7, true)}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -301,13 +325,20 @@ function main() {
|
||||
// wins). Comparing grounded findings instead corrects for modes
|
||||
// that produce convincing-but-fake output.
|
||||
console.log("\n═══ MODE vs codereview_lakehouse (grounded findings, per file) ═══\n");
|
||||
console.log(` ${pad("mode", 30)} ${pad("wins", 5, true)} ${pad("losses", 7, true)} ${pad("ties", 5, true)} ${pad("Δ avg grounded", 16, true)}`);
|
||||
console.log(` ${"─".repeat(30)} ${"─".repeat(5)} ${"─".repeat(7)} ${"─".repeat(5)} ${"─".repeat(16)}`);
|
||||
console.log(` ${pad("mode", 56)} ${pad("wins", 5, true)} ${pad("losses", 7, true)} ${pad("ties", 5, true)} ${pad("Δ avg grounded", 16, true)}`);
|
||||
console.log(` ${"─".repeat(56)} ${"─".repeat(5)} ${"─".repeat(7)} ${"─".repeat(5)} ${"─".repeat(16)}`);
|
||||
// Pick whichever codereview_lakehouse key shows up most often as the
|
||||
// baseline (handles corpus-suffixed keys when showCorpus=true).
|
||||
const baselineKey = modesSorted
|
||||
.filter(k => k.startsWith("codereview_lakehouse"))
|
||||
.sort((a, b) =>
|
||||
Object.values(byFile).filter(f => f[b]).length -
|
||||
Object.values(byFile).filter(f => f[a]).length)[0] ?? "codereview_lakehouse";
|
||||
for (const mode of modesSorted) {
|
||||
if (mode === "codereview_lakehouse") continue;
|
||||
if (mode === baselineKey) continue;
|
||||
let wins = 0, losses = 0, ties = 0, totalDelta = 0, n = 0;
|
||||
for (const file of Object.keys(byFile)) {
|
||||
const baseline = byFile[file]["codereview_lakehouse"];
|
||||
const baseline = byFile[file][baselineKey];
|
||||
const challenger = byFile[file][mode];
|
||||
if (!baseline || !challenger) continue;
|
||||
const fc = fileCache[file] ??= readFileSafe(file);
|
||||
@@ -322,7 +353,7 @@ function main() {
|
||||
if (n === 0) continue;
|
||||
const avgDelta = (totalDelta / n).toFixed(1);
|
||||
console.log(
|
||||
` ${pad(modeLabel(mode), 30)} ${pad(wins, 5, true)} ${pad(losses, 7, true)} ${pad(ties, 5, true)} ${pad(avgDelta, 16, true)}`
|
||||
` ${pad(modeLabel(mode), 56)} ${pad(wins, 5, true)} ${pad(losses, 7, true)} ${pad(ties, 5, true)} ${pad(avgDelta, 16, true)}`
|
||||
);
|
||||
}
|
||||
console.log("\n[compare] done — ⚗ marks lossy/control modes, exclude from recommendations\n");
|
||||
|
||||
Reference in New Issue
Block a user