Repository navigation
feat(claude-code): report the branch each prompt works on - #44
Merged
Merged
Conversation
The telemetry export names a repository and never a branch, and it resolves that repository once at process launch and never again — so a session that moves between branches, or between repositories, reports the first one for its whole life. Per-pull-request cost was therefore an estimate built on a guess. New hooks post what the client was actually working on, keyed by `prompt_id` because that is what the cost events carry. Session start, working-directory change, prompt submit and file-touching tool calls, debounced per `(session, agent, prompt, remote, branch)`, posted asynchronously, failing open and silent — telemetry must never block the user or break a session. `OTEL_METRICS_INCLUDE_REPOSITORY` goes into the settings block: it is off by default upstream and is the single setting per-repository cost rests on. `OPENTRACE_API_URL` goes in beside it because Claude Code strips `OTEL_*` from hook subprocesses, so the hooks cannot read the exporter's endpoint however carefully they try. The ingest key does NOT go in that block. Everything in `env` is inherited by every hook, every Bash tool call and every MCP server a session starts, readable with a bare `env`. It lands in `~/.claude/opentrace-telemetry.token` at 0600 instead — its own file, not the MCP's, because that one holds a graph-reading CLI key and sharing a path made the outcome depend on which command wrote last. A copy an earlier build left in `env` is stripped on write rather than merely superseded, and the file outranks the variable when both exist, so a stale value cannot shadow a rotated key and fail quietly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`claude plugin validate --strict` treats warnings as errors, and the validator
now warns on an unquoted `${CLAUDE_PLUGIN_ROOT}`: a plugin installed under a path
containing a space expands into several words and the hook fails to run.
Six commands, four of them added by the session-context hooks and two that
predate them — so this was failing on `main` too, from the moment the warning
shipped. CI installs `@anthropic-ai/claude-code` unpinned and `main` last ran on
20 August, which is why it went unnoticed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
💰 Agent economicsno agent sessions have reported to this workspace in the last 30 days, so there is no spend to attribute. Set up agent telemetry → Full economics → · by CodeHub |
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.
What
The telemetry export names a repository and never a branch, and it resolves that repository once at process launch and never again — so a session that moves between branches, or between repositories, reports the first one for its whole life. Per-pull-request cost was an estimate built on a guess.
New hooks post what the client was actually working on, keyed by
prompt_idbecause that is what the cost events already carry.Pairs with
opentrace-api#352, which consumes this.The hooks
Session start, working-directory change, prompt submit, and file-touching tool calls. Debounced per
(session, agent, prompt, remote, branch), posted asynchronously, failing open and silent — telemetry must never block the user or break a session.They send the git remote, branch, head commit and the session/prompt/agent ids. Never a user, an organisation, a file path, a prompt, or any code. The server attributes every observation to the member the key belongs to, so there is nothing to spoof by editing the script.
Settings
OTEL_METRICS_INCLUDE_REPOSITORYis off by default upstream and is the single setting per-repository cost rests on.OPENTRACE_API_URLgoes in beside it because Claude Code stripsOTEL_*from hook subprocesses — verified directly — so the hooks cannot read the exporter's endpoint however carefully they try. Left unset, the destination falls back to the public host, which silently points a self-hosted install at the wrong place.The key deliberately does not go in that block
Everything in
envis inherited by every hook, every Bash tool call and every MCP server a session starts — readable with a bareenv, and apt to end up in a transcript. The key lands in~/.claude/opentrace-telemetry.tokenat0600instead.Its own file, not the MCP's.
opentrace-plugin.tokenholds a graph-reading CLI key; this is a usage key that cannot read anything. Sharing one path made the outcome depend on which command wrote last —installleft the usage key where the MCP looks and broke it outright,connectleft the CLI key where the hooks look and posted a graph-reading credential on every prompt.Upgrade behaviour:
envis stripped on write, not merely superseded, so upgrading remediates the exposure instead of preserving itdisconnect --usageclears the key file, or turning monitoring off would leave the hooks authenticated and still reportingVerification
115 unit tests pass, lint clean. Thirteen are new and pin the behaviour above: the two credentials staying in separate files,
0600on creation, recreating a world-readable file from an older build, writing through a symlink rather than replacing it, stripping the legacy key while keeping unrelated vars, and both token-resolution orders. The symlink and resolution-order tests fail against the previous code, so they are guards rather than documentation.Known gap, not addressed here
ensurePluginInstalledreturns early when the plugin is already present and never compares versions, so existing installs will not receive these hooks. Bumping to0.6.0does not change that on its own. Worth deciding before this is relied on, since the API side has nothing to consume until the hooks reach people.🤖 Generated with Claude Code