OTel: git_commit_id still silently dropped for `git -C <path> commit` and quiet/redirected commits (2.1.278) — recurrence of #77237
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using a recent version of Claude Code (2.1.278)
What's Wrong?
Re-filing #77237, which was auto-closed by the stale-issue bot on 2026-09-21 ("inactive for too long") with no code fix — verified below that the underlying behavior is unchanged in 2.1.278 (the original report was against 2.1.207/2.1.209).
With OTel enabled (OTEL_LOG_TOOL_DETAILS=1), git_commit_id on the claude_code.tool_result event is still extracted by scraping the Bash command string and the command's stdout, rather than by resolving the actual HEAD SHA. Two gates, both still present in 2.1.278:
Gate 1 — command string must match /\bgit\s+commit\b/. git -C <path> commit … fails this (the -C <path> separates the two words), even though the commit succeeds and prints a normal summary line to stdout.
Gate 2 — a [<ref> <sha>] summary line must be present in the captured stdout. git commit -q, --quiet, and >/dev/null redirection all suppress or drop that line, so extraction fails even though the command string passes gate 1.
Verification against 2.1.278 (bundled CLI)
strings on the installed binary (~/.local/share/claude/versions/2.1.278) shows the extraction call site is essentially unchanged from the original report:
We.command.match(/\bgit\s+commit\b/) && hr.data && typeof hr.data==="object" && "stdout" in hr.data
? (ss = await L6t(String(hr.data.stdout), s.session.project.cwd), ... Fn.git_commit_id = ss.commitId ...)
: /* git_commit_id left unset */
L6t/D6t (the stdout-scrape + resolve helpers):
var q9r = /^\[(?:([\w./-]+)|detached HEAD)(?: \(root-commit\))? ([0-9a-f]{4,})\]/m;
function D6t(e){ let n = O6t(e).match(q9r); if (n?.[2]===void 0) return; return {sha:n[2], ...} }
async function L6t(e,n){ let r = D6t(e); if (!r) return; let s = await QPn(n); return {commitId: s?.startsWith(r.sha) ? s : r.sha, ...} }
Two things changed since the original report, neither of which fixes the bug:
- The stdout-scrape regex gained a
^…$/manchor anddetached HEADsupport — cosmetic, doesn't change whether the summary line has to survive in the captured stdout. - There's now a real git-internal HEAD-SHA resolver,
QPn(walks.gitrefs directly — no shelling out, no stdout dependency), used insideL6t— but only to expand/verify the SHAD6talready scraped. IfD6treturnsundefined(gate 1 or gate 2 fails),L6treturns immediately andQPnis never called. The fix is already sitting right next to the bug:QPn(cwd)can resolve the current HEAD SHA independent of stdout, but it's wired as a scrape-dependent enrichment step instead of a scrape-independent fallback.
Live repro (git behavior, confirms the command-string gate is the live failure point)
$ git -C /tmp/repro commit -m "path form"
[master d6301e7] path form
1 file changed, 1 insertion(+)
create mode 100644 b.txt
$ echo 'git -C /tmp/repro commit -m x' | grep -qE '\bgit\s+commit\b' && echo yes || echo no
no
Git prints the normal [<ref> <sha>] summary line — the commit is fully attributable — but the command string never reaches gate 2 because gate 1 already rejected it.
$ git commit -q -m "quiet commit"
$ # stdout is empty — confirmed via `git commit -q -m ... 2>&1` capturing zero bytes
What Should Happen?
Per the monitoring-usage docs, git_commit_id should be present for any successful git commit, independent of command spelling or stdout formatting. Since the resolver needed to do this correctly (QPn/L6t's internal HEAD lookup) already exists in the codebase, the fix is to call it unconditionally whenever a Bash tool call whose command contains git and commit (in either order/adjacency) succeeds, rather than gating it behind a successful stdout scrape.
Steps to Reproduce
- Enable telemetry with tool details (
CLAUDE_CODE_ENABLE_TELEMETRY=1,OTEL_LOGS_EXPORTER=otlp,OTEL_LOG_TOOL_DETAILS=1) against an OTLP collector you can inspect. - In a git repo, have Claude run as separate Bash commands:
git commit --allow-empty -m ok→git_commit_idpresentgit -C . commit --allow-empty -m nope→git_commit_idabsent (commit succeeds, SHA is knowable, telemetry silently drops it)git commit -q --allow-empty -m nope→git_commit_idabsent
- Inspect the exported
claude_code.tool_resultrecords.
Impact
Same as #77237: git_commit_id is the only structured link between a Claude-run commit and its SHA in OTel. Commit-attribution pipelines built on it undercount silently and with no error surfaced, specifically for the git -C <path> form (worktrees, multi-repo, path-explicit commits) and quiet/redirected commits.
This repo (ai-internal-enablement) enforces a written workaround for this exact gap in both org-level and project-level CLAUDE.md instructions (never use git -C <path> commit, never quiet/pipe a commit's stdout) — evidence this is a live operational cost for at least one downstream telemetry consumer, not a one-off report.
Claude Model
N/A — behavior is independent of the model.
Is this a regression?
No — confirmed present continuously from 2.1.207 (original report) through 2.1.278 (current, verified above).
Last Working Version
N/A — never worked as documented.
Claude Code Version
2.1.278
Platform
CLI (terminal)
Operating System
macOS (Darwin)
3 Comments
Adding a noise comment to prevent this from being closed.
@fsc-eriker thanks, from what I see of the sweep script, currently the only real guarantee is if the issue receives 10 or more thumbs up - without that, tickets eventually get closed even if unaddressed and containing full repro steps.
Adding a noise comment to prevent this from being closed.