Repository navigation
fix(trace): attribute failed, overlapping and cross-checkout shell edits to the AI - #3521
Conversation
Claude Code fires PostToolUseFailure, not PostToolUse, when a tool call fails. The CLI installed only PostToolUse, so a command that wrote files and then exited with an error left those files attributed to a human. Install the post-tool-use handler for PostToolUseFailure too. A failed call expects a different hook response, so pending session links stay on disk for the next command that succeeds. Refs #3519 Assisted-by: Claude Code Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev> Chainloop-Trace-Sessions: 21c8f01a-33fb-437e-bbe6-f1ea822a7a5e
The pre-command working-tree signature was keyed by session and agent. When one agent ran overlapping shell commands, the second pre hook replaced the first command's signature, and the first post hook deleted the second's. The files of one command were then attributed to a human. Key the signature by the agent's tool call ID when the agent reports one: Claude's tool_use_id, and opencode's callID, which the plugin now passes. Agents without call IDs keep the per-agent slot. Refs #3519 Assisted-by: Claude Code Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev> Chainloop-Trace-Sessions: 21c8f01a-33fb-437e-bbe6-f1ea822a7a5e
Shell hooks run in the session's own checkout and snapshot only that checkout. When the agent changed files in another checkout with a command like `cd <other checkout> && ...`, those files were attributed to a human, even though file tool edits in the same checkout were attributed to the AI. When a file tool edits a file in another checkout, add that checkout to the session record of the session's own checkout. Shell hooks now also snapshot each listed checkout and record its changes in that checkout's ledger. A checkout that the session never edited with a file tool is still not snapshotted. Refs #3519 Assisted-by: Claude Code Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev> Chainloop-Trace-Sessions: 21c8f01a-33fb-437e-bbe6-f1ea822a7a5e
AI Session Checks — 🟢 93% · ✅ 0 failing
|
| Status | Attribution | File | Lines |
|---|---|---|---|
| modified | ai | app/cli/pkg/action/trace_agent_hook_test.go |
+194 / -4 |
| modified | ai | app/cli/pkg/action/trace_agent_hook.go |
+150 / -32 |
| modified | ai | app/cli/internal/trace/state/snapshot_test.go |
+77 / -45 |
| modified | ai | app/cli/internal/trace/state/snapshot.go |
+48 / -25 |
| modified | ai | app/cli/internal/trace/claude/hooks_test.go |
+40 / -0 |
| modified | ai | app/cli/internal/trace/opencode/hooks_test.go |
+10 / -0 |
| modified | ai | app/cli/internal/trace/claude/hooks.go |
+8 / -1 |
| modified | ai | app/cli/internal/trace/provider.go |
+9 / -0 |
| modified | ai | app/cli/internal/trace/opencode/hooks.go |
+6 / -0 |
| modified | ai | app/cli/internal/trace/state/session.go |
+6 / -0 |
| modified | ai | app/cli/internal/trace/opencode/testdata/plugin_full.ts |
+4 / -0 |
| modified | ai | app/cli/internal/trace/opencode/testdata/plugin_tracerun.ts |
+4 / -0 |
Policies (4)
| Status | Policy | Material | Messages |
|---|---|---|---|
| ✅ Passed | ai-config-ai-agents-allowed |
ai-coding-session-21c8f0 |
- |
| ✅ Passed | ai-config-no-dangerous-commands |
ai-coding-session-21c8f0 |
- |
| ✅ Passed | ai-config-no-secrets |
ai-coding-session-21c8f0 |
- |
| ✅ Passed | ai-config-mcp-servers-allowed |
ai-coding-session-21c8f0 |
- |
Security Checks — ✅ 5 passing
✅ secret-scan
| Status | Policy | Messages |
|---|---|---|
| ✅ Passed | secrets-detection |
- |
✅ sast-scan
| Status | Policy | Messages |
|---|---|---|
| ✅ Passed | owasp-top10-2025 |
- |
| ✅ Passed | sast |
- |
| ✅ Passed | cwe-top25 |
- |
| ✅ Passed | cwe-top26-40-cusp |
- |
security-context — 2 advisories
This change touches code with a recorded security-fix history. These are pointers to what past fixes established, not findings in this diff, and they never fail the check.
View security context ↗ · Security context documentation ↗
⏭️ 3 scans not applied
| Scan | Reason |
|---|---|
vulnerability-scan |
no manifest/lockfile changed |
github-actions-scan |
no workflow files changed |
iac-scan |
no IaC files changed |
PR validation — ✅ 3 passing
| Status | Policy | Material | Messages |
|---|---|---|---|
| ✅ Passed | pr-min-approvals |
pr-info |
- |
| ✅ Passed | pr-description-required |
pr-info |
- |
| ✅ Passed | pr-user-story-linked |
pr-info |
- |
Powered by Chainloop and Chainloop Trace
There was a problem hiding this comment.
All reported issues were addressed across 11 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
The opencode plugin sends callID as tool_use_id on shell hooks, but the opencode hook reader did not decode it. Overlapping opencode shell commands then still shared one pre-command snapshot slot. Refs #3519 Assisted-by: Claude Code Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev> Chainloop-Trace-Sessions: 21c8f01a-33fb-437e-bbe6-f1ea822a7a5e
Main now sends the tool call ID as tool_use_id on the shell hooks, so that overlapping commands keep their own snapshots (#3521). The plugin is rewritten on this branch, so both of its entry points send it: OpenCode 1.x names the ID callID, and OpenCode 2 names it id. Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev>
Summary
The CLI attributed some AI-made file changes to a human in AI coding session evidence. This PR fixes three causes in the shell-command attribution of
chainloop trace.PostToolUseFailurehook, notPostToolUse, when a tool call fails. A failed command can still change files. The CLI now installs the post-tool-use handler forPostToolUseFailuretoo. A failed call expects a different hook response, so pending session links stay on disk for the next command that succeeds.tool_use_idand opencode'scallID. Agents without a call ID keep the per-agent slot.cd <other checkout> && .... When a file tool edits a file in another checkout, the CLI now adds that checkout to the session record. Shell hooks then also snapshot each listed checkout, and record its changes in the ledger of that checkout.Existing installations get the new Claude Code hook when they run
chainloop trace initagain.Known limits
cdfrom commands.Refs #3519
This PR was made with AI assistance (Claude Code).
🤖 Posted by Maximus bot (Claude Code) on behalf of @migmartri