fix: pre-landing review fixes — specialist round (4 reviewers, 22 findings)

The catch that mattered (testing + maintainability, independently): --llm
extraction metrics were computed per fixture but never reached any scoreboard
cell — dead matched_any_gold/stored_rows aggregation fields proved the
unfinished wiring. Now aggregated Σ-style into the write-back cell when llm
is on, pinned by a harness-level stubbed-transport test.

Also: RUN-scoped --llm BudgetTracker (a per-invocation cap multiplied by
fixture count — ~$550 worst case — now one tracker, exhaustion aborts the
run loudly); continuity loop collapsed to one prep per pair + read-only
reader per harness (the writer replay was provably observable-effect-free
and orderings rebuilt byte-identical brains — 90 preps → 15, runtime ~12s →
~7s, identical scores; baseline counts updated 24 → 12 honestly);
factKeywordProbe escapes ILIKE metacharacters; validator rejects unpassable
slug-less retrieve-turns; ci-gate fails HARD on a broken ref instead of
silently running ungated; privacy year-scan covers the gold dir; e2e tests
self-sufficient (shared artifacts in beforeAll) with a minimal-env --llm
gate; round4/cellKey single-sourced; explicit return in eval.ts dispatch;
stale comment + type anchor fixes.
This commit is contained in:
Garry Tan
2026-06-12 12:38:34 -07:00
parent 8818dde884
commit c2938f62fb
19 changed files with 252 additions and 125 deletions
+1 -1
View File
@@ -48,7 +48,7 @@ All micro-averaged per (harness × suite) cell; registered in
- `push_recall` = Σ|injected ∩ gold| / Σ|gold| over should-retrieve turns. Pointer budgets cap this by design.
- `write_back_fidelity` = |gold facts that survive the PRODUCTION conversation→memory pipeline and are keyword-findable with correct entity attribution| / |gold facts|. The deterministic mode injects a gold extractor at the pipeline's extractor seam so segmentation, batching, dedup, and provenance stamping execute shipped code with zero LLM calls.
- `provenance_accuracy` = |surviving facts with correct {source, source_session, source_markdown_slug}| / |surviving facts|.
- `continuity_rate` = |decision probes recalled by the reader| / |probes|, over writer→reader pairs replayed through DIFFERENT harness adapters on a shared brain. A probe succeeds via pointer injection or stored-fact keyword lookup. Headline = mean over off-diagonal (writer ≠ reader) pairs.
- `continuity_rate` = |decision probes recalled by the reader| / |probes|, per READER harness. The writer fixture's decisions persist through the production write-back pipeline — which is harness-INDEPENDENT in v1 — so each pair preps once and every harness replays the read-only reader against the same persisted state (an ordered writer×reader sweep would rebuild byte-identical brains for identical scores). A probe succeeds via pointer injection or stored-fact keyword lookup. The per-writer axis activates when harness-specific write paths land.
- `source_isolation_violations` = count of injected slugs from a non-active source. **Gates at zero**, every run, regardless of baseline — cross-source leakage is the data-leak invariant.
- `avg_injected_tokens` = mean estimated tokens (chars/4) of injected context per replayed turn. Intrusion-budget diagnostic; reported, NOT gated (gating awaits calibration data — filed TODO).
- `extraction_recall` / `extraction_precision``--llm` runs only: the real extractor's output vs gold keyword probes.
+2 -2
View File
@@ -222,9 +222,9 @@ Every metric `gbrain eval *` and `gbrain search stats` reports has a plain-Engli
**Key:** `continuity_rate`
**Plain English:** A decision is recorded in one harness's session; a DIFFERENT harness asks about it later on the same brain. What fraction of those decision probes were recalled — by pointer injection or stored-fact lookup? This is the continuity-that-survives-the-harness-hop moat, measured.
**Plain English:** A decision is recorded in one session and persisted through the production write path; a different harness asks about it later on the same brain. What fraction of those decision probes were recalled — by pointer injection or stored-fact lookup? This is the continuity-that-survives-the-harness-hop moat, measured.
**Range:** 0..1, higher is better. Headline = mean over writer→reader pairs with different harnesses.
**Range:** 0..1, higher is better. Scored per reader harness (the v1 write path is harness-independent, disclosed in docs/eval/BRAINBENCH.md).
### Source-isolation violations (BrainBench)
+3 -3
View File
@@ -68,7 +68,7 @@
},
"counts": {
"claude-code/continuity": {
"gold_total": 24,
"gold_total": 12,
"gold_failed": 0
},
"claude-code/know-to-ask": {
@@ -84,7 +84,7 @@
"gold_failed": 0
},
"codex/continuity": {
"gold_total": 24,
"gold_total": 12,
"gold_failed": 0
},
"codex/know-to-ask": {
@@ -100,7 +100,7 @@
"gold_failed": 0
},
"openclaw/continuity": {
"gold_total": 24,
"gold_total": 12,
"gold_failed": 0
},
"openclaw/know-to-ask": {
+1 -1
View File
@@ -115,7 +115,7 @@ if [ -d "$BB_DIR/fixtures" ]; then
echo " VIOLATION: out-of-range year in $match (fixtures use 2024-2026)"
VIOLATIONS=$((VIOLATIONS + 1))
fi
done < <(grep -rEn '\b(201[0-9]|202[0-3]|202[7-9]|20[3-9][0-9])\b' "$BB_DIR/fixtures" --include='*.json' 2>/dev/null || true)
done < <(grep -rEn '\b(201[0-9]|202[0-3]|202[7-9]|20[3-9][0-9])\b' "$BB_DIR/fixtures" "$BB_DIR/gold" --include='*.json' 2>/dev/null || true)
fi
if [ "$VIOLATIONS" -gt 0 ]; then
+13 -3
View File
@@ -17,16 +17,26 @@ set -euo pipefail
BASELINE_PATH="evals/brainbench/baselines/main.json"
MAIN_REF="${BRAINBENCH_MAIN_REF:-origin/master}"
OUT="${BRAINBENCH_OUT:-/tmp/brainbench-result.json}"
# mktemp default (review finding): a fixed world-writable /tmp path is a
# symlink-planting target on shared hosts. CI overrides via BRAINBENCH_OUT.
OUT="${BRAINBENCH_OUT:-$(mktemp /tmp/brainbench-result-XXXXXX.json)}"
MAIN_BASELINE="$(mktemp /tmp/brainbench-main-baseline-XXXXXX.json)"
trap 'rm -f "$MAIN_BASELINE"' EXIT
# Fail HARD when the ref itself is broken — only a genuinely-absent baseline
# may take the ungated first-landing path (review finding: an unfetched ref
# or typo'd BRAINBENCH_MAIN_REF must not silently disable the gate).
if ! git rev-parse --verify --quiet "${MAIN_REF}^{commit}" > /dev/null; then
echo "[brainbench-gate] ERROR: ref ${MAIN_REF} does not resolve — fetch it or fix BRAINBENCH_MAIN_REF" >&2
exit 2
fi
if git show "${MAIN_REF}:${BASELINE_PATH}" > "$MAIN_BASELINE" 2>/dev/null; then
echo "[brainbench-gate] comparing against ${MAIN_REF}:${BASELINE_PATH}"
bun src/cli.ts eval brainbench --compare "$MAIN_BASELINE" --out "$OUT"
else
# First landing: main has no baseline yet. Run without a gate so the PR
# that introduces BrainBench can commit the initial baseline.
# First landing: the ref exists but carries no baseline yet. Run without a
# gate so the PR that introduces BrainBench can commit the initial baseline.
echo "[brainbench-gate] no baseline on ${MAIN_REF} yet — running ungated (initial-landing path)"
bun src/cli.ts eval brainbench --out "$OUT"
fi
+4 -3
View File
@@ -16,7 +16,7 @@
*/
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { dirname } from 'node:path';
import { execSync } from 'node:child_process';
import { flushThenExit } from '../core/cli-force-exit.ts';
import { cliOptsToProgressOptions, getCliOptions } from '../core/cli-options.ts';
@@ -26,6 +26,7 @@ import { isAvailable } from '../core/ai/gateway.ts';
import { FixtureValidationError, loadCorpus } from '../eval/brainbench/fixtures.ts';
import { runBrainBench } from '../eval/brainbench/harness.ts';
import {
cellKey,
compareBaselines,
parseBaseline,
renderScoreboardMarkdown,
@@ -417,7 +418,7 @@ export async function runBrainBenchCore(): Promise<{
}
const cells: Record<string, Record<string, number>> = {};
for (const c of run.cells) {
cells[`${c.harness}/${c.suite}`] = { ...c.metrics, gold_failed: c.gold_failed, gold_total: c.gold_total };
cells[cellKey(c)] = { ...c.metrics, gold_failed: c.gold_failed, gold_total: c.gold_total };
}
return { status: 'completed', fixtures_hash: corpus.fixtures_hash, cells };
} catch (err) {
@@ -426,4 +427,4 @@ export async function runBrainBenchCore(): Promise<{
}
/** Test hook: everything above process.exit, without exiting. */
export const _internal = { parseArgs, DEFAULT_BASELINE_PATH: join(DEFAULT_BASELINE_PATH) };
export const _internal = { parseArgs, DEFAULT_BASELINE_PATH };
+4 -2
View File
@@ -135,8 +135,10 @@ export interface EvalRunRecord {
* v3 (BrainBench wave, decision 16): `mode` widened to `SearchMode | 'n/a'`
* for search-mode-independent suites — brainbench runs once per sweep and
* records 'n/a' instead of fabricating a mode. v2 records (mode always a
* SearchMode) parse fine under v3 readers; eval-compare renders 'n/a' rows
* un-grouped.
* SearchMode) parse fine under v3 readers. NOTE: eval-compare's markdown
* renderer iterates SEARCH_MODES only, so 'n/a' rows surface in its --json
* `records` output, not the mode table (review finding; markdown surfacing
* is a follow-up).
*/
schema_version: 3;
run_id: string;
+1
View File
@@ -59,6 +59,7 @@ export async function runEvalCommand(engine: BrainEngine, args: string[]): Promi
// command owns its exit codes (0 pass / 1 regression / 2 error).
const { runEvalBrainBench } = await import('./eval-brainbench.ts');
await runEvalBrainBench(args.slice(1));
return; // unreachable — runEvalBrainBench always exits — but keeps control flow explicit
}
if (sub === 'code-retrieval') {
// v0.33.3 pre-w0 — code-retrieval baseline / gate harness. Needs a brain
+1 -1
View File
@@ -744,7 +744,7 @@ async function processPage(
const text = renderSegmentForExtraction(page.title || page.slug, seg);
const sessionId = `${PER_SEGMENT_SOURCE_PREFIX}:${page.slug}`;
let extracted: Awaited<ReturnType<typeof extractFactsFromTurn>> = [];
let extracted: ExtractedFact[] = [];
try {
extracted = await state.extractor({
turnText: text,
+2 -2
View File
@@ -184,8 +184,8 @@ export const METRIC_GLOSSARY: Readonly<Record<string, Readonly<MetricGlossEntry>
}),
'continuity_rate': Object.freeze({
industry_term: 'Cross-session continuity rate (BrainBench)',
eli10: 'A decision is recorded in one harness\'s session; a DIFFERENT harness asks about it later on the same brain. What fraction of those decision probes were recalled — by pointer injection or stored-fact lookup? This is the continuity-that-survives-the-harness-hop moat, measured.',
range: '0..1, higher is better. Headline = mean over writer→reader pairs with different harnesses.',
eli10: 'A decision is recorded in one session and persisted through the production write path; a different harness asks about it later on the same brain. What fraction of those decision probes were recalled — by pointer injection or stored-fact lookup? This is the continuity-that-survives-the-harness-hop moat, measured.',
range: '0..1, higher is better. Scored per reader harness (the v1 write path is harness-independent, disclosed in docs/eval/BRAINBENCH.md).',
}),
'source_isolation_violations': Object.freeze({
industry_term: 'Source-isolation violations (BrainBench)',
+23 -10
View File
@@ -197,16 +197,21 @@ export function validateGold(file: string, raw: unknown, fixture: BrainBenchFixt
if (typeof tg.should_retrieve !== 'boolean') {
throw new FixtureValidationError(file, `gold.turns["${key}"].should_retrieve must be boolean`);
}
if (tg.should_retrieve && !isStringArray(tg.gold_slugs)) {
// gold_slugs may legitimately be absent on write-back-only turns; require
// it only when the turn participates in retrieval suites.
{
// On a retrieval-suite fixture, should_retrieve=true REQUIRES non-empty
// gold_slugs — a slug-less retrieve-turn is a guaranteed miss no harness
// can pass (review finding: it silently poisons the failure rate).
const retrievalSuites = fixture.suites.some(
(s: BrainBenchSuite) => s === 'know-to-ask' || s === 'push',
);
if (retrievalSuites && !tg.gold_facts) {
if (
retrievalSuites &&
tg.should_retrieve &&
(!isStringArray(tg.gold_slugs) || tg.gold_slugs.length === 0)
) {
throw new FixtureValidationError(
file,
`gold.turns["${key}"].gold_slugs required when should_retrieve=true on a retrieval-suite fixture`,
`gold.turns["${key}"].gold_slugs must be non-empty when should_retrieve=true on a retrieval-suite fixture (a slug-less retrieve turn is an unpassable gold item)`,
);
}
}
@@ -288,9 +293,16 @@ export async function loadCorpus(fixtureDir: string, goldDir: string): Promise<L
const fixtures: LoadedFixture[] = [];
const goldByFixtureId = new Map<string, { raw: unknown; file: string }>();
for (const gf of goldFiles) {
const path = join(goldDir, gf);
const content = await readFile(path, 'utf-8');
// Parallel I/O; hashing stays in sorted order over the resolved array
// (determinism needs ordered hash.update, not serial reads).
const goldContents = await Promise.all(goldFiles.map((gf) => readFile(join(goldDir, gf), 'utf-8')));
const fixtureContents = await Promise.all(
fixtureFiles.map((ff) => readFile(join(fixtureDir, ff), 'utf-8')),
);
for (let i = 0; i < goldFiles.length; i++) {
const gf = goldFiles[i];
const content = goldContents[i];
hash.update(`gold/${gf}\n`);
hash.update(content);
let parsed: unknown;
@@ -306,9 +318,10 @@ export async function loadCorpus(fixtureDir: string, goldDir: string): Promise<L
}
const seenIds = new Set<string>();
for (const ff of fixtureFiles) {
for (let i = 0; i < fixtureFiles.length; i++) {
const ff = fixtureFiles[i];
const path = join(fixtureDir, ff);
const content = await readFile(path, 'utf-8');
const content = fixtureContents[i];
hash.update(`fixtures/${ff}\n`);
hash.update(content);
let parsed: unknown;
+63 -53
View File
@@ -9,14 +9,20 @@
* reset. A sentinel-slug test pins that sharing leaks nothing.
*
* Continuity pairs (writer fixture → production write-back → reader fixture)
* run per ordered (writerHarness ≠ readerHarness) pair on a shared brain;
* scores land on the READER's cell. With a single requested harness the
* writer==reader diagonal runs instead (disclosed, not the headline shape).
* run on a shared brain; scores land on the READER's cell. The write path is
* gbrain's pipeline — harness-INDEPENDENT in v1 (disclosed in
* docs/eval/BRAINBENCH.md) — so each pair preps ONCE (reset + seed +
* write-back) and every requested harness replays the read-only reader against
* that shared state. The per-(writer×reader)-ordering loop this replaces
* rebuilt byte-identical brains 6x per pair and replayed a writer whose state
* could not outlive the call (review findings: dead work, identical scores).
* The writer axis activates when harness-specific write paths actually land.
*
* Sealed gold: adapters only ever receive AdapterFixtureView + PublicTurn
* (toPublicTurn picks fields; gold never crosses).
*/
import { BudgetTracker } from '../../core/budget/budget-tracker.ts';
import { createBenchmarkBrain, resetTables } from '../longmemeval/harness.ts';
import type { PGLiteEngine } from '../../core/pglite-engine.ts';
import { ClaudeCodeAdapter } from './adapters/claude-code.ts';
@@ -28,6 +34,7 @@ import { runWriteBack, type WriteBackScore } from './metrics/write-back.ts';
import { scoreContinuityPair } from './metrics/continuity.ts';
import { SeedError, seedBrain, type SeedOutcome } from './seed.ts';
import {
round4,
toPublicTurn,
type AdapterFixtureView,
type BrainBenchSuite,
@@ -199,6 +206,12 @@ export async function runBrainBench(
};
const continuityByReader = new Map<HarnessName, ContinuityAgg>();
// RUN-scoped --llm budget (review finding: a per-invocation cap would
// multiply by fixture count — ~$550 worst case on the committed corpus).
const llmTracker = opts.llm
? new BudgetTracker({ maxCostUsd: opts.budgetUsd ?? 5, label: 'brainbench:llm' })
: undefined;
const engine = await createBenchmarkBrain();
let fixturesRun = 0;
try {
@@ -234,23 +247,13 @@ export async function runBrainBench(
const score = await runWriteBack(engine, lf.fixture, lf.gold, {
llm: opts.llm,
budgetUsd: opts.budgetUsd,
budgetTracker: llmTracker,
});
accumulateWriteBack(writeBackAgg, id, score);
}
}
// ---- continuity pairs ----
const pairHarnesses: Array<[HarnessName, HarnessName]> = [];
if (opts.harnesses.length === 1) {
pairHarnesses.push([opts.harnesses[0], opts.harnesses[0]]);
} else {
for (const w of opts.harnesses) {
for (const r of opts.harnesses) {
if (w !== r) pairHarnesses.push([w, r]);
}
}
}
// ---- continuity pairs: ONE prep per pair, every harness reads it ----
for (const [pairId, pair] of pairFixtures) {
if (!pair.writer || !pair.reader) continue; // loader validates; belt+suspenders
const writer = pair.writer;
@@ -258,32 +261,31 @@ export async function runBrainBench(
const decisions = reader.gold.continuity?.decisions ?? [];
if (!decisions.length) continue;
for (const [writerHarness, readerHarness] of pairHarnesses) {
progress(`continuity ${pairId} ${writerHarness}${readerHarness}`);
await resetTables(engine);
let writerSeed: SeedOutcome;
let readerSeed: SeedOutcome;
try {
writerSeed = await seedBrain(engine, writer.fixture);
readerSeed = await seedBrain(engine, reader.fixture);
} catch (err) {
if (err instanceof SeedError) {
seedFailures.push({ fixture_id: `${pairId} (${writerHarness}${readerHarness})`, error: err.message });
continue;
}
throw err;
progress(`continuity ${pairId}`);
await resetTables(engine);
let readerSeed: SeedOutcome;
try {
await seedBrain(engine, writer.fixture);
readerSeed = await seedBrain(engine, reader.fixture);
} catch (err) {
if (err instanceof SeedError) {
seedFailures.push({ fixture_id: pairId, error: err.message });
continue;
}
throw err;
}
fixturesRun += 2;
// Writer conversation replays through ITS harness (suppression state
// realistic), then its decisions persist via the production pipeline.
const writerAdapter = makeAdapter(writerHarness);
await replayFixture(engine, writerAdapter, writer, writerSeed, []);
await runWriteBack(engine, writer.fixture, writer.gold, {
llm: opts.llm,
budgetUsd: opts.budgetUsd,
});
// The writer's decisions persist through the PRODUCTION pipeline —
// harness-independent in v1, so it runs once per pair.
await runWriteBack(engine, writer.fixture, writer.gold, {
llm: opts.llm,
budgetUsd: opts.budgetUsd,
budgetTracker: llmTracker,
});
// Reader replays on the SAME brain through a different harness.
// Every requested harness reads the SAME persisted state (read-only).
for (const readerHarness of opts.harnesses) {
const readerAdapter = makeAdapter(readerHarness);
const readerRows = await replayFixture(engine, readerAdapter, reader, readerSeed, ['continuity']);
turnRows.push(...readerRows);
@@ -297,10 +299,9 @@ export async function runBrainBench(
agg.gold_total += score.gold_total;
agg.gold_failed += score.gold_failed;
if (!agg.fixtures.includes(reader.fixture.fixture_id)) agg.fixtures.push(reader.fixture.fixture_id);
agg.failed_items.push(...score.failed_items.map((f) => `${f} [${writerHarness}${readerHarness}]`));
agg.failed_items.push(...score.failed_items.map((f) => `${f} [reader: ${readerHarness}]`));
continuityByReader.set(readerHarness, agg);
}
fixturesRun += 2;
}
} finally {
await engine.disconnect();
@@ -310,7 +311,7 @@ export async function runBrainBench(
const cells: SuiteMetrics[] = [];
for (const harness of opts.harnesses) {
for (const suite of opts.suites) {
const cell = assembleCell(harness, suite, turnRows, writeBackAgg, continuityByReader);
const cell = assembleCell(harness, suite, turnRows, writeBackAgg, continuityByReader, opts.llm);
if (cell) cells.push(cell);
}
}
@@ -324,38 +325,47 @@ function accumulateWriteBack(agg: WriteBackAgg, fixtureId: string, score: WriteB
agg.survived += score.survived;
agg.provenance_ok += score.provenance_ok;
agg.stored_rows += score.stored_rows;
agg.matched_any_gold += score.matched_any_gold;
agg.fixtures.push(fixtureId);
agg.failed_items.push(...score.failed_items);
}
function round4(n: number): number {
return Math.round(n * 10000) / 10000;
}
function assembleCell(
harness: HarnessName,
suite: BrainBenchSuite,
turnRows: TurnRow[],
writeBackAgg: WriteBackAgg,
continuityByReader: Map<HarnessName, ContinuityAgg>,
llm: boolean,
): SuiteMetrics | null {
if (suite === 'write-back') {
if (writeBackAgg.fixtures.length === 0) return null;
// The write path is gbrain's pipeline — identical for every harness seam
// in v1, so each harness cell carries the same (once-computed) numbers.
// When harness-specific write paths land, this is where they diverge.
const metrics: Record<string, number> = {
write_back_fidelity: round4(
writeBackAgg.gold_total > 0 ? writeBackAgg.survived / writeBackAgg.gold_total : 1,
),
provenance_accuracy: round4(
writeBackAgg.survived > 0 ? writeBackAgg.provenance_ok / writeBackAgg.survived : 1,
),
};
if (llm) {
// Σ-aggregated extraction quality (review finding: per-fixture values
// were computed but never reached any cell).
metrics.extraction_recall = round4(
writeBackAgg.gold_total > 0 ? writeBackAgg.survived / writeBackAgg.gold_total : 1,
);
metrics.extraction_precision = round4(
writeBackAgg.stored_rows > 0 ? writeBackAgg.matched_any_gold / writeBackAgg.stored_rows : 1,
);
}
return {
suite, harness, seam: SEAM[harness],
gold_total: writeBackAgg.gold_total,
gold_failed: writeBackAgg.gold_failed,
metrics: {
write_back_fidelity: round4(
writeBackAgg.gold_total > 0 ? writeBackAgg.survived / writeBackAgg.gold_total : 1,
),
provenance_accuracy: round4(
writeBackAgg.survived > 0 ? writeBackAgg.provenance_ok / writeBackAgg.survived : 1,
),
},
metrics,
fixtures: [...writeBackAgg.fixtures],
};
}
+6 -2
View File
@@ -59,8 +59,12 @@ export async function factKeywordProbe(
sourceId: string,
keywords: string[],
): Promise<boolean> {
const conds = keywords.map((_, i) => `fact ILIKE $${i + 2}`).join(' AND ');
const params = [sourceId, ...keywords.map((kw) => `%${kw}%`)];
// Escape ILIKE metacharacters so a gold keyword containing % or _ can't
// silently broaden the match into a false pass (review finding — scoring
// integrity, not injection: everything is parameterized).
const escapeLike = (s: string) => s.replace(/[\\%_]/g, (m) => `\\${m}`);
const conds = keywords.map((_, i) => `fact ILIKE $${i + 2} ESCAPE '\\'`).join(' AND ');
const params = [sourceId, ...keywords.map((kw) => `%${escapeLike(kw)}%`)];
const rows = await engine.executeRaw<{ one: number }>(
`SELECT 1 AS one FROM facts
WHERE source_id = $1 AND expired_at IS NULL AND ${conds}
+31 -10
View File
@@ -21,6 +21,7 @@
* {source, source_session, source_markdown_slug}
*/
import type { BudgetTracker } from '../../../core/budget/budget-tracker.ts';
import type { ExtractInput, ExtractedFact } from '../../../core/facts/extract.ts';
import {
PER_SEGMENT_SOURCE_PREFIX,
@@ -40,6 +41,8 @@ export interface WriteBackScore {
provenance_ok: number;
/** Total non-audit rows stored (extraction_precision denominator in --llm mode). */
stored_rows: number;
/** Stored rows matching ANY gold probe (extraction_precision numerator). */
matched_any_gold: number;
metrics: Record<string, number>;
failed_items: string[];
}
@@ -133,7 +136,16 @@ export async function runWriteBack(
engine: PGLiteEngine,
fixture: BrainBenchFixture,
gold: FixtureGold,
opts: { llm: boolean; budgetUsd?: number },
opts: {
llm: boolean;
budgetUsd?: number;
/**
* RUN-scOPED tracker for --llm mode (review finding: a per-invocation cap
* would multiply by fixture count). The harness owns one tracker for the
* whole run and threads it here; the pipeline uses it as-is.
*/
budgetTracker?: BudgetTracker;
},
): Promise<WriteBackScore> {
const sourceId = fixture.active_source ?? 'default';
const slug = conversationSlug(fixture.fixture_id);
@@ -148,18 +160,25 @@ export async function runWriteBack(
);
}
await runExtractConversationFactsCore(engine, {
const extractResult = await runExtractConversationFactsCore(engine, {
sourceId,
slug,
types: ['conversation'],
overrideDisabled: true,
sleepMs: 0,
// Deterministic CI mode injects the gold extractor; --llm runs the real
// one under the caller's budget cap (cost-guard pattern, decision 2).
// one under the RUN-scoped tracker (cost-guard pattern, decision 2).
...(opts.llm
? { maxCostUsd: opts.budgetUsd }
? { budgetTracker: opts.budgetTracker, maxCostUsd: opts.budgetUsd }
: { extractor: makeGoldExtractor(fixture, gold) }),
});
if (extractResult.budget_exhausted) {
// Partial extraction would silently corrupt every downstream score —
// abort the run loudly instead (CLI maps this to exit 2).
throw new Error(
`brainbench --llm budget exhausted during ${fixture.fixture_id} (spent ~$${(extractResult.spent_usd ?? 0).toFixed(2)}) — raise --budget-usd or drop --llm`,
);
}
const stored = await engine.executeRaw<StoredFactRow>(
`SELECT fact, entity_slug, source, source_session, source_markdown_slug
@@ -203,13 +222,14 @@ export async function runWriteBack(
provenance_accuracy: survived > 0 ? provenanceOk / survived : 1,
};
// Computed in both modes so the harness can aggregate; only SCORED as
// extraction metrics when the real extractor ran (--llm).
const matchedAnyGold = stored.filter((row) =>
goldItems.some(({ spec }) =>
spec.match_keywords.every((kw) => row.fact.toLowerCase().includes(kw.toLowerCase())),
),
).length;
if (opts.llm) {
// Extraction quality vs gold, only meaningful when the real extractor ran.
const matchedAnyGold = stored.filter((row) =>
goldItems.some(({ spec }) =>
spec.match_keywords.every((kw) => row.fact.toLowerCase().includes(kw.toLowerCase())),
),
).length;
metrics.extraction_recall = goldItems.length > 0 ? survived / goldItems.length : 1;
metrics.extraction_precision = stored.length > 0 ? matchedAnyGold / stored.length : 1;
}
@@ -222,6 +242,7 @@ export async function runWriteBack(
survived,
provenance_ok: provenanceOk,
stored_rows: stored.length,
matched_any_gold: matchedAnyGold,
metrics,
failed_items: failed,
};
+8 -10
View File
@@ -18,11 +18,12 @@
* decimals, keys sorted, receipts excluded (decision 10).
*/
import type {
BrainBenchBaseline,
BrainBenchResult,
CompareOutcome,
SuiteMetrics,
import {
round4,
type BrainBenchBaseline,
type BrainBenchResult,
type CompareOutcome,
type SuiteMetrics,
} from './types.ts';
/** Gated metrics + their good direction. Anything absent is diagnostic-only. */
@@ -41,11 +42,8 @@ export const GATED_METRICS: Readonly<Record<string, 'lower' | 'higher'>> = {
const BASELINE_SCHEMA_VERSION = 1;
function round4(n: number): number {
return Math.round(n * 10000) / 10000;
}
function cellKey(c: SuiteMetrics): string {
/** The canonical `${harness}/${suite}` cell key — baseline + run-all records share it. */
export function cellKey(c: Pick<SuiteMetrics, 'harness' | 'suite'>): string {
return `${c.harness}/${c.suite}`;
}
+8
View File
@@ -113,6 +113,14 @@ export interface PublicTurn {
ts?: string;
}
/**
* The canonical 4-decimal rounding (decision 10 — baseline diff-stability).
* ONE implementation; scoreboard + harness import it (review DRY finding).
*/
export function round4(n: number): number {
return Math.round(n * 10000) / 10000;
}
export function toPublicTurn(turn: FixtureTurn): PublicTurn {
const out: PublicTurn = { turn_id: turn.turn_id, role: turn.role, text: turn.text };
if (turn.ts !== undefined) out.ts = turn.ts;
+12
View File
@@ -118,6 +118,18 @@ describe('validator rejection classes', () => {
expect(() => validateGold('g.json', badGold, fixture)).toThrow(/no matching fixture turn/);
});
test('retrieval-suite gold with should_retrieve=true and empty gold_slugs rejected (unpassable item)', () => {
const fixture = validateFixture('f.json', structuredClone(BASE_FIXTURE));
const badGold = structuredClone(BASE_GOLD) as { fixture_id: string; turns: Record<string, Record<string, unknown>> };
badGold.turns['1'] = { should_retrieve: true, gold_slugs: [] };
expect(() => validateGold('g.json', badGold, fixture)).toThrow(/must be non-empty/);
delete badGold.turns['1'].gold_slugs;
badGold.turns['1'].gold_facts = [
{ gist: 'x', fact: 'y', entity_slug: null, match_keywords: ['k'] },
];
expect(() => validateGold('g.json', badGold, fixture)).toThrow(/must be non-empty/);
});
test('gold_facts without match_keywords rejected', () => {
const fixture = validateFixture('f.json', structuredClone(BASE_FIXTURE));
const badGold = structuredClone(BASE_GOLD) as { turns: Record<string, Record<string, unknown>> };
+38
View File
@@ -132,6 +132,44 @@ describe('runWriteBack (deterministic, production pipeline)', () => {
}
});
test('--llm extraction metrics reach the harness CELLS (review finding: they were dropped in aggregation)', async () => {
__setChatTransportForTests(async (): Promise<ChatResult> => ({
text: JSON.stringify({
facts: [{
fact: 'Alice Example flagged the pricing model undercutting gross margin',
kind: 'belief',
entity: 'people/alice-example',
confidence: 1.0,
notability: 'high',
}],
}),
blocks: [],
stopReason: 'end',
usage: { input_tokens: 10, output_tokens: 10, cache_read_tokens: 0, cache_creation_tokens: 0 },
model: 'stub:stub',
providerId: 'stub',
}));
__setEmbedTransportForTests(
(async () => ({ embeddings: [Array.from({ length: 1536 }, () => 0.1)] })) as never,
);
try {
const { loadCorpus } = await import('../src/eval/brainbench/fixtures.ts');
const { runBrainBench } = await import('../src/eval/brainbench/harness.ts');
const corpus = await loadCorpus('evals/brainbench/fixtures', 'evals/brainbench/gold');
const sub = { ...corpus, fixtures: corpus.fixtures.filter((f) => f.fixture.fixture_id === 'wb-001-pricing-concern') };
const out = await runBrainBench(sub, {
harnesses: ['openclaw'], suites: ['write-back'], includeHoldout: true, llm: true, budgetUsd: 1,
});
const cell = out.cells.find((c) => c.suite === 'write-back');
expect(cell).toBeDefined();
expect(cell!.metrics.extraction_recall).toBeCloseTo(1 / 3);
expect(cell!.metrics.extraction_precision).toBe(1);
} finally {
__setChatTransportForTests(null);
__setEmbedTransportForTests(null);
}
});
test('multi-segment conversations extract per segment (the 45-min gap splits)', async () => {
await resetTables(engine);
// gen-wb fixtures carry a deliberate >30min gap; use one.
+31 -22
View File
@@ -40,6 +40,15 @@ beforeAll(() => {
cpSync(join(REPO, 'evals/brainbench/fixtures', `${id}.fixture.json`), join(fixtures, `${id}.fixture.json`));
cpSync(join(REPO, 'evals/brainbench/gold', `${id}.gold.json`), join(gold, `${id}.gold.json`));
}
// Shared artifacts every consumer test depends on, built ONCE here so each
// test is self-sufficient under -t filters / sharding (review finding:
// order-dependent file production across describe blocks).
const r = run(['--fixtures', fixtures, '--gold', gold, '--update-baseline', join(root, 'base1.json')]);
if (r.exitCode !== 0) throw new Error(`beforeAll baseline build failed: ${r.stderr}`);
const doctored = JSON.parse(readFileSync(join(root, 'base1.json'), 'utf-8'));
const cellKey = Object.keys(doctored.counts).find((k) => doctored.counts[k].gold_total > 0)!;
doctored.counts[cellKey].gold_failed = -1; // pretends fewer failures than any run can match
writeFileSync(join(root, 'doctored.json'), JSON.stringify(doctored, null, 2));
});
describe('exit contract over a multi-brain run (PGLite exitCode-hijack guard)', () => {
@@ -56,11 +65,10 @@ describe('exit contract over a multi-brain run (PGLite exitCode-hijack guard)',
}, 30_000);
test('--update-baseline is byte-deterministic across two runs (decision 10)', () => {
const b1 = join(root, 'base1.json');
const b2 = join(root, 'base2.json');
expect(run(['--fixtures', fixtures, '--gold', gold, '--update-baseline', b1]).exitCode).toBe(0);
expect(run(['--fixtures', fixtures, '--gold', gold, '--update-baseline', b2]).exitCode).toBe(0);
expect(readFileSync(b1, 'utf-8')).toBe(readFileSync(b2, 'utf-8'));
// base1.json was produced by an entirely separate run in beforeAll.
expect(readFileSync(b2, 'utf-8')).toBe(readFileSync(join(root, 'base1.json'), 'utf-8'));
}, 60_000);
test('--compare against own baseline: exit 0 PASS', () => {
@@ -71,14 +79,7 @@ describe('exit contract over a multi-brain run (PGLite exitCode-hijack guard)',
}, 30_000);
test('doctored main baseline (pretends fewer failures): exit 1 REGRESSION with named breach', () => {
const doctored = JSON.parse(readFileSync(join(root, 'base1.json'), 'utf-8'));
const cellKey = Object.keys(doctored.counts).find((k) => doctored.counts[k].gold_total > 0)!;
doctored.counts[cellKey].gold_failed = Math.max(0, doctored.counts[cellKey].gold_failed - 1);
// also make the run's count strictly greater by raising the bar impossibly
doctored.counts[cellKey].gold_failed = -1 as never;
const path = join(root, 'doctored.json');
writeFileSync(path, JSON.stringify(doctored, null, 2));
const r = run(['--fixtures', fixtures, '--gold', gold, '--compare', path]);
const r = run(['--fixtures', fixtures, '--gold', gold, '--compare', join(root, 'doctored.json')]);
expect(r.exitCode).toBe(1);
expect(r.stdout).toContain('## Gate: REGRESSION');
expect(r.stdout).toContain('newly-failed');
@@ -239,10 +240,11 @@ describe('--json stdout completeness', () => {
describe('--llm availability gate', () => {
test('no config + no keys: exit 2 with an actionable message, before any run', () => {
const bareHome = mkdtempSync(join(tmpdir(), 'bb-home-'));
const env = { ...process.env, HOME: bareHome } as Record<string, string>;
delete env.ANTHROPIC_API_KEY;
delete env.CLAUDE_API_KEY;
delete env.OPENAI_API_KEY;
// Minimal explicit env (review finding): spreading process.env and
// deleting a hardcoded key list leaks other provider keys (GOOGLE_*,
// per-recipe ${ID}_API_KEY) — on a dev machine that could flip the gate
// open and make REAL API calls from a test.
const env: Record<string, string> = { PATH: process.env.PATH ?? '', HOME: bareHome };
const proc = Bun.spawnSync(
['bun', 'src/cli.ts', 'eval', 'brainbench', '--fixtures', fixtures, '--gold', gold, '--llm'],
{ cwd: REPO, env, stdout: 'pipe', stderr: 'pipe' },
@@ -273,13 +275,19 @@ describe('render-brainbench-delta.ts (the CI step-summary block)', () => {
});
describe('privacy guard violation branches (negative path)', () => {
test('a fixture with a real dollar amount + out-of-range year fails the scan', () => {
test('a fixture with a real dollar amount + out-of-range year fails the scan (gold dir scanned too)', () => {
const dirty = mkdtempSync(join(tmpdir(), 'bb-privacy-'));
mkdirSync(join(dirty, 'fixtures'), { recursive: true });
mkdirSync(join(dirty, 'gold'), { recursive: true });
writeFileSync(
join(dirty, 'fixtures', 'leak.fixture.json'),
JSON.stringify({ turns: [{ text: 'They raised $50M back in 2019 for the series B' }] }),
JSON.stringify({ turns: [{ text: 'They raised $50M for the series B' }] }),
);
// The year violation lives in GOLD — pins that the year scan covers the
// gold dir as well (review finding: it previously scanned fixtures only).
writeFileSync(
join(dirty, 'gold', 'leak.gold.json'),
JSON.stringify({ fixture_id: 'leak', turns: { '1': { gold_facts: [{ fact: 'raised back in 2019' }] } } }),
);
const proc = Bun.spawnSync(['bash', 'scripts/check-synthetic-corpus-privacy.sh'], {
cwd: REPO,
@@ -319,10 +327,11 @@ describe('run-all wiring (decision 16) — full corpus, in-process', () => {
expect(core.status).toBe('completed');
expect(Object.keys(core.cells ?? {}).length).toBe(12);
expect(core.fixtures_hash).toBeDefined();
// committed baseline matches the committed corpus hash (drift guard)
if (existsSync('evals/brainbench/baselines/main.json')) {
const baseline = JSON.parse(readFileSync('evals/brainbench/baselines/main.json', 'utf-8'));
expect(baseline.fixtures_hash).toBe(core.fixtures_hash);
}
// Committed baseline matches the committed corpus hash (drift guard).
// UNCONDITIONAL (review finding): a conditional existsSync would turn a
// deleted baseline into a silent no-op instead of a failure.
expect(existsSync('evals/brainbench/baselines/main.json')).toBe(true);
const baseline = JSON.parse(readFileSync('evals/brainbench/baselines/main.json', 'utf-8'));
expect(baseline.fixtures_hash).toBe(core.fixtures_hash);
}, 120_000);
});