Repository navigation
feat(hook): name traces after the session title - #104
Open
a-j-i-n-k-y-a wants to merge 2 commits into
Open
a-j-i-n-k-y-a wants to merge 2 commits into
a-j-i-n-k-y-a wants to merge 2 commits into
Conversation
Every trace was named "Claude Code Turn", so traces were
indistinguishable in Langfuse. Claude Code writes the session title into
the transcript as its own row ("custom-title" from /rename, "ai-title"
auto-generated); use it as the trace name and keep the constant as the
fallback for sessions without one.
A rename wins over the auto-generated title, and within a type the last
row read wins. Titles are kept in session state because each firing only
reads the rows appended since the last one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The entry it plants carried a hardcoded timestamp. Once that date fell outside the 30-day retention window, save_hook_state pruned the entry and the test failed for a reason it was not written to catch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
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.
Problem
Every trace the hook emits is named
Claude Code Turn. With more than a handfulof sessions the Traces list becomes unnavigable — each row looks identical, and
since Langfuse sessions have no name field there is nothing else to recognise a
session by. Filtering on
metadata.projectnarrows things to a directory, butwithin a directory the rows are still indistinguishable.
Claude Code already knows the answer. It writes the session title into the
transcript as its own row type:
{"type": "custom-title", "customTitle": "Meeting Module", "sessionId": "..."} {"type": "ai-title", "aiTitle": "Langfuse port 5432 conflict", "sessionId": "..."}custom-titleis what/renamewrites;ai-titleis the auto-generated oneshown in the
/resumepicker. The hook already reads this transcript — it justnever looks at these rows.
Change
Use the session title as the trace name, falling back to the existing constant.
A rename now shows up in Langfuse under the same name the user chose in Claude
Code, so the Traces and Sessions lists become readable without any new
configuration.
Three details drove the rest of the diff:
read_new_jsonlreturns only rowsappended since the last run, so a title row seen on turn 1 is behind the read
offset by turn 20. Titles are kept in
SessionStateand round-trip throughthe state file alongside
turn_numbers/turn_progress.custom-titlemust beatai-titleregardless of row order. The two canarrive in different firings, so a single remembered string would let a late
ai-titleoverwrite an explicit rename. State holdstitles_by_type: Dict[str, str]and the preference order is applied at readtime.
transcripts copy history verbatim, title rows included; extracting earlier
would let a fork inherit its parent's title.
Titles are trimmed and capped at 200 characters so a pathological value cannot
bloat every trace. Attached mode (
parent_context is not None) is untouched —it deliberately propagates no trace-level fields, and
test_parent_trace_attachment.pystill passes unchanged.No new dependencies, env vars, or network calls, and nothing changes about what
conversation data is captured, truncated or transmitted. The only new log line
names the row type, never the title text, and only under
CC_LANGFUSE_DEBUG.Second commit: unrelated test rot
test_entry_of_another_session_survives_the_emitfails onmaintoday,independently of this change:
It plants a state entry with a hardcoded
"updated": "2026-09-10T00:00:00+00:00",and
save_hook_stateprunes entries older than 30 days — so the entry is nowswept and the assertion fires for a reason the test was not written to catch.
The fix uses
_now_iso(), a helper already used elsewhere in that same file.It is a separate commit because it is a separate problem; happy to split it into
its own PR if you would rather land it independently. Without it the test gate
cannot be green.
Tests
Eight tests added to
tests/unit/test_transcript_reader.py, which already owns"rows read out of the transcript into session state across firings": preference
order in both row orders, last-occurrence-wins per type, the
ai-title-then-rename sequence, unusable values (empty, whitespace,None,non-string, missing field, and a non-title row that happens to carry
customTitle), trimming and the length cap, state-file round-trip, and atwo-firing test where the title row sits behind the second firing's read offset
so the title can only come from persisted state. Plus the fallback case
asserting
TRACE_NAMEwhen no title row exists.Both load-bearing tests were confirmed to fail against the unfixed behaviour.
Also verified end to end against a self-hosted Langfuse v4 (
events_only)instance with the patched plugin installed from a local marketplace.
What a reviewer should doubt
titles_by_typethrough a transcript rotation.read_new_jsonldropspersisted turn state when the transcript shrinks; titles are deliberately kept
on the grounds that a title is a session-level fact and the rows get re-read
from byte 0 anyway. The alternative — clearing it and briefly falling back to
the constant — is defensible.
ai-titleshould be a fallback at all, or whether only an explicit/renameshould change the trace name. Auto-titles are derived fromconversation content; that content already reaches Langfuse via
metadata.user_text, but a trace name is more likely to end up in ascreenshot or an export. Easy to drop if you would rather be conservative.
🤖 Generated with Claude Code