AnthropicClaude Certified Architect – Professional2026-103.4 Analyze observability challenges and select monitoring strategies at scale
Connect agent work to downstream results
Completed by the knowledge check
Learning objective
Instrument traces that link requests, prompt and model versions, model calls, tool calls, subagent threads, and downstream effects.
Core idea
A useful trace answers which request caused an action, under which configuration, through which model and tool calls, and with what downstream result. A conversation identifier alone cannot answer those questions: one conversation can contain several turns, retries, branches, and external writes.
For an architect, trace design is an attribution decision across team boundaries. Use the Agent SDK’s documented parent relationships where available, add application-owned prompt and release references, and require the services behind tools to report their own execution outcomes. A generated statement that an action succeeded is not the downstream system’s receipt.
How it works
Establish the application parent
Create an application span for the business request before calling Agent SDK query(). The SDK automatically propagates active OpenTelemetry context into its Claude Code subprocess through TRACEPARENT and TRACESTATE. The CLI’s claude_code.interaction span then becomes a child of the application span.
An explicit TRACEPARENT in options.env overrides auto-injection. Interactive Claude Code sessions ignore inbound TRACEPARENT; the documented inbound propagation applies to the Agent SDK and non-interactive claude -p. Do not copy this integration design into an interactive terminal and assume the same parent relationship.
Enable the subprocess export with CLAUDE_CODE_ENABLE_TELEMETRY=1, OTEL_TRACES_EXPORTER=otlp, and CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1, plus the appropriate OTLP endpoint and protocol. Traces are beta, and their span names and attributes can change between releases. Validate your backend queries against the deployed CLI version rather than assuming a permanent schema.
Attach configuration provenance
The SDK’s claude_code.llm_request span exposes the model name, latency, and token counts. Add an application-owned prompt revision reference to the request span, together with a deployment revision and retrieval-index revision when those affect behavior. Name these as your own attributes; do not imply the API automatically assigns a prompt version to arbitrary prompt text.
Record the configured model identifier with the release configuration. If it is an alias, retain that fact rather than claiming immutable model-version provenance. Use an immutable artifact reference for the rendered prompt template and its approved configuration. A label such as “production” is insufficient if it points to different content after a release. Separate the template revision from request variables so provenance does not require placing customer content in searchable span attributes.
For API-support investigation, Claude Code’s monitoring reference documents request_id, derived from the response’s request-id header, on model-request spans and relevant events. This identifies one API request. Your application trace identifier links the larger business execution; retain both rather than replacing one with the other.
Represent tool and subagent execution
A claude_code.tool span surrounds a tool invocation. Its claude_code.tool.blocked_on_user and claude_code.tool.execution children distinguish permission waiting from execution. That distinction prevents a slow approval from being diagnosed as a slow downstream service.
Subagent model and tool spans nest under the parent’s launching tool span. For custom lifecycle instrumentation, Claude Code provides SubagentStart and SubagentStop; the Agent SDK hooks page lists both events as supported in Python and TypeScript. Register SDK callbacks through the hooks option. In TypeScript, agent_id and agent_type belong to the base input for every hook. In Python, they are optional on PreToolUse, PostToolUse, PostToolUseFailure, and PermissionRequest, and required on SubagentStart and SubagentStop. Shared tool-hook configuration belongs to 7.1.1.
SubagentStart input includes agent_id and agent_type, alongside common fields such as session_id and transcript_path. Use the event to open an application subagent span or execution segment. It also fires on subagent resume and each message handled by an in-process agent-team teammate, so agent_id alone is not a unique span identifier: allocate a segment identifier for each observed start. On claude_code.llm_request spans, agent_id joins a model call to its thread and hook segment; it is absent on the main session. parent_agent_id identifies the spawning agent, but is absent on the main session and agents spawned directly from it.
SubagentStop includes agent_id, agent_type, agent_transcript_path, stop_hook_active, and last_assistant_message. Its common transcript_path points to the main session; agent_transcript_path points to the subagent transcript. Store a restricted reference only if needed, rather than copying the file into telemetry.
A stop hook can keep the subagent running through decision: "block" and reason. Treat stop as a lifecycle observation, and close a completed execution segment when your lifecycle policy accepts termination. Account for repeated events and unmatched stops: Claude Code also emits SubagentStop for internal agents. Never manufacture an application assignment from every stop event. On Claude Code v2.1.271 or later, a subagent using SubagentHandback delivers its report in that tool’s message input; last_assistant_message then contains closing text, not necessarily the report.
Each observed SubagentStart opens its own execution segment, and a SubagentStop closes one only when your lifecycle policy accepts termination.
Join the actual external effect
Extend the trace into the service behind a tool using the service’s instrumentation and supported context propagation. Claude Code sends traceparent on model requests and outbound HTTP MCP requests by default when ANTHROPIC_BASE_URL is unset or points to the Anthropic API; the header is not sent to third-party providers. Behind a custom ANTHROPIC_BASE_URL proxy, set CLAUDE_CODE_PROPAGATE_TRACEPARENT=1 to enable propagation. The same switch controls forwarding TRACEPARENT to Bash and PowerShell commands. A command emitting OpenTelemetry spans can then nest under claude_code.tool.execution.
That documented command behavior is not a guarantee that every remote integration forwards context automatically. Make each adapter’s propagation explicit. For an asynchronous job, store an application correlation reference on the accepted job and link the completion record back to the originating request. The downstream owner supplies the actual receipt, job status, or durable transaction reference.
See 3.2.5 for the access trail from proposal to backend receipt. Here, link the model request, tool attempt, and target receipt through the trace. A trace that records only the first can falsely attribute success after a dependency failure. Preserve uncertain outcomes when the connection fails before the target’s receipt arrives; tracing alone cannot settle whether a write committed.
Key distinctions and trade-offs
Session versus request: session.id groups activity across SDK calls in one session. A request span identifies the particular business operation and its parent-child execution.
API request ID versus trace ID: The first supports one provider request; the second connects application, agent, and downstream work.
Native spans versus application attributes: Native instrumentation supplies execution structure. Prompt revisions, business operation references, and downstream receipt associations remain your integration’s responsibility.
A single trace backend simplifies investigation, but requires common instrumentation ownership. Detached local logs are cheaper to introduce and expensive to reconcile after an incident. Agree on parent propagation and completion references before adding payload content.
Architecture / decision scenario
A retail platform offers a refund assistant with a 20-second response target. Support reports that the assistant announced refunds that never appeared in the payment system. The payments lead proposes storing only the assistant’s final message because full transcripts are sensitive.
The architect keeps content collection restricted but rejects final-message-only attribution. The application request span holds the prompt revision and order-operation reference. SDK spans identify model and refund-tool activity; the payment adapter records the payment system’s transaction receipt or unresolved submission status. A delegated policy checker gets lifecycle segments tied to its agent_id.
One trace shows 12 seconds waiting for approval and a later submission timeout without a receipt. The support owner receives an unresolved outcome rather than a confirmed refund. Payments owns receipt reconciliation; the platform team owns propagation and lifecycle joins. The design distinguishes a missing effect from a slow approval without requiring unrestricted conversation storage.
Common exam traps
- Using one conversation ID as every request and span ID.
- Assuming an API request ID correlates the entire tool workflow.
- Treating
SubagentStartas a once-only event for an agent’s lifetime. - Confusing
transcript_pathwithagent_transcript_path. - Treating every
SubagentStopas an accepted completed assignment. - Assuming inbound
TRACEPARENTworks in interactive Claude Code.
What to remember
Connect the business request, configuration provenance, model calls, tools, subagent segments, and actual downstream receipts. Native beta tracing supplies much of the execution tree; your application supplies the business meaning and validates propagation across each external boundary.
Knowledge check
A team opens an application subagent span on each SubagentStart hook event and keys it by agent_id. Its trace backend shows overlapping start records for one agent_id, and some spans stretch across hours. The policy-checker subagent is regularly resumed. What should the instrumentation do?
SubagentStartsession_id instead of by agent_idSubagentStop rather than on startSubagentStart after the first one seen for an agent_idSubagentStart fires when a subagent is spawned, when it is resumed, and for each message an in-process teammate handles, so one agent_id can start several execution segments. session_id groups the whole session, and dropping later starts or opening on stop loses the segments the resumes created.An Agent SDK service routes Claude Code’s model requests through a company gateway set with ANTHROPIC_BASE_URL, and tracing is enabled. The gateway team reports that model requests arrive without a traceparent header, so its spans cannot join the agent trace. What should the service set?
TRACEPARENT in options.env for each query() callOTEL_LOG_TOOL_DETAILS=1 in the subprocess environmentCLAUDE_CODE_PROPAGATE_TRACEPARENT=1 in the subprocess environmentCLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1 in the subprocess environmenttraceparent on model requests by default only when ANTHROPIC_BASE_URL is unset or points at the Anthropic API; behind a custom proxy, CLAUDE_CODE_PROPAGATE_TRACEPARENT=1 enables it. TRACEPARENT in options.env pins the subprocess’s parent context, which is a different link, and tracing is already on.A refund assistant’s submit_refund tool returns as soon as it places the request on the payment system’s queue. Support reports refunds the assistant announced that never reached customers. Which record should establish whether a refund happened?
claude_code.tool span for submit_refundrequest_id of the model request that called the toolrequest_id identifies one API request. The downstream owner’s transaction receipt, or a recorded unresolved status, is the evidence of the effect.A developer exports TRACEPARENT in a terminal and starts an interactive Claude Code session to reproduce the SDK integration’s tracing. The session’s claude_code.interaction spans appear as disconnected roots instead of children of the developer’s span. What explains the result?
CLAUDE_CODE_PROPAGATE_TRACEPARENT=1 firstTRACESTATETRACEPARENT entirelyTRACEPARENT is honored only by Agent SDK and non-interactive claude -p runs; an interactive CLI session ignores it. The propagation switch controls outbound headers, so a reproduction of the parent relationship has to use one of the supported run modes.References
- Anthropic, Observability with OpenTelemetry
- Anthropic, Hooks reference
- Anthropic, Control execution with hooks
- Anthropic, Monitoring usage