fix(context): env-plane window knob works config-less; harden two gateway-state-leak victims

Three CI-only check failures, two root causes:

1. windowTurnCount ignored GBRAIN_RETRIEVAL_REFLEX_WINDOW_TURNS when
   loadConfig() returned null (no config file AND no DATABASE_URL — a clean
   CI shard with no brain). loadConfig drops its env→config mapping in that
   case, so the documented escape hatch silently died and the window fell
   back to 4 → windowed extraction widened when the test set window=1 →
   prior-turn entity leaked. Fixed: read the env var directly in
   windowTurnCount, mirroring reflexEnabled's direct process.env read. This
   is a real product bug, not just a test artifact — any config-less host
   using the env hatch was affected. Regression test pins it.

2. sync-cost-preview + doctor-federation-health failed only IN-SHARD: a
   sibling test configured a non-legacy (ZeroEntropy 1280-d / $0.05) gateway
   and never reset it. The legacy-embedding preload only restores the
   OpenAI/1536 default when the gateway slot is EMPTY, so a non-empty foreign
   config survives into the next file — and a file's beforeAll runs BEFORE
   the preload's restoring beforeEach, so federation-health built a
   vector(1280) column and its 1536-d fixture hit CheckExpectedDim. My new
   test files reshuffled the deterministic file→shard assignment, exposing
   this latent ordering bug. Hardened both victims to establish the gateway
   state they assert (sync-cost-preview resets to the unconfigured fallback;
   federation-health pins legacy 1536 before initSchema) so they're
   order-independent. Verified against a simulated leaker run before them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-06-14 08:16:06 -07:00
co-authored by Claude Fable 5
parent 07d3e45427
commit 82cc7fff90
4 changed files with 60 additions and 1 deletions
+12
View File
@@ -79,6 +79,18 @@ export interface ReflexParams {
export const DEFAULT_WINDOW_TURNS = 4; export const DEFAULT_WINDOW_TURNS = 4;
export function windowTurnCount(cfg: GBrainConfig | null): number { export function windowTurnCount(cfg: GBrainConfig | null): number {
// Env plane is read DIRECTLY here (mirroring reflexEnabled's direct
// process.env read), not just via loadConfig's env→config mapping. When
// there's no config file AND no DATABASE_URL, loadConfig() returns null and
// drops that mapping entirely — so without this, the documented
// GBRAIN_RETRIEVAL_REFLEX_WINDOW_TURNS escape hatch would be silently
// ignored and the window would fall back to the default of 4 (a real
// config-less-environment bug, e.g. a clean CI shard with no brain).
const env = process.env.GBRAIN_RETRIEVAL_REFLEX_WINDOW_TURNS;
if (env != null && env !== '') {
const e = Number(env);
if (Number.isFinite(e) && e >= 1) return Math.floor(e);
}
const n = cfg?.retrieval_reflex_window_turns; const n = cfg?.retrieval_reflex_window_turns;
if (typeof n === 'number' && Number.isFinite(n) && n >= 1) return Math.floor(n); if (typeof n === 'number' && Number.isFinite(n) && n >= 1) return Math.floor(n);
return DEFAULT_WINDOW_TURNS; return DEFAULT_WINDOW_TURNS;
+13
View File
@@ -8,11 +8,24 @@
*/ */
import { describe, test, expect, beforeAll, afterAll, beforeEach } from 'bun:test'; import { describe, test, expect, beforeAll, afterAll, beforeEach } from 'bun:test';
import { PGLiteEngine } from '../src/core/pglite-engine.ts'; import { PGLiteEngine } from '../src/core/pglite-engine.ts';
import { configureGateway } from '../src/core/ai/gateway.ts';
import { checkFederationHealth } from '../src/commands/doctor.ts'; import { checkFederationHealth } from '../src/commands/doctor.ts';
let engine: PGLiteEngine; let engine: PGLiteEngine;
beforeAll(async () => { beforeAll(async () => {
// Pin the legacy OpenAI/1536 embedding shape BEFORE initSchema builds the
// content_chunks vector column. beforeAll runs before the legacy-embedding
// preload's restoring beforeEach fires, so the gateway here is whatever the
// PRIOR file in this shard left it — if that file configured a 1280-d model
// and didn't reset, initSchema would build a vector(1280) column and the
// 1536-d coverage fixture below would fail CheckExpectedDim. Establish the
// dimension this file's fixtures assume rather than inheriting it.
configureGateway({
embedding_model: 'openai:text-embedding-3-large',
embedding_dimensions: 1536,
env: { ...process.env },
});
engine = new PGLiteEngine(); engine = new PGLiteEngine();
await engine.connect({}); await engine.connect({});
await engine.initSchema(); await engine.initSchema();
+23
View File
@@ -415,4 +415,27 @@ describe('windowTurnCount — knob edge semantics', () => {
expect(windowTurnCount({ retrieval_reflex_window_turns: 1 } as never)).toBe(1); expect(windowTurnCount({ retrieval_reflex_window_turns: 1 } as never)).toBe(1);
expect(windowTurnCount({ retrieval_reflex_window_turns: 6.9 } as never)).toBe(6); expect(windowTurnCount({ retrieval_reflex_window_turns: 6.9 } as never)).toBe(6);
}); });
test('the env escape hatch is honored even when config is null (no config file / DB)', async () => {
const { windowTurnCount } = await import('../src/core/context/reflex.ts');
const prev = process.env.GBRAIN_RETRIEVAL_REFLEX_WINDOW_TURNS;
try {
// loadConfig() returns null in a config-less environment (clean CI shard,
// no brain) and drops its env→config mapping — windowTurnCount must still
// read the env var directly, or the documented escape hatch is dead and
// the window silently defaults to 4.
process.env.GBRAIN_RETRIEVAL_REFLEX_WINDOW_TURNS = '1';
expect(windowTurnCount(null)).toBe(1);
process.env.GBRAIN_RETRIEVAL_REFLEX_WINDOW_TURNS = '7';
expect(windowTurnCount(null)).toBe(7);
// Env wins over a config value too (env is the higher-precedence plane).
expect(windowTurnCount({ retrieval_reflex_window_turns: 3 } as never)).toBe(7);
// Garbage env falls through to config / default, not a crash.
process.env.GBRAIN_RETRIEVAL_REFLEX_WINDOW_TURNS = 'not-a-number';
expect(windowTurnCount(null)).toBe(4);
} finally {
if (prev === undefined) delete process.env.GBRAIN_RETRIEVAL_REFLEX_WINDOW_TURNS;
else process.env.GBRAIN_RETRIEVAL_REFLEX_WINDOW_TURNS = prev;
}
});
}); });
+12 -1
View File
@@ -13,7 +13,7 @@
* envelope paths don't depend on DB state. * envelope paths don't depend on DB state.
*/ */
import { describe, test, expect } from 'bun:test'; import { describe, test, expect, beforeEach } from 'bun:test';
import { import {
EMBEDDING_COST_PER_1K_TOKENS, EMBEDDING_COST_PER_1K_TOKENS,
estimateEmbeddingCostUsd, estimateEmbeddingCostUsd,
@@ -21,9 +21,20 @@ import {
shouldBlockSync, shouldBlockSync,
} from '../src/core/embedding.ts'; } from '../src/core/embedding.ts';
import { lookupEmbeddingPrice } from '../src/core/embedding-pricing.ts'; import { lookupEmbeddingPrice } from '../src/core/embedding-pricing.ts';
import { resetGateway } from '../src/core/ai/gateway.ts';
import { estimateTokens } from '../src/core/chunkers/code.ts'; import { estimateTokens } from '../src/core/chunkers/code.ts';
describe('Layer 8 D1 — embedding cost model', () => { describe('Layer 8 D1 — embedding cost model', () => {
// The estimateEmbeddingCostUsd tests assert the gateway-UNCONFIGURED OpenAI
// fallback ($0.13/Mtok). That fallback only fires when no gateway is
// configured — so guarantee that precondition rather than depending on
// ambient state. Without this, a sibling test file in the same shard that
// configured (and didn't reset) a cheaper model leaks its rate in here and
// these assertions flip (the legacy-embedding preload only restores when the
// gateway slot is EMPTY, so a non-empty foreign config survives).
beforeEach(() => resetGateway());
test('EMBEDDING_COST_PER_1K_TOKENS back-compat constant is the OpenAI 3-large rate', () => { test('EMBEDDING_COST_PER_1K_TOKENS back-compat constant is the OpenAI 3-large rate', () => {
// Retained only for back-compat imports. Live cost math now resolves the // Retained only for back-compat imports. Live cost math now resolves the
// CONFIGURED model's rate via embedding-pricing.ts (see model-aware test // CONFIGURED model's rate via embedding-pricing.ts (see model-aware test