Skip to content

The picker cache the old rows came from is taken away, not just stopped being written (#1185) - #1249

Merged
gen16k merged 1 commit into
mainfrom
fix/1185-retired-picker-cache
Sep 6, 2026
Merged

gen16k merged 1 commit into
mainfrom
fix/1185-retired-picker-cache

Conversation

@gen16k

@gen16k gen16k commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

#1185 stopped writing Claude Code's discovery cache and the flag that makes the picker read it. It did not take either away — the flag is scrubbed at the next root waired claude enable, and the file was never removed. So a host that upgrades and has not re-run enable as root carries a stale cache the picker still reads.

Found on sv-mag while doing L97's on-device after-check, and reproduced against the real client before fixing.

The state a host is actually in

sv-mag, right now:

$ cat ~/.claude/cache/gateway-models.json
{"baseUrl":"http://127.0.0.1:9472","fetchedAt":1788458345856,"models":[
 {"id":"claude-waired-auto","display_name":"Waired — 200k (any of your devices)"},
 {"id":"anthropic-waired-local","display_name":"Waired local (this device)"},
 {"id":"claude-waired-peer","display_name":"Waired peer (another device)"}]}

$ grep DISCOVERY /etc/claude-code/managed-settings.json
    "CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY": "1",

Written 2026-09-03. Both survive an upgrade, and both are removed only by a root waired claude enable.

What that looks like — measured, not inferred

Claude Code 2.1.261, an isolated CLAUDE_CONFIG_DIR holding that cache and a modelPicker lineup, discovery enabled, /model opened in a pty:

row on screen
ZZOLD-Waired-auto (cache) yes
ZZOLD-Waired-local (cache) yes
ZZOLD-Waired-peer (cache) yes
ZZNEW-Waired (modelPicker) yes
ZZNEW-Waired-local (modelPicker) yes

Both lists. Three of the rows are the same destinations under their old names.

The fix

RemoveRetiredCache in internal/integration/claudecode/retiredcache.go, called from the per-user picker write. That is the half that needs no elevation and runs on every claude launch through the SessionStart hook, so it closes the window without waiting for anybody to re-run enable as root; by the time the root half scrubs the flag there is nothing left to read. waired claude disable clears it too.

Ownership, the same discipline as the rest of #1185: only a document that names this gateway and whose every row is a Waired id. A cache describing some other gateway is somebody else's — and Claude Code ignores it anyway, since it compares baseUrl against the live ANTHROPIC_BASE_URL by exact string, so the narrow rule is also the complete one. Absent, unreadable, unparseable and foreign are all left alone without an error: this runs inside a hook on every launch, and a surprise there would be a failure on a path whose whole job is best-effort.

Verified on sv-mag with a build of this branch

-rw------- 1 gen16k gen16k 306 Sep  3 17:59 /home/gen16k/.claude/cache/gateway-models.json
Wrote Claude Code /model rows: /home/gen16k/.claude/settings.json
ls: cannot access '/home/gen16k/.claude/cache/gateway-models.json': No such file or directory

and the rows it wrote are the new ones, with per-peer entries from the live mesh:

waired, waired/local, waired/peer,
waired/peer-pc-mbp14-m5-local, waired/peer-sv-macmini-local

Checks

go build ./..., go test ./..., gofmt -l, golangci-lint run --max-same-issues=0. New table test covers ours / a different gateway / a foreign model id / one waired id among foreign ones / an empty list / not JSON / no live base URL / no file / CLAUDE_CONFIG_DIR.

docs-not-needed: the cache file is named to users nowhere on the site — #1241 already rewrote the one troubleshooting section that mentioned it, and this change only deletes a file waired itself wrote.

Refs #1185
Refs waired-ai/waired#1313

🤖 Generated with Claude Code

https://claude.ai/code/session_01HJUUSmAfbRdjER1seDdjXm

…ed being written (#1185)

Found on sv-mag while checking #1185 on a real host, and reproduced
against the real client before fixing.

Before #1185 the Waired rows reached the /model picker by waired writing
Claude Code's own discovery cache and setting the flag that makes the
picker read it. #1185 stopped writing both. It did not take either away
— the flag is scrubbed at the next root `waired claude enable`, and the
file was not removed at all.

So a host that upgrades and has not re-run enable as root carries a
stale cache that Claude Code still reads. Measured on 2.1.261: the
picker shows BOTH, the three old rows under their old names and the new
ones beside them. sv-mag is in exactly that state today — a cache
written on 2026-09-03 naming claude-waired-auto, anthropic-waired-local
and claude-waired-peer.

The per-user picker write takes it away now. That is the half that needs
no elevation and runs on every `claude` launch through the SessionStart
hook, so it closes the window without waiting for anybody to re-run
enable; by the time the root half scrubs the flag there is nothing left
to read.

Ownership as everywhere else: only a document that names THIS gateway
and whose every row is a Waired id. A cache describing some other
gateway is somebody else's — and Claude Code ignores it anyway, since it
compares baseUrl against the live ANTHROPIC_BASE_URL by exact string.
Absent, unreadable, unparseable or foreign are all left alone without an
error, because this runs inside a hook on every launch.

Verified on sv-mag with a build of this branch: the file was there
before the write and gone after it, and the rows in settings.json are
the new ones.

Refs #1185
Refs waired-ai/waired#1313

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJUUSmAfbRdjER1seDdjXm
Signed-off-by: gen16k <gen16k@users.noreply.github.com>
@gen16k
gen16k merged commit e2c606e into main Sep 6, 2026
37 of 39 checks passed
@gen16k
gen16k deleted the fix/1185-retired-picker-cache branch September 6, 2026 05:59
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