Skip to content

feat(badge): reflect the model the session is actually using, not the stale config default#1020

Open
SmolSmolStar wants to merge 4 commits into
web3dev1337:mainfrom
SmolSmolStar:feat/badge-live-model
Open

feat(badge): reflect the model the session is actually using, not the stale config default#1020
SmolSmolStar wants to merge 4 commits into
web3dev1337:mainfrom
SmolSmolStar:feat/badge-live-model

Conversation

@SmolSmolStar

Copy link
Copy Markdown
Contributor

Problem

The terminal model badge (#1005) reads the model only from the Claude settings cascade (settings files + env). But /model can switch a running session's model without touching those files — so the badge can show one model while the session is actually running another, with no sign anything changed.

Hit in the wild: the badge said Fable the whole time, but /model showed Opus — the user had been on Opus for most of the session and never knew, because the badge only ever reflected the saved default.

Fix

Read the live model from the running session's transcript. Claude Code records the model on every assistant turn in ~/.claude/projects/<encoded-cwd>/<session>.jsonl, so the newest entry is the true current model even after a mid-session /model switch.

  • resolveClaudeConfig now prefers the live transcript model and falls back to the config-derived model when no transcript can be resolved (safe degradation to today's behaviour).
  • Reads only the tail (64 KB) of the transcript, so multi-MB transcripts stay cheap; skips synthetic placeholder models.
  • Effort still comes from config (it isn't recorded per-turn, and it hot-reloads from the settings file anyway — the model was the real trap).
  • The project-folder encoding (<cwd> → dashes for every non-alphanumeric char) matches Claude Code's naming.

Caveats (intentional)

  • Reflects the model as of the last assistant turn — it updates each turn, not the instant /model is opened. Still accurate to what's actually running, versus today's stale guess.
  • Loses the [1m] context suffix the config model can carry (the transcript stores the plain model id). The model identity — the thing that was confusing — is correct.

Test plan

  • Verified end-to-end against a real running session: returned claude-opus-4-8 (matching /model) where the config default was claude-fable-5.
  • Adds unit tests: path encoding, tail model read (incl. skipping <synthetic>), live-over-config preference, and no-transcript fallback.
  • Full agentModelConfigService suite green (23/23), no regressions.

🤖 Generated with Claude Code

SmolSmolStar and others added 2 commits July 13, 2026 02:40
…nfig default

The terminal model badge read the model only from the Claude settings cascade
(settings files + env). But a user can switch models mid-session with /model, which
never touches those files — so the badge could show "Fable" while the session was
actually running Opus, with no indication anything had changed. (Hit in the wild:
badge said Fable the whole time; /model showed Opus.)

Read the live model from the running session's transcript instead. Claude Code records
the model on every assistant turn in ~/.claude/projects/<encoded-cwd>/<session>.jsonl,
so the newest entry reflects the true current model even after a /model switch.
resolveClaudeConfig now prefers that live model and falls back to the config-derived
model when no transcript can be resolved. Effort still comes from config (it isn't in
the transcript, and it hot-reloads from the settings file anyway).

Implementation reads only the tail (64 KB) of the transcript so multi-MB files stay
cheap, and skips synthetic placeholder models.

Verified end-to-end against a real running session (returned claude-opus-4-8, matching
/model, where the config default was claude-fable-5). Adds unit tests for the path
encoding, tail model read (incl. skipping synthetic), live-over-config preference, and
the no-transcript fallback. Full service suite green (23/23).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ing regex

Review findings addressed:
- The regex tail-scan matched any "model":"..." substring, including model
  params inside message content (sub-agent spawns, quoted JSON) — verified
  against a real transcript where it would report a sub-agent's model. Now
  whole JSONL lines are parsed newest-first and only an assistant turn's
  top-level message.model counts.
- Live-model lookups are now cached per directory with the same TTL as the
  file cache, so per-session badge polling doesn't re-scan the project dir
  and stat every historical transcript each time.
- The cwd->project-folder encoder is hoisted to utils/pathUtils and shared
  with sessionRecoveryService instead of being a fourth private copy.
- The model badge tooltip no longer claims session-only /model picks can't
  be shown — transcript detection is exactly what surfaces them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@web3dev1337

Copy link
Copy Markdown
Owner

Reviewed as part of an open-PR sweep. The transcript-based live-model concept and the config fallback are sound and kept as-is. Pushed fixes for four findings: (1) the substring regex could match "model" keys inside message content (e.g. sub-agent spawn params — reproduced against a real transcript), so extraction now JSON-parses lines and only accepts an assistant turn's top-level message.model; (2) added per-directory TTL caching so the per-session poll doesn't readdir+stat every historical transcript; (3) the cwd encoder now reuses the shared claudeProjectFolderName from pathUtils (was a 4th independent copy of scar-tissue logic); (4) updated the now-contradictory badge tooltip. Tests extended (content false-positive + cache behavior), 25/25 passing. Verdict after fix: good to merge.

web3dev1337 and others added 2 commits July 18, 2026 16:02
… running

User feedback: terminals with no open chat still showed model/effort from
the worktree's most recent transcript, presented as live state. A finished
or closed session leaves its transcript behind, so transcript detection is
now gated on session recovery reporting an active Claude agent for that
terminal; otherwise the badge shows the configured launch default.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
User feedback: with xhigh as the daily default the warning-orange chip lit
up every terminal header — now expensive efforts keep only a faintly tinted
border. Badge text order flips to model · EFFORT: the model is the fact you
scan for, effort is the qualifier.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@web3dev1337

Copy link
Copy Markdown
Owner

Two follow-ups from live testing: (1) transcript-based detection is now gated on an actually-running Claude agent (session recovery's lastAgent/lastAgentActive) — a closed chat's leftover transcript no longer masquerades as live state; terminals with no agent show the configured launch default. (2) Badge presentation: model name now comes first (model · EFFORT), and the high-effort orange is muted to a faint tinted border — with xhigh as a daily default it lit up every header. Tests: 26/26 passing.

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.

2 participants