mirror of
https://github.com/garrytan/gbrain.git
synced 2026-08-14 00:48:18 +00:00
* fix(autopilot,eval): nightly quality probe enable path works end-to-end + wire conversation-parser probe Takeover of #2629 and #2630 (rebased onto master; dropped the test/engine-find-trajectory.test.ts hunk both PRs carried — master already ships the equivalent gateway-dims fix). #2629 — nightly quality probe enable path: - autopilot + doctor read the probe flag dual-plane (DB config row from 'gbrain config set' wins, ~/.gbrain/config.json fallback) via new resolveProbeEnabled/resolveProbeMaxUsd helpers - resolveRepoRoot prefers the gbrain package root where the committed fixture lives, not the brain repoPath - rate_limited skips no longer write an audit row every autopilot cycle - eval-longmemeval strips 'provider:' recipe ids before raw Anthropic SDK calls and emits the gold answer for downstream judges - cross-modal batch folds the gold answer into the judge task; probe passes QA-shaped dimensions instead of the agent-response rubric - DEFAULT_SLOTS slot A moves to openai:gpt-5.2 (gpt-4o left the recipe); new consistency test pins every default slot to its recipe #2630 — conversation-parser nightly probe wire-up: - autopilot step 4.6 invokes runConversationParserNightlyProbe (dual-plane flag + D10 tokenmax mode-gate, package-root fixtures, 24h gate, audit trail via new src/core/audit-parser-probe.ts) - doctor's conversation_parser_probe_health replaces the hardcoded 'Skipped' stub with a real pure-function check over the audit trail Co-authored-by: p3ob7o <p3ob7o@users.noreply.github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(pricing): add openai:gpt-5.2 canonical entry for the new default slot A DEFAULT_SLOTS slot A moved to openai:gpt-5.2, which had no CANONICAL_PRICING entry — estimateCost silently dropped slot A from the --max-usd pre-flight and est_cost_usd audit rows (~1/3 under-count on the default panel). Rates from the OpenAI recipe chat touchpoint (verified 2026-04-20). Also refresh the --slot-a-model help text default and pin a pricing-presence assertion in the DEFAULT_SLOTS consistency test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Sinabina <sinabina@Sinabinas-MacBook-Pro-4.local> Co-authored-by: p3ob7o <p3ob7o@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Garry Tan <garrytan@gmail.com>
92 lines
4.6 KiB
TypeScript
92 lines
4.6 KiB
TypeScript
/**
|
|
* Source-shape regression tests for the v0.41 autopilot wiring of
|
|
* `runNightlyQualityProbe`.
|
|
*
|
|
* The autopilot loop is hard to drive end-to-end without spinning a real
|
|
* daemon (database, queue, gateway, etc). These tests pin the structural
|
|
* shape of the wiring — the feature flag check, the try/catch, the DI
|
|
* shape passed to runNightlyQualityProbe — so future refactors can't
|
|
* silently strip the protections without a CI signal.
|
|
*
|
|
* The pure decision logic lives in shouldRunNightly (already pinned by
|
|
* tests in nightly-quality-probe.test.ts).
|
|
*/
|
|
|
|
import { describe, test, expect } from 'bun:test';
|
|
import { readFileSync } from 'node:fs';
|
|
import { resolve } from 'node:path';
|
|
|
|
const AUTOPILOT_SRC = resolve('src/commands/autopilot.ts');
|
|
const SOURCE = readFileSync(AUTOPILOT_SRC, 'utf-8');
|
|
|
|
describe('autopilot wiring: nightly quality probe', () => {
|
|
test('imports runNightlyQualityProbe from the phase module', () => {
|
|
expect(SOURCE).toContain(`runNightlyQualityProbe`);
|
|
expect(SOURCE).toContain(`nightly-quality-probe`);
|
|
});
|
|
|
|
test('uses the eng-D2 adapter module (not direct subprocess of eval-longmemeval/cross-modal)', () => {
|
|
expect(SOURCE).toContain(`nightly-probe-adapters`);
|
|
expect(SOURCE).toContain(`runLongMemEvalForProbe`);
|
|
expect(SOURCE).toContain(`runCrossModalBatchForProbe`);
|
|
});
|
|
|
|
test('feature flag gate present: dual-plane read (DB row wins, file plane fallback)', () => {
|
|
// Per D10: the scheduler ONLY checks the feature flag. The 24h rate-limit
|
|
// lives inside runNightlyQualityProbe itself (no scheduler-side precheck).
|
|
// The flag resolves through resolveProbeEnabled so `gbrain config set
|
|
// autopilot.nightly_quality_probe.enabled true` (the doctor hint, DB
|
|
// plane) and ~/.gbrain/config.json (file plane) BOTH work — a file-only
|
|
// read made the printed hint a silent no-op.
|
|
expect(SOURCE).toContain(`getConfig('autopilot.nightly_quality_probe.enabled')`);
|
|
expect(SOURCE).toMatch(/resolveProbeEnabled\(dbEnabled,\s*cfg\?\.autopilot\?\.nightly_quality_probe\?\.enabled\)/);
|
|
});
|
|
|
|
test('NO scheduler-side rate-limit check (D10 simplification)', () => {
|
|
// Codex round-1 #11 caught: scheduler-side rate-limit duplicates phase-internal logic.
|
|
// The wiring code MUST NOT call shouldRunNightly directly OR read recent events
|
|
// before invoking the phase.
|
|
expect(SOURCE).not.toContain(`shouldRunNightly(`);
|
|
expect(SOURCE).not.toContain(`readRecentQualityProbeEvents(`);
|
|
});
|
|
|
|
test('probe call wrapped in try/catch that does NOT bump consecutiveErrors', () => {
|
|
// The try/catch around the probe must log the error but never crash the loop.
|
|
// We verify the structural pattern: the probe call is inside a try block,
|
|
// the catch block calls logError, and consecutiveErrors is not bumped inside the catch.
|
|
expect(SOURCE).toMatch(/try\s*\{\s*[^}]*nightly_quality_probe/);
|
|
expect(SOURCE).toMatch(/catch[\s\S]*?autopilot\.nightly_probe[\s\S]*?do NOT bump consecutiveErrors/);
|
|
});
|
|
|
|
test('DI shape: isEnabled / hasEmbeddingProvider / resolveMaxUsd / resolveRepoRoot / runLongMemEval / runCrossModalBatch / now', () => {
|
|
// The exact 7 fields of NightlyProbeDeps.
|
|
expect(SOURCE).toContain(`isEnabled:`);
|
|
expect(SOURCE).toContain(`hasEmbeddingProvider:`);
|
|
expect(SOURCE).toContain(`resolveMaxUsd:`);
|
|
expect(SOURCE).toContain(`resolveRepoRoot:`);
|
|
expect(SOURCE).toContain(`runLongMemEval:`);
|
|
expect(SOURCE).toContain(`runCrossModalBatch:`);
|
|
expect(SOURCE).toContain(`now:`);
|
|
});
|
|
|
|
test('resolveRepoRoot prefers the gbrain package root (committed fixture home), not the brain repoPath', () => {
|
|
// The DI harness in nightly-quality-probe.test.ts passes process.cwd()
|
|
// (= the gbrain repo in CI), which papered over the wiring passing
|
|
// repoPath (= sync.repo_path, the user's BRAIN repo, where the fixture
|
|
// never exists). Pin the package-root resolution + existence check.
|
|
expect(SOURCE).toMatch(/fileURLToPath\(new URL\('\.\.\/\.\.', import\.meta\.url\)\)/);
|
|
expect(SOURCE).toContain(`'longmemeval-nightly.jsonl'`);
|
|
expect(SOURCE).toMatch(/fixtureAtPkgRoot \? pkgRoot : repoPath/);
|
|
});
|
|
|
|
test('hasEmbeddingProvider reads from gateway.isAvailable("embedding") (codex round-2 #12 — in-process, not subprocess)', () => {
|
|
expect(SOURCE).toContain(`isAvailable('embedding')`);
|
|
expect(SOURCE).toContain(`gateway`);
|
|
});
|
|
|
|
test('max_usd resolves dual-plane (default = 5 pinned by resolveProbeMaxUsd unit tests)', () => {
|
|
expect(SOURCE).toContain(`getConfig('autopilot.nightly_quality_probe.max_usd')`);
|
|
expect(SOURCE).toMatch(/resolveProbeMaxUsd\(dbMaxUsd,\s*cfg\?\.autopilot\?\.nightly_quality_probe\?\.max_usd\)/);
|
|
});
|
|
});
|