fix(claudecode): extract model from transcript to fix Unknown attribution (#1804) - #1805
Merged
Conversation
…tion Claude Code was the only major agent without a ModelExtractor. It sourced the LLM model solely from the SessionStart hook payload (via the model-hint file). When SessionStart never delivered a model for a session — hooks installed mid-session, a resumed/continued session, or a cleared hint — state.ModelName stayed empty with no fallback, so every checkpoint carried an empty model and the dashboard/recap attributed the session to "Unknown" (issue #1804). Pi, Copilot, and Factory Droid already recover the model from the transcript at condensation time via sessionStateBackfillModel. Implement ExtractModel for Claude Code the same way: prefer the most recent assistant message.model (clean, hook-consistent, reflects mid-session switches), falling back to the system/init line's model for very short transcripts. Malformed lines (e.g. an incompletely-flushed transcript) are skipped, not fatal. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01KXZBGKPX420X41MT10V30729
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes Claude Code sessions being attributed as “Unknown” by ensuring the CLI can backfill a missing model during condensation: Claude Code now implements agent.ModelExtractor, allowing existing strategy logic (sessionStateBackfillModel) to recover the model from the JSONL transcript when hooks didn’t provide it.
Changes:
- Added
ExtractModelforClaudeCodeAgent, preferring the most recent assistantmessage.modeland falling back to the system init model. - Registered Claude Code as an
agent.ModelExtractorvia compile-time interface assertion so generic model backfill runs without extra wiring. - Added unit + strategy-level tests to guard the backfill behavior and edge cases (malformed lines, empty/no-model transcripts).
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| cmd/entire/cli/strategy/manual_commit_condensation_test.go | Adds an end-to-end test ensuring condensation-time model backfill works for Claude Code (regression coverage for #1804). |
| cmd/entire/cli/agent/claudecode/model.go | Introduces transcript scanning logic to extract Claude Code’s model from JSONL. |
| cmd/entire/cli/agent/claudecode/model_test.go | Adds focused unit tests for Claude Code model extraction behavior and resilience. |
| cmd/entire/cli/agent/claudecode/lifecycle.go | Registers ClaudeCodeAgent as implementing agent.ModelExtractor to activate generic backfill. |
The system/init model fallback matched any "system" envelope carrying a "model" field. Capture the transcript's subtype and require subtype:"init" so a future non-init system envelope can't be mistaken for the session's model. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01KXZC7323X2P4AAZKDS0JFV7F
Claude Code sets message.model to "<synthetic>" on API-error assistant entries. Taking the most recent assistant model verbatim let that placeholder become a session's persisted attribution. Skip it so the last genuine model is retained. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01KXZCQ2DPN3D16G492SEMWBC0
…model scan Explain that ExtractModel considers all assistant lines, including subagent (isSidechain) messages, matching the rest of the package which does not distinguish sidechains when scanning transcripts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01KXZDN76ZPKQ51JW391EQBXBQ
pjbgf
approved these changes
Jul 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
https://entire.io/gh/entireio/cli/trails/899
Summary
Fixes the primary CLI-side cause of #1804 — Claude Code sessions attributed as "Unknown" because their checkpoints carry an empty model.
Root cause: Claude Code was the only major agent without a
ModelExtractor. It sourced the model solely from the SessionStart hook payload (stored in the model-hint file, recovered on later turns). When SessionStart never delivered a model for a session — hooks installed mid-session, a resumed/--continuesession, compaction spawning a fresh segment, or a cleared hint —state.ModelNamestayed""permanently with no fallback. Every checkpoint then carried an empty model, and the entire.io frontend renders an empty model as the "Unknown" agent.Pi, Copilot, and Factory Droid already recover the model from the transcript at condensation time via
sessionStateBackfillModel(manual_commit_condensation.go). This makes Claude Code do the same. The CC transcript always records the model — on thesystem/initline and on every assistantmessage.model— so recovery is reliable.What changed
ExtractModelforClaudeCodeAgent(agent/claudecode/model.go): prefer the most recent assistantmessage.model(clean, hook-consistent identifier, reflects mid-session model switches), falling back to thesystem/initline'smodelfor very short transcripts. Malformed lines (e.g. an incompletely-flushed transcript) are skipped rather than fatal.agent.ModelExtractorinterface assertion, so the existing generic backfill (AsModelExtractor) now fires for Claude Code with no other wiring changes.Why this shape
Matches the existing Pi/Copilot/Droid pattern exactly (KISS/DRY) and realizes the reporter's suggestion #2 ("propagate the session-level model onto checkpoint records"). Low risk:
system.init.modelis line 1 of the transcript, present even in a partial/unflushed file.Not in scope (deliberately)
finalizere-reads the full transcript later regardless. Worth revisiting for token-count accuracy on large transcripts, but decoupled from attribution.agentfield when model is empty. Out of scope for this CLI PR.Tests
agent/claudecode/model_test.go— most-recent-assistant precedence, system/init fallback, empty transcript, no-model, malformed-line resilience.strategy/manual_commit_condensation_test.go— end-to-end guard thatsessionStateBackfillModelrecovers the model for Claude Code (the reporter's exact scenario).Full unit suite green (8175 tests);
golangci-lintv2.11.3 clean on both changed packages.Closes #1804
🤖 Generated with Claude Code
Note
Low Risk
Localized attribution/backfill logic with no auth or data-path changes; behavior matches existing agents’ transcript model extraction.
Overview
Fixes Claude Code checkpoints showing Unknown model when SessionStart never populated session state (mid-session hooks, resumed sessions, cleared hints).
Claude Code now implements
ModelExtractorviaExtractModel, scanning the JSONL transcript: the latest assistantmessage.modelwins (including mid-session switches), withsystem/initmodelas fallback when there are no assistant lines yet. Bad lines are skipped instead of failing the scan. A compile-timeagent.ModelExtractorassertion wires this into the existingsessionStateBackfillModelpath at condensation—same pattern as Pi, Copilot, and Factory Droid.Tests cover precedence, fallbacks, empty/malformed transcripts, and an end-to-end
sessionStateBackfillModelcase for Claude Code.Reviewed by Cursor Bugbot for commit 0b5dc22. Configure here.