Skip to main content
ClaudeWave
Skill3.8k repo starsupdated 4mo ago

braintrust-tracing

Braintrust Tracing for Claude Code provides a comprehensive guide to instrumenting Claude Code sessions within Braintrust's observability platform, detailing the hook architecture (SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, SubagentStop) that captures execution traces, and explaining how to correlate sub-agent sessions through root span identifiers for end-to-end debugging and monitoring. Use this skill when implementing logging and tracing for multi-turn Claude Code workflows involving sub-agents.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/parcadei/Continuous-Claude-v3 /tmp/braintrust-tracing && cp -r /tmp/braintrust-tracing/.claude/skills/braintrust-tracing ~/.claude/skills/braintrust-tracing
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# Braintrust Tracing for Claude Code

Comprehensive guide to tracing Claude Code sessions in Braintrust, including sub-agent correlation.

## Architecture Overview

```
                         PARENT SESSION
                    +---------------------+
                    |  SessionStart       |
                    |  (creates root)     |
                    +----------+----------+
                               |
                    +----------v----------+
                    |  UserPromptSubmit   |
                    |  (creates Turn)     |
                    +----------+----------+
                               |
          +--------------------+--------------------+
          |                    |                    |
+---------v--------+  +--------v--------+  +--------v--------+
| PostToolUse      |  | PostToolUse     |  | PreToolUse      |
| (Read span)      |  | (Edit span)     |  | (Task - inject) |
+------------------+  +-----------------+  +--------+--------+
                                                    |
                                         +----------v----------+
                                         |   SUB-AGENT         |
                                         |   SessionStart      |
                                         |   (NEW root_span_id)|
                                         +----------+----------+
                                                    |
                                         +----------v----------+
                                         |   SubagentStop      |
                                         |   (has session_id)  |
                                         +---------------------+
```

## Hook Event Flow

| Hook | Trigger | Creates | Key Fields |
|------|---------|---------|------------|
| **SessionStart** | Session begins | Root span | `session_id`, `root_span_id` |
| **UserPromptSubmit** | User sends prompt | Turn span | `prompt`, `turn_number` |
| **PreToolUse** | Before tool runs | (modifies Task prompts) | `tool_input.prompt` |
| **PostToolUse** | After tool runs | Tool span | `tool_name`, `input`, `output` |
| **Stop** | Turn completes | LLM spans | `model`, `tokens`, `tool_calls` |
| **SubagentStop** | Sub-agent finishes | (no span) | `session_id` of sub-agent |
| **SessionEnd** | Session ends | (finalizes root) | `turn_count`, `tool_count` |

## Trace Hierarchy

```
Session (task span) - root_span_id = session_id
|
+-- Turn 1 (task span)
|   |
|   +-- claude-sonnet (llm span) - model call with tool_use
|   +-- Read (tool span)
|   +-- Edit (tool span)
|   +-- claude-sonnet (llm span) - response after tools
|
+-- Turn 2 (task span)
|   |
|   +-- claude-sonnet (llm span)
|   +-- Task (tool span) -----> [Sub-agent session - SEPARATE trace]
|   +-- claude-sonnet (llm span)
|
+-- Turn 3 ...
```

## Sub-Agent Tracing: What Works and What Doesn't

### What Doesn't Work

**SessionStart doesn't receive the Task prompt.**

We tried injecting trace context into Task prompts via PreToolUse:

```bash
# PreToolUse hook injects:
[BRAINTRUST_TRACE_CONTEXT]
{"root_span_id": "abc", "parent_span_id": "xyz", "project_id": "123"}
[/BRAINTRUST_TRACE_CONTEXT]
```

But SessionStart only receives session metadata, not the modified prompt. The injected context is lost.

### What DOES Work

**Task spans in parent session contain everything:**
- `agentId` - identifier for the sub-agent run
- `totalTokens`, `totalToolUseCount` - metrics
- `content` - full agent response/summary
- `tool_input.prompt` - original task prompt
- `tool_input.subagent_type` - agent type (e.g., "oracle")

**SubagentStop hook receives the sub-agent's `session_id`:**
- This equals the sub-agent's orphaned trace `root_span_id`
- Allows correlation between parent Task span and child trace

### The Correlation Pattern

**Current state:** Sub-agents create orphaned traces (new `root_span_id`).

**Correlation method:**
1. Query parent session's Task spans for agent metadata
2. Match `agentId` or timing with orphaned traces
3. Sub-agent's `session_id` = its trace's `root_span_id`

**Future solution (not yet implemented):**
```
SubagentStop fires -> writes session_id to temp file
PostToolUse (Task) -> reads temp file -> adds child_session_id to Task span metadata
```

This would link: `Task.agentId` + `Task.child_session_id` -> orphaned trace `root_span_id`

## State Management

### Per-Session State Files

```
~/.claude/state/braintrust_sessions/
  {session_id}.json       # Per-session state
```

Each session file contains:
```json
{
  "root_span_id": "abc-123",
  "project_id": "proj-456",
  "turn_count": 5,
  "tool_count": 23,
  "current_turn_span_id": "turn-789",
  "current_turn_start": 1703456789,
  "started": "2025-12-24T10:00:00.000Z",
  "is_subagent": false
}
```

### Global State
```
~/.claude/state/braintrust_global.json   # Cached project_id
~/.claude/state/braintrust_hook.log      # Debug log
```

## Debugging Commands

### Check if Tracing is Active
```bash
# View hook logs in real-time
tail -f ~/.claude/state/braintrust_hook.log

# Check if session has state
cat ~/.claude/state/braintrust_sessions/*.json | jq -s '.'

# Verify environment
echo "TRACE_TO_BRAINTRUST=$TRACE_TO_BRAINTRUST"
echo "BRAINTRUST_API_KEY=${BRAINTRUST_API_KEY:+set}"
```

### Query Braintrust Directly
```bash
# List recent sessions
uv run python -m runtime.harness scripts/braintrust_analyze.py --sessions 5

# Analyze last session
uv run python -m runtime.harness scripts/braintrust_analyze.py --last-session

# Replay specific session
uv run python -m runtime.harness scripts/braintrust_analyze.py --replay <session-id>

# Find sub-agent traces (orphaned roots)
uv run python -m runtime.harness scripts/braintrust_analyze.py --agent-stats
```

### Debug Hook Execution
```bash
# Enable verbose logging
export BRAINTRUST_CC_DEBUG=true

# Test hooks manually
echo '{"session_id":"test-123","type":"resume"}' | \
  bash "$CLAUDE_PROJECT_DIR/.claude/plugins/braintrust-tracing/hooks/session_start.sh"