fix(gate): discriminate raw opts to fix override-gate default-value collision

The override-gate previously used `o.charsPerToken !== CHARS_PER_TOKEN`
to detect "host pinning". After CHARS_PER_TOKEN=4 was replaced with
slab/history-specific 2.5 constants, a host that deliberately passes the
literal `4` (the same as DEFAULTS) was silently swapped to 2.5 - the
gate couldn't distinguish "host passed nothing" from "host passed 4".

Fix: check the raw opts (\`opts.charsPerToken !== undefined\`) instead
of comparing the merged value against a sentinel. Host-supplied `4` now
flows through as `4`, even though it matches the default.

Tests pin both directions:
- slab path (render.test.ts): explicit cpt=4 keeps the 161k slab
  REJECTED at cpt=4 (would be ACCEPTED at the built-in 2.5)
- history path (history.test.ts): explicit cpt=4 keeps a 14-turn closed
  conversation REJECTED at cpt=4 (would collapse at the built-in 2.5),
  with companion cpt=2.5 test proving the fixture actually straddles

275 tests pass, typecheck clean, build clean.
This commit is contained in:
teamchong
2026-05-20 09:58:31 -04:00
parent 3173e8ad92
commit ea32dd2ed5
3 changed files with 86 additions and 12 deletions
+9 -4
View File
@@ -1402,8 +1402,10 @@ export async function transformRequest(
// English-prose default 4 baked into CHARS_PER_TOKEN. Use a slab-specific
// upper-bound cpt at this gate so JSON-dense system + tool-doc content
// gets a fair break-even check. Host can still override via
// `o.charsPerToken` (e.g., to plug in a live empirical fit).
const slabCpt = o.charsPerToken !== undefined && o.charsPerToken !== CHARS_PER_TOKEN
// `opts.charsPerToken` (e.g., to plug in a live empirical fit).
// Discriminate on the *raw* `opts` so a host that genuinely wants the
// English-prose `4` can pin to it without colliding with the merged default.
const slabCpt = opts.charsPerToken !== undefined
? o.charsPerToken
: SLAB_CHARS_PER_TOKEN;
if (!isCompressionProfitable(combined, o.cols, undefined, numCols, slabCpt)) {
@@ -1701,9 +1703,12 @@ export async function transformRequest(
// count after wrapping.
// History cpt is empirically ~1.09 (N=10 rejected-events sample) — JSON-
// dense like the slab, so use the same conservative upper-bound cpt
// baked into HISTORY_CHARS_PER_TOKEN=2.5. Host override (o.charsPerToken)
// baked into HISTORY_CHARS_PER_TOKEN=2.5. Host override (opts.charsPerToken)
// wins if the dashboard ever feeds back a live empirical fit.
const historyCpt = o.charsPerToken !== undefined && o.charsPerToken !== CHARS_PER_TOKEN
// Same discriminator as the slab path: check the *raw* `opts` so a host
// that genuinely wants `4` can pin to it without colliding with the merged
// default.
const historyCpt = opts.charsPerToken !== undefined
? o.charsPerToken
: HISTORY_CHARS_PER_TOKEN;
const historyProfitable = (text: string, cols: number): boolean =>
+39 -8
View File
@@ -474,14 +474,13 @@ describe('transformRequest history compression (always-on)', () => {
expect(info.historyReason).toBe('collapsed');
expect(info.collapsedTurns).toBe(10);
// Counterfactual: a host override slightly above 4 (= "English-prose
// worse than the static default") forces the gate back into rejection
// territory. We use 4.5 rather than 4 because passing exactly the
// DEFAULTS value (4) collides with the `!== CHARS_PER_TOKEN` branch
// and gets re-rewritten to HISTORY_CHARS_PER_TOKEN — by design, so
// unspecified-host requests still get the empirical cpt. 4.5 bypasses
// that override gate and confirms the fix actually flows through the
// cpt argument, not some other side-effect.
// Counterfactual: a host override above 4 (= "English-prose territory,
// but worse than the historical default") forces the gate back into
// rejection — confirms the fix actually flows through the `cpt`
// argument, not some other side-effect. After fragility #2 (override-
// gate default-value collision), the override-gate uses `!== undefined`
// so 4.5 is honored as an explicit override on its own merits, not
// because it differs from any sentinel.
const stale = await transformRequest(mkBody(msgs, bigPlain(80_000)), {
charsPerToken: 4.5,
});
@@ -489,6 +488,38 @@ describe('transformRequest history compression (always-on)', () => {
expect(stale.info.collapsedTurns).toBeUndefined();
});
it('explicit charsPerToken=4 is honored end-to-end (no silent swap to constants)', async () => {
// Regression for fragility #2: the override-gate previously used a
// `!== CHARS_PER_TOKEN` check that silently swapped 4 → SLAB_CHARS_PER_TOKEN
// (2.5) for unspecified hosts. That coupling broke the distinction
// between "host didn't override" and "host deliberately wants 4". The
// gate now uses `!== undefined` so a literal 4 stays a literal 4.
//
// Observable proof: a borderline-density fixture (1200-char bodies × 14
// turns) that is rejected at cpt=4 but accepted at cpt=2.5. If the gate
// silently swapped explicit 4 → 2.5, this fixture would collapse — but
// with the fix it stays rejected, confirming the gate honored the literal
// 4. The companion test below pins cpt=2.5 collapse on the same shape.
const msgs: Message[] = [];
for (let i = 0; i < 14; i++) {
const body = `turn ${i}: ` + bigPlain(1200);
msgs.push(i % 2 === 0 ? usr(body) : asst(body));
}
const explicit4 = await transformRequest(mkBody(msgs, bigPlain(80_000)), {
charsPerToken: 4,
});
expect(explicit4.info.historyReason).toBe('not_profitable');
expect(explicit4.info.collapsedTurns).toBeUndefined();
// Same shape at cpt=2.5 collapses — proves the fixture actually straddles
// the threshold and isn't a tautology.
const explicit25 = await transformRequest(mkBody(msgs, bigPlain(80_000)), {
charsPerToken: 2.5,
});
expect(explicit25.info.historyReason).toBe('collapsed');
expect(explicit25.info.collapsedTurns).toBe(10);
});
it('history-image blocks carry NO cache_control (conservative first-cut)', async () => {
const msgs: Message[] = [];
for (let i = 0; i < 14; i++) {
+38
View File
@@ -1900,6 +1900,44 @@ describe('transform', () => {
expect(isCompressionProfitable(slab, 100, undefined, 2, 2.5)).toBe(true);
});
it('TransformOptions.charsPerToken: explicit 4 is honored (no silent swap to SLAB_CHARS_PER_TOKEN)', async () => {
// Fragility #2 regression: the override-gate previously used a `!==
// CHARS_PER_TOKEN` check that silently swapped 4 → 2.5 because the static
// default *also* happens to be 4. After the fix it uses `!== undefined`,
// so passing exactly 4 is honored as an explicit override.
//
// Observable proof: the 161k production-shape slab is REJECTED at cpt=4
// (text=40,275 tok < image=44,000 tok) and ACCEPTED at the built-in
// SLAB_CHARS_PER_TOKEN=2.5 (text=64,440 tok). If the collision bypass
// breaks, the slab will compress under an explicit `4` — which would
// mean the host can't ever pin to the conservative English-prose value.
const parts: string[] = [];
let acc = 0;
const target = 161_101;
while (acc < target) {
const len = 60 + (acc % 40);
parts.push('A'.repeat(len) + (acc % 200 === 0 ? ' ' : ''));
acc += len + 1;
}
const slab = parts.join('\n').slice(0, target);
const req = JSON.stringify({
model: 'claude-3-5-sonnet',
messages: [{ role: 'user', content: 'hi' }],
system: slab,
});
const bytes = new TextEncoder().encode(req);
// Built-in cpt (no override): slab compresses via SLAB_CHARS_PER_TOKEN=2.5.
const builtin = await transformRequest(bytes, { multiCol: 2 });
expect(builtin.info.compressed).toBe(true);
// Explicit cpt=4: host pinned to the English-prose value. The slab gate
// must honor it and reject the slab — not silently fall back to 2.5.
const overridden = await transformRequest(bytes, { multiCol: 2, charsPerToken: 4 });
expect(overridden.info.compressed).toBe(false);
expect(overridden.info.reason).toMatch(/^not_profitable/);
});
// --- Adaptive break-even: CHARS_PER_IMAGE derived from atlas cell, not hardcoded ---
// Brief: when font-rater swaps to a smaller cell (e.g. Cozette 4×7), more chars
// pack into one image, so the N-image break-even thresholds shift. Tests below