Repository navigation
feat(store): record git branch per session; fix Claude firstPrompt - #699
Conversation
Claude Code 2.1.302 opens the first user message with several <system-reminder> text blocks before the typed prompt. The title-gen anchor skipped the whole message (it starts with a reminder), fell back to the first text block, stripInjectedTags emptied it, and because firstUserMsg was already latched it was never re-evaluated: firstPrompt stayed null for the whole session. - Anchor = first user text block that is non-empty after stripping injected tags (Grok <user_info> still skipped, <user_query> unchanged). - recordFirstUserMsg never latches an empty value; firstUserMsg and firstPrompt are set together and re-evaluated while firstPrompt is null. - session-index _upsert pulls firstPrompt from the store, like title: it is recorded in detectSession before the first entry creates the row, so setFirstPrompt found no row and sessions.json kept null. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Per-PR cost attribution needs the branch a turn ran on; nothing recorded it. - server/git-branch.js: resolve the checked-out branch of a cwd by reading .git/HEAD (follows a linked worktree's `gitdir:` file), no process spawn, per-cwd 5s TTL cache, never throws. Detached HEAD, non-git, missing or unknown cwd -> null. - Entry: new append-only INDEX_FIELDS `branch` (omitted when null), set by both wire parsers' buildEntryFields from the entry cwd. rebuild-index passes branch:null (the record-time branch is not in _req/_res). Importer uses the transcript's own branch (Claude `gitBranch`, Codex session_meta git.branch). responseId merge fills a missing branch from another copy. - Session: session index keeps the branch of the latest-received turn that recorded one (never rolled back by older or branchless turns); exposed via sessions.json and /_api/sessions. `ccxray usage --json` adds topSessions[].branch; --cwd grouping unchanged. - Docs: data-model, usage --json schema, wire-protocol, CHANGELOG, CLAUDE.md. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Claude Code 2.1.302 sends its title-generation request before the first main turn, under the same session_id. Its user text wraps the typed prompt in <session>...</session> plus title instructions, and detectSession latched it as firstPrompt. detectSession now records firstUserMsg/firstPrompt only for main turns, reusing isAnthropicSubagent (the classifier behind entry.isSubagent). The gate applies to messages[] bodies only, so Codex and Grok input[] bodies are unchanged. A title-gen-only session keeps firstPrompt null until the main turn arrives. Co-Authored-By: Claude Opus 5.5 <[email protected]>
# Conflicts: # CHANGELOG.md
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d8df918ce3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (entry.branch && (!s.branch || recvAt >= (s._branchRecvAt || 0))) { | ||
| s.branch = entry.branch; | ||
| s._branchRecvAt = recvAt; |
There was a problem hiding this comment.
Persist branch recency across session-index reloads
After a restart, sessions.json restores s.branch but not _branchRecvAt, because flush() omits every underscore-prefixed field. If the importer then supplies an older missing turn for this session, (s._branchRecvAt || 0) makes that older branch overwrite the persisted latest branch, contradicting the latest-received rule and corrupting /_api/sessions until a rebuild. Persist the branch timestamp or reconstruct it before accepting incremental entries.
Useful? React with 👍 / 👎.
Summary
Per-PR cost attribution needs to know which branch a session ran on and what it was asked. Two gaps fixed:
branch(read from.git/HEAD, following a linked worktree'sgitdir:; nogitspawn; 5 s per-cwd cache; omitted outside git / detached HEAD). Imported Claude and Codex transcripts use their own recorded branch.GET /_api/sessions/sessions.jsonkeep the branch of the latest-received turn;ccxray usage --jsonaddstopSessions[].branch.rebuild-indexdoes not back-fill.firstPromptwas always null with Claude Code 2.1.302. The first user message opens with several<system-reminder>blocks; the prompt block is now picked, and an injected-only message never latches an empty value.<session>…</session>; only main turns latchfirstPromptnow (reusesisAnthropicSubagent). Codex/Grokinput[]bodies are unaffected.Verification
node --testunit group (2 workers): 2069/2069 pass. Heavy group: all pass except the box-localHerdr Node launcher fallback › still uses the mise Node when it is supported, which fails the same way on main on this machine.branch,/_api/sessionsshowsbranchand the typed prompt asfirstPrompt(no<session>wrapper), andccxray usage --jsonshowstopSessions[].branch. Raw captures of the test session were deleted afterwards.