Files
Deval Shah 9199a8d0df fix(eval): opus-density — commit live issue #6 results, fix two response-reading bugs
Ran eval/opus-density/run.mjs against the API (harness shipped without committed
results). Issue #6 answer: Opus 4.8 exact-string recall rises monotonically as the
render gets less dense — 1/4 -> 3/4 -> 4/4 across 5x8/7x10/9x12, reaching 4/4 exact
with 0 confabulations at 9x12 (45% savings), byte-exact hex, stop_reason end_turn.
Density is the lever; not a DEFAULT_MODEL_BASES change.

Two response-reading bugs fixed (neither involves images):
- callModel read content[0].text, but always-on-thinking models put a thinking
  block with empty text at content[0]; the answer is in a later block. Every Fable
  answer read as "" and scored miss/confab. Fixed: select the text block;
  max_tokens 128 -> 512.
- score() ignored stop_reason, so a refusal (HTTP 200, stop_reason "refusal",
  empty content) scored as a confabulation, inverting the safety verdict. Fixed:
  treat refusal as its own state; a refused guard is safe.

Adds results.json (live run), RESULTS.md (write-up), verify.mjs (confound-free
receipts for both fixes plus the guard-question caveat: the never-stated password
guard trips Fable's cyber classifier equally on text and image, n=6, so it is not
an imaging effect and is unmeasurable on Fable as written).
2026-07-05 07:55:06 -04:00

75 lines
3.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Opus 4.8 lower-density / larger-cell read sweep
**Question (issue #6):** the known Opus misread rate on pxpipe images was measured
at the production **5×8** cell density. If the same text is rendered *less dense*
(bigger cells → fewer chars/page → more pages), does Opus 4.8 read exact strings
reliably enough to be worth enabling — and at what token cost?
This harness answers that with a measurement, **not** a default change. It does
not touch production code or the model allowlist. Fable 5 stays the only default
reader unless the numbers below clear the acceptance bar.
## What it does
For each render **variant** (cell size) it renders one synthetic "session"
transcript to PNG(s) via the production renderer, then asks each **model** a
fixed battery of questions against the image and scores the answers.
- **Variants** (cell = 5+wBonus × 8+hBonus px, all via `RenderStyle`):
- `5x8` — production density (`{cellWBonus:0, cellHBonus:0}`)
- `7x10``{cellWBonus:2, cellHBonus:2}`
- `9x12``{cellWBonus:4, cellHBonus:4}`
Each variant keeps the ≤1568×728 page cap, so images stay in Anthropic's
linear-billing window (no server-side downscale) and page count rises as
density drops.
- **Models:** `claude-opus-4-8`, `claude-fable-5` (both high-res tier).
- **Tasks** (each answer committed before ground truth is revealed):
1. exact 12-char hex recall
2. camelCase identifier recall
3. file path / CLI flag / number recall
4. gist recall (a decision / value / state that survives lossy reads)
5. never-stated-fact guard — the correct answer is "not stated / UNKNOWN"
## Metrics (per model × variant)
- exact-match accuracy (tasks 13)
- **confabulation count** — a confident *wrong* exact string (the dangerous mode)
- abstain / "not safe to quote" count
- gist accuracy (task 4) and false-answer rate on the guard (task 5)
- image tokens (28-px patch count), text-baseline tokens, and savings %
- rough wall-clock latency
## Acceptance — do NOT claim "Opus works" unless, for a variant:
- gist recall == the text baseline (or within a pre-declared tolerance), AND
- every exact-identifier question is either answered correctly OR abstains /
refers to the factsheet — **zero silent wrong exact strings** on the protected
exact tasks, AND
- token savings stay positive on token-dense Claude-Code-like content.
Only a variant that clears all three is a candidate for an opt-in Opus render
profile. Enabling Opus in `DEFAULT_MODEL_BASES` is explicitly out of scope until
then.
## Run
```bash
# Dry run — renders every variant and prints the token/savings accounting.
# No API key needed; no model is called.
pnpm exec tsx eval/opus-density/run.mjs
# Full run — also calls the models and scores the battery.
ANTHROPIC_API_KEY=sk-ant-... pnpm exec tsx eval/opus-density/run.mjs
```
Results are written to `eval/opus-density/results.json`.
A first live run (2026-07-05) is committed here: `results.json` plus a write-up in
[`RESULTS.md`](./RESULTS.md). Short version — Opus reads **4/4 exact, 0 confab** at
the largest cell (`9x12`, still 45% savings), climbing from 1/4 at production
`5x8`, so the issue-#6 answer is yes and density-dependent. Running it also surfaced
two response-reading bugs in this harness (now fixed in `run.mjs`): the answer of an
always-on-thinking model isn't in `content[0]`, and a classifier refusal was being
scored as a confabulation. `verify.mjs` reproduces both receipts plus the guard-
question caveat.