Skip to content

fix: default an unknown model's context window to 1M, not 200k - #94

Merged
omartelo merged 1 commit into
mainfrom
fix/context-window-model-default
Jul 25, 2026
Merged

omartelo merged 1 commit into
mainfrom
fix/context-window-model-default

Conversation

@omartelo

@omartelo omartelo commented Jul 25, 2026 •

Copy link
Copy Markdown
Collaborator

An Opus 5 session read 6% of its context as 30% in the footer.

What was wrong

The window the percent is measured against came from a table listing the 1M
models. opus-5 was not in it, so the read fell through to inferring a window
from the token count — the smallest standard size the session still fit inside,
which is 200k until a session passes 200k tokens. Hence 5×.

Adding the missing row would have fixed this launch and left the next one
broken. The table's default was the actual bug: it enumerated the large
windows and assumed small for anything unlisted, so every model release needed a
release here to read right.

What changed

Inverted. Haiku is the 200k exception, everything else is 1M:

func windowForModel(model string, tokens int) int {
	if strings.Contains(model, "haiku") && tokens <= smallWindow {
		return smallWindow
	}
	return defaultWindow
}

The bet is that an unknown id is a model newer than the binary rather than a
legacy one, and it holds — every Claude generation since 4.6 ships at 1M. A
token count past 200k disproves the Haiku guess (a window cannot hold more than
its size) and widens, so a future Haiku at 1M corrects itself.

That collapsed modelWindows, contextWindows and contextWindowFor into the
four lines above: 42 insertions, 70 deletions. The deprecated ids the old
table carried went with them — nothing in a provider's model picker offers them
today.

Two tests changed because the contract did, not to stay green: an unlisted model
now takes 1M instead of the smallest window that fits. The deprecated-model rows
were dropped for the same reason as the code.

The CLAUDE.md ceiling for this went too. That section carries what breaks if
nobody knows it; an approximate readout does not qualify, and the mechanism now
lives in the comment on windowForModel, where someone changing it will read it.

Tradeoff

A session on a still-live 200k model outside the exception (Sonnet 4.5, Opus
4.5) now reads low. The widen guard only ever widens, never narrows. Accepted:
those are not what the providers spawn today, and under-reading a model nobody
runs beats a table that needs a release per model launch.

Not done here, deliberately

Reading the exact window per session. Checked and ruled out:

Source Why not
Transcript JSONL schema dump confirms no window field anywhere — only token counts
claude CLI no models subcommand
statusLine (context_window.context_window_size) exact, but the slot belongs to the user's own status line
Models API (max_input_tokens) authoritative, but needs auth + network, and still cannot see a window capped below the model's native size

So a 1M-capable model held at 200k still reads low. Same ceiling as before this
PR, now stated in the code rather than the prompt.

Test plan

  • go test ./... — 400 pass
  • go vet ./... and gofmt -l . clean
  • cross-compile loop: GOOS=linux|darwin|windows go build ./... && go vet ./...
  • smoke in task dev: footer reads ~6% on an Opus 5 session, not ~30% — verified manually before merge

An Opus 5 session read 6% of its context as 30% in the footer. The window the
percent is measured against came from a table listing the 1M models; `opus-5`
was not in it, so the read fell through to inferring a window from the token
count — the smallest standard size the session still fit inside, which is 200k
until a session passes 200k tokens.

Adding the missing row would have fixed this launch and left the next one
broken. The table's default was the actual bug: it enumerated the large windows
and assumed small for anything unlisted, so every model release needed a release
here to read right.

Inverted. Haiku is the 200k exception and everything else is 1M, which is what a
model released after the build reads with no new version. The bet is that an
unknown id is a model newer than the binary rather than a legacy one, and it
holds — every Claude generation since 4.6 ships at 1M. A token count past 200k
disproves the Haiku guess (a window cannot hold more than its size) and widens,
so a future Haiku at 1M corrects itself.

That collapsed `modelWindows`, `contextWindows` and `contextWindowFor` into four
lines. The deprecated ids the old table carried went with them: nothing in a
provider's model picker offers them today, and the entries were noise. The
tradeoff is that a session on a still-live 200k model outside the exception
(Sonnet 4.5, Opus 4.5) now reads low — the widen guard only ever widens.

Two tests changed because the contract did, not to stay green: an unlisted model
now takes 1M instead of the smallest window that fits. The deprecated-model rows
were dropped for the same reason as the code.

The `CLAUDE.md` ceiling for this went too. That section carries what breaks if
nobody knows it; an approximate readout does not qualify, and the mechanism now
lives in the comment on `windowForModel` where it is read.

Not done here, deliberately: reading the exact window per session. It exists
only in the provider's status-line payload — a slot the user's own status line
owns — and the Models API's `max_input_tokens` needs auth and network and still
cannot see a window capped below the model's native size.
@omartelo
omartelo merged commit 4e996e3 into main Jul 25, 2026
3 checks passed
@omartelo
omartelo deleted the fix/context-window-model-default branch July 25, 2026 21:38
@omartelo omartelo mentioned this pull request Jul 25, 2026
6 tasks done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant