Skip to content

fix(claudecode): extract model from transcript to fix Unknown attribution (#1804) - #1805

Merged
gtrrz-victor merged 4 commits into
mainfrom
fix/claudecode-extract-model
Jul 20, 2026
Merged

fix(claudecode): extract model from transcript to fix Unknown attribution (#1804)#1805
gtrrz-victor merged 4 commits into
mainfrom
fix/claudecode-extract-model

Conversation

@gtrrz-victor

@gtrrz-victor gtrrz-victor commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

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/--continue session, compaction spawning a fresh segment, or a cleared hint — state.ModelName stayed "" 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 the system/init line and on every assistant message.model — so recovery is reliable.

What changed

  • ExtractModel for ClaudeCodeAgent (agent/claudecode/model.go): prefer the most recent assistant message.model (clean, hook-consistent identifier, reflects mid-session model switches), falling back to the system/init line's model for very short transcripts. Malformed lines (e.g. an incompletely-flushed transcript) are skipped rather than fatal.
  • Registered the agent.ModelExtractor interface 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.model is line 1 of the transcript, present even in a partial/unflushed file.

Not in scope (deliberately)

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 that sessionStateBackfillModel recovers the model for Claude Code (the reporter's exact scenario).

Full unit suite green (8175 tests); golangci-lint v2.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 ModelExtractor via ExtractModel, scanning the JSONL transcript: the latest assistant message.model wins (including mid-session switches), with system/init model as fallback when there are no assistant lines yet. Bad lines are skipped instead of failing the scan. A compile-time agent.ModelExtractor assertion wires this into the existing sessionStateBackfillModel path at condensation—same pattern as Pi, Copilot, and Factory Droid.

Tests cover precedence, fallbacks, empty/malformed transcripts, and an end-to-end sessionStateBackfillModel case for Claude Code.

Reviewed by Cursor Bugbot for commit 0b5dc22. Configure here.

…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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ExtractModel for ClaudeCodeAgent, preferring the most recent assistant message.model and falling back to the system init model.
  • Registered Claude Code as an agent.ModelExtractor via 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.

Comment thread cmd/entire/cli/agent/claudecode/model.go
Comment thread cmd/entire/cli/agent/claudecode/model.go
gtrrz-victor and others added 3 commits July 20, 2026 11:02
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
@gtrrz-victor
gtrrz-victor merged commit b7b4636 into main Jul 20, 2026
10 checks passed
@gtrrz-victor
gtrrz-victor deleted the fix/claudecode-extract-model branch July 20, 2026 11:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Claude Code 2.1.x sessions attributed as 'Unknown' — transcript flush sentinel times out (3s) + empty model on checkpoints (v0.8.42)

3 participants