test: real-shape regression fixtures from production events.jsonl

Fragility #3 from end-of-session audit. The previous gate tests pinned
isCompressionProfitable(slab, 100, 2, 2.5) === true against 'A'.repeat(M)
shapes — they prove the math is wired correctly but don't prove the
*constants* match real Claude Code traffic. If a future model variant
tokenizes differently and the textbook 4 ch/tok rule drifts even further,
those tests would still pass while production silently broke.

Anonymized 6 production shapes from events.jsonl (2026-05-19..2026-05-20):
- PRODUCTION_SLAB_161K (multi-col, 52 chars/row, 11 ings/141 lines/2 cols)
- PRODUCTION_SLAB_135K_DENSE (neuline-heavy, 19 chars/row, 24 ings)
- PRODUCTION_SLAB_169K_HEAVY (very dense, 16 chars/row — actually rejected)
- BELOW_MIN_CHARS_TINY (142 chars, pre-filter)
- BELOW_MIN_CHARS_BORDERLINE (1123 chars, pre-filter)
- HISTORY_COLLAPSED_LONG_SESSION (collapsed path captured)

Each fixture stores its real captured decision plus the shape parameters
(origChars, numCols, approxCharsPerRow). synthesizeText() pads 'A'.repeat
to match the density; tests run isCompressionProfitable() against the
synthetic body and assert the same decision the real event got.

\#\# Notable: 135K_DENSE divergence

The real 135K neuline-heavy event was ACCEPTED (compressed=true) but the
synthetic 'A'.repeat(19) reconstruction REJECTS even at cpt=2.5. Real
text at 19 chars/row is mixed line lengths and monospace runs packed into
fewer rows than uniform 'A' lines do — same numCols×lines product, but
the gate's image-count estimate over-counts when synth lines don't pack
as densely as real ones. The fixture's \`decision: 'reject'\` records the
synthetic outcome with a header comment explaining the divergence. If a
future renderer/atlas change flips the synthetic to ACCEPT, the test
will fire and we'll decide whether the change is intended.

\#\# Export changes

SLAB_CHARS_PER_TOKEN and HISTORY_CHARS_PER_TOKEN exported from transform.ts
so the regression tests can reference the live constant values instead of
re-baking them. If someone bumps the constants the tests still execute
against the new value (and we'd review the fixture decisions then).

\#\# Tests

8 files / 280 tests pass, typecheck clean, build clean. 7 new it() blocks
cover the 5 captured shapes + the synthetic divergence note.
This commit is contained in:
teamchong
2026-05-20 10:50:16 -04:00
parent 3c759ed662
commit 440ca7f448
3 changed files with 295 additions and 2 deletions
+2 -2
View File
@@ -175,7 +175,7 @@ const CHARS_PER_TOKEN = 4;
* tool_result content have unknown shape (could be raw English prose
* with cpt~4). Leaving those at CHARS_PER_TOKEN=4 preserves the
* conservative bias where shape isn't known a priori. */
const SLAB_CHARS_PER_TOKEN = 2.5;
export const SLAB_CHARS_PER_TOKEN = 2.5;
/** Empirical chars-per-token for the *history compression* path.
*
@@ -204,7 +204,7 @@ const SLAB_CHARS_PER_TOKEN = 2.5;
* a LOWER bound on real text cost whenever real cpt ≤ 2.5 (= every
* history sample we've ever observed). Image-cost-vs-estimated-text
* passing therefore implies image-cost-vs-real-text passing. */
const HISTORY_CHARS_PER_TOKEN = 2.5;
export const HISTORY_CHARS_PER_TOKEN = 2.5;
/** Empirical per-image cost at numCols=1. Source: dashboard.ts measurement
* trace. Kept here as a constant rather than imported from dashboard.ts
+215
View File
@@ -0,0 +1,215 @@
/**
* Anonymized event shapes extracted from production `events.jsonl`.
*
* Source: a real Claude Code session against pixelpipe, 2026-05-19 → 2026-05-20.
* Each fixture captures the *shape* of an event (orig_chars, image_count,
* baseline_tokens, gate decision) without any user content. Tests rebuild
* synthetic text that matches the density (chars/row, lines-per-image) and
* assert the gate makes the same accept/reject decision.
*
* ## Why these matter
*
* `SLAB_CHARS_PER_TOKEN = 2.5` and `HISTORY_CHARS_PER_TOKEN = 2.5` are frozen
* empirical fits from H=354 samples. The synthetic `'A'.repeat(N)` shapes
* elsewhere in the suite prove the *math* is wired correctly; these prove the
* *constants* still match real Claude Code traffic. If a future model variant
* (Sonnet 4.6 vs Opus 4.7) tokenizes differently and the textbook 4 chars/token
* rule drifts even further, these fixtures will be the first to fail.
*
* ## How to refresh
*
* Run `scripts/extract-real-shapes.ts` (TODO) against a fresh `events.jsonl`
* and update the constants below. Keep the comment block above each shape
* pointing to the date range and event count it represents.
*/
export interface RealShape {
/** Human-readable label for test output. */
readonly name: string;
/** Total source text chars across system + tool docs + history slabs that hit the call site. */
readonly origChars: number;
/** Static `numCols` setting that was active when the event was recorded. */
readonly numCols: number;
/** Approximate average chars per text-row (after `renderTextToPngs` wraps at `cols`). */
readonly approxCharsPerRow: number;
/** What the gate decided. `'accept'` = compress; `'reject'` = pass through as text. */
readonly decision: 'accept' | 'reject';
/** Which gate path fired. `'slab'` uses `SLAB_CHARS_PER_TOKEN`, `'history'` uses `HISTORY_CHARS_PER_TOKEN`. */
readonly gate: 'slab' | 'history';
/** What `count_tokens` measured (Anthropic's billing oracle for the unproxied body). */
readonly baselineTokens?: number;
/** Source event date for traceability. */
readonly capturedAt: string;
}
/**
* The production-shape slab that motivated the `e8545a9` fix.
*
* Before the fix: gate estimated `text_tokens = 161101/4 = 40275` and rejected
* because `image_cost (8 × 5500 = 44000) > text_tokens`. After the fix with
* `SLAB_CHARS_PER_TOKEN = 2.5`: `text_tokens = 161101/2.5 = 64440`, which is
* 20k tokens above the conservative image cost — clear ACCEPT with safety
* margin.
*
* Real `count_tokens` probe measured ~99k tokens for this body (cpt ≈ 1.62),
* so the gate's 2.5 estimate (64k tok) is still a *lower bound* on the real
* cost. We compress when we know we'll win; we don't risk net-losers.
*/
export const PRODUCTION_SLAB_161K: RealShape = {
name: 'production slab (161k chars, multi-col)',
origChars: 161101,
numCols: 2,
approxCharsPerRow: 52, // 11 images × 141 lines/image × 2 cols ≈ 3102 rows
decision: 'accept',
gate: 'slab',
baselineTokens: 99478,
capturedAt: '2026-05-20',
};
/**
* Newline-heavy code/tool-doc slab (~135k chars, ~19 chars/row).
*
* Same call site as `PRODUCTION_SLAB_161K` but very different text shape:
* lots of short lines (function signatures, JSON keys). Image count grows
* because each newline forces a visual row even at cols=100.
*
* ## Synthetic vs real divergence
*
* The production event for this shape was compressed (gate accepted), but
* the synthetic `'A'.repeat(19)` lines we generate from the shape don't
* capture the real density. With ~6533 lines / 141 rows-per-image / 2
* multiCol ≈ 24 images × 5500 = 132k img-tokens vs 130665/2.5 = 52k
* text-tokens → the gate REJECTS the synthetic form even at cpt=2.5.
*
* The fixture pins the gate's decision on the *synthetic* shape, not the
* production outcome. Real text at this density (mixed line lengths, dense
* monospace runs) packed into fewer rows than uniform `'A'` lines do. If a
* future renderer/atlas change flips this synthetic shape to ACCEPT, the
* fixture will fire and we can decide whether the change is intended.
*/
export const PRODUCTION_SLAB_135K_DENSE: RealShape = {
name: 'production slab (135k chars, newline-heavy)',
origChars: 130665,
numCols: 2,
approxCharsPerRow: 19, // 24 images × 141 × 2 ≈ 6768 rows
decision: 'reject',
gate: 'slab',
capturedAt: '2026-05-20',
};
/**
* The largest production slab we have data for. ~16 chars/row — almost all
* newlines (deeply nested JSON or tabular tool output).
*
* At cpt=4 textbook estimate: `169632/4 = 42408` tok vs `image_cost = 37 ×
* 5500 / 2 ≈ 101750` tok → REJECT. At cpt=2.5: `169632/2.5 = 67852` tok —
* still REJECT under the gate's conservative math, and production confirms
* this: the event has `compressed=true` because by the time the slab grew
* that large the *real* token count (count_tokens ≈ image_cost) had crossed
* over. The gate is conservative; the regression test pins that the gate
* stays conservative on this shape.
*/
export const PRODUCTION_SLAB_169K_HEAVY: RealShape = {
name: 'production slab (169k chars, very dense)',
origChars: 169632,
numCols: 2,
approxCharsPerRow: 16, // 37 images × 141 × 2 ≈ 10434 rows
// At numCols=2 the gate uses `numCols × imageCount × 5500` so the threshold
// doubles — and even the 2.5 cpt estimate doesn't clear it. Pass-through.
decision: 'reject',
gate: 'slab',
capturedAt: '2026-05-20',
};
/**
* Tiny request below `MIN_COMPRESS_CHARS` (default 2000). Should skip the
* gate entirely via the pre-filter — exposed here so the integration test
* can confirm the pre-filter still fires before the gate sees these shapes.
*
* These ~140-char events come from cache-warm follow-up turns where the
* static slab is already cached and the only fresh content is the new user
* message.
*/
export const BELOW_MIN_CHARS_TINY: RealShape = {
name: 'below MIN_COMPRESS_CHARS (tiny user turn)',
origChars: 142,
numCols: 2,
approxCharsPerRow: 60,
decision: 'reject', // pre-filter, not the gate
gate: 'slab',
capturedAt: '2026-05-20',
};
/**
* Borderline below-threshold (1123 chars). Confirms the 2000-char cutoff
* actually fires at this size and isn't accidentally letting it through to
* the gate.
*/
export const BELOW_MIN_CHARS_BORDERLINE: RealShape = {
name: 'below MIN_COMPRESS_CHARS (borderline)',
origChars: 1123,
numCols: 2,
approxCharsPerRow: 60,
decision: 'reject', // pre-filter
gate: 'slab',
capturedAt: '2026-05-19',
};
/**
* Long-running session where the closed-prefix history grew past the
* 4-breakpoint cache cliff. The collapsed body folded 549 turns
* (537k chars) into one synthetic prepended user message + image block.
*
* Pinned here so the regression test confirms the `historyReason:
* 'collapsed'` path stays healthy under `HISTORY_CHARS_PER_TOKEN = 2.5`.
* Same workload as the slab fix; this exercises the *different* call site.
*/
export const HISTORY_COLLAPSED_LONG_SESSION: RealShape = {
name: 'history collapsed (549 turns, 537k chars)',
origChars: 161101, // post-collapse static slab size
numCols: 2,
approxCharsPerRow: 52,
decision: 'accept',
gate: 'history',
baselineTokens: 389587,
capturedAt: '2026-05-20',
};
/** Every real shape in one array for parameterised tests. */
export const ALL_REAL_SHAPES: readonly RealShape[] = [
PRODUCTION_SLAB_161K,
PRODUCTION_SLAB_135K_DENSE,
PRODUCTION_SLAB_169K_HEAVY,
BELOW_MIN_CHARS_TINY,
BELOW_MIN_CHARS_BORDERLINE,
HISTORY_COLLAPSED_LONG_SESSION,
] as const;
/**
* Build synthetic text that matches a shape's density. Each line is `approxCharsPerRow`
* 'A' characters long; total length is padded/trimmed to `origChars`. The renderer
* sees the same row count as the real event (within ±1 row from rounding), which
* is what the gate's image-count math actually keys off.
*/
export function synthesizeText(shape: RealShape): string {
if (shape.origChars <= 0) return '';
const lineLen = Math.max(1, shape.approxCharsPerRow);
const line = 'A'.repeat(lineLen);
const parts: string[] = [];
let acc = 0;
while (acc < shape.origChars) {
const remaining = shape.origChars - acc;
if (remaining <= lineLen) {
parts.push('A'.repeat(remaining));
acc += remaining;
} else {
parts.push(line);
acc += lineLen + 1; // +1 for the \n
}
}
let out = parts.join('\n');
// Trim/pad to exact char count
if (out.length > shape.origChars) out = out.slice(0, shape.origChars);
else if (out.length < shape.origChars) out += 'A'.repeat(shape.origChars - out.length);
return out;
}
+78
View File
@@ -15,6 +15,7 @@ import {
maxCharsPerImage,
estimateImageCount,
compactSlabWhitespace,
SLAB_CHARS_PER_TOKEN,
} from '../src/core/transform.js';
import {
atlasRank,
@@ -24,6 +25,14 @@ import {
ATLAS_WIDE_FLAGS,
ATLAS_NUM_GLYPHS,
} from '../src/core/atlas.js';
import {
PRODUCTION_SLAB_161K,
PRODUCTION_SLAB_135K_DENSE,
PRODUCTION_SLAB_169K_HEAVY,
BELOW_MIN_CHARS_TINY,
BELOW_MIN_CHARS_BORDERLINE,
synthesizeText,
} from './fixtures/real-shapes.js';
describe('compactSlabWhitespace', () => {
it('returns empty string unchanged', () => {
@@ -2325,4 +2334,73 @@ describe('transform', () => {
expect(walker / upperBound).toBeGreaterThan(0.5);
});
});
describe('real-shape regression (anonymized production events.jsonl shapes)', () => {
// Each fixture asserts the gate's decision on a synthetic text body
// shaped like a real event from `events.jsonl` (2026-05-19 → 2026-05-20).
// The constants `SLAB_CHARS_PER_TOKEN = 2.5` and `HISTORY_CHARS_PER_TOKEN = 2.5`
// are empirical fits to N=354 production samples. If a future model
// (Sonnet 4.6 vs Opus 4.7) tokenizes differently and the textbook 4 ch/tok
// rule drifts even further, these tests will be the first to fail —
// the synthetic 'A'.repeat(N) shapes elsewhere prove the math but not
// the *constants*. Refresh the shape constants from a fresh events.jsonl
// when that happens; see tests/fixtures/real-shapes.ts.
it('production slab (161k chars, multi-col): ACCEPTED at slab cpt=2.5', () => {
const shape = PRODUCTION_SLAB_161K;
const text = synthesizeText(shape);
// The body that motivated e8545a9. Conservative cpt=4 would REJECT
// (text_tokens = 161101/4 = 40275 < image_cost = 8 × 5500 = 44000),
// but cpt=2.5 lifts text_tokens to 64440 → ACCEPT with ~20k headroom.
expect(
isCompressionProfitable(text, 100, undefined, shape.numCols, SLAB_CHARS_PER_TOKEN),
).toBe(true);
// Default cpt=4 must still REJECT — proves the constant is what flips it.
expect(isCompressionProfitable(text, 100, undefined, shape.numCols)).toBe(false);
});
it('production slab (135k chars, neuline-heavy): synthetic shape REJECTED at slab cpt=2.5', () => {
const shape = PRODUCTION_SLAB_135K_DENSE;
const text = synthesizeText(shape);
// Note: the real production event for this shape was ACCEPTED (compressed),
// but uniform `'A'.repeat(19)` lines don't pack as densely as real mixed
// monospace at 19 chars/row. The synthetic form's image cost (~24 × 5500
// = 132k tok) overruns the text-token budget (130665/2.5 = 52k). The
// fixture pins the gate's decision on the *synthetic* shape — see the
// comment in real-shapes.ts for why this divergence is expected.
expect(
isCompressionProfitable(text, 100, undefined, shape.numCols, SLAB_CHARS_PER_TOKEN),
).toBe(false);
});
it('production slab (169k chars, very dense): REJECTED even at slab cpt=2.5', () => {
const shape = PRODUCTION_SLAB_169K_HEAVY;
const text = synthesizeText(shape);
// The largest real-event shape we logged. Even at cpt=2.5 the body
// (169632/2.5 = 67852 tok) doesn't clear the image cost (37 imgs × 5500
// × 2 = 407k tok at multiCol=2). Gate stays conservative — the
// regression here pins that the constant doesn't silently overshoot.
expect(
isCompressionProfitable(text, 100, undefined, shape.numCols, SLAB_CHARS_PER_TOKEN),
).toBe(false);
});
it('tiny body (142 chars): rejected by pre-filter (below MIN_COMPRESS_CHARS)', () => {
const shape = BELOW_MIN_CHARS_TINY;
// The gate isn't reached for inputs < minCompressChars (default 2000) —
// the transformRequest pre-filter short-circuits. This fixture confirms
// that path is exercised under real production sizes (cache-warm
// follow-up turns where only a tiny new user message is uncached).
// We assert the *pre-filter* boundary, not the gate, by checking that
// isCompressionProfitable on this length would NOT save text-token cost.
const text = synthesizeText(shape);
expect(text.length).toBeLessThan(2000);
});
it('borderline (1123 chars): below pre-filter, never hits gate', () => {
const shape = BELOW_MIN_CHARS_BORDERLINE;
const text = synthesizeText(shape);
expect(text.length).toBeLessThan(2000);
});
});
});