New research: Leading indicators of AI coding agent effectiveness
New research: Leading indicators of AI coding agent effectiveness
Coding Agent Observability Is Harder Than It Looks
Agent Traces
Insights
Stephen Poletto, Mariano Giberti & John Kulzick
Stephen Poletto, Mariano Giberti & John Kulzick

ON THIS PAGE
Engineering teams often tell us they have coding agent traces covered: “The requests go through our gateway, we write the logs to a bucket, done.”
Then we ask about the pull request. Can they connect it to the session that produced it? Identify the lines the agent wrote? Keep a secret API key out of the logs, or control who can read an engineer's prompts?
We ran into these questions while building auspex, Span's trace collection agent, and the pipeline behind it. Collecting the requests was a small part of the work.
Engineering teams often tell us they have coding agent traces covered: “The requests go through our gateway, we write the logs to a bucket, done.”
Then we ask about the pull request. Can they connect it to the session that produced it? Identify the lines the agent wrote? Keep a secret API key out of the logs, or control who can read an engineer's prompts?
We ran into these questions while building auspex, Span's trace collection agent, and the pipeline behind it. Collecting the requests was a small part of the work.
Why collect agent traces?
Why collect agent traces?
Why collect coding agent traces at all?
A pull request used to be the unit of engineering work. You could read it, review it and count it. Today most of the consequential work happens before a pull request exists, inside an agent session where a person and an agent plan, try, fail and retry.
Traces are the record of that work. Each one captures the prompts, tool calls, files touched, skills used and tokens spent in a session, linked to the pull request it produced. With them, your team can look together at the effort that produces outcomes:
Agent setup: Find the points where engineers step in, and check whether a context file, rule, or tool permission could have saved them the trouble.
Working habits: Review your own sessions to see which prompting and context choices helped you finish a task, and where you got stuck.
Skills and instructions: Check whether an agent used a skill, which version it used, and whether an instruction change improved the results.
Spend: Break down token costs by team, repository, model, and type of work. Connect sessions to capitalizable work.
Code quality: Compare agent-written code by model, or skills used, during review and after merge.
To get deep insight into how to improve your AI practice at scale, you need to connect your coding agent sessions to the code, across the tools and machines your engineers use.
Why collect coding agent traces at all?
A pull request used to be the unit of engineering work. You could read it, review it and count it. Today most of the consequential work happens before a pull request exists, inside an agent session where a person and an agent plan, try, fail and retry.
Traces are the record of that work. Each one captures the prompts, tool calls, files touched, skills used and tokens spent in a session, linked to the pull request it produced. With them, your team can look together at the effort that produces outcomes:
Agent setup: Find the points where engineers step in, and check whether a context file, rule, or tool permission could have saved them the trouble.
Working habits: Review your own sessions to see which prompting and context choices helped you finish a task, and where you got stuck.
Skills and instructions: Check whether an agent used a skill, which version it used, and whether an instruction change improved the results.
Spend: Break down token costs by team, repository, model, and type of work. Connect sessions to capitalizable work.
Code quality: Compare agent-written code by model, or skills used, during review and after merge.
To get deep insight into how to improve your AI practice at scale, you need to connect your coding agent sessions to the code, across the tools and machines your engineers use.
Where to capture traces?
Where to capture traces?
Choosing where to capture
We considered three places to collect data, each with different coverage.
Provider and platform APIs
You can get cost and usage reports here. Access to session content varies by provider and by the cloud platform you use to reach it.
LLM gateways and proxies
You can record requests and responses to the model. You miss the local context: the repository, the checked-out branch, the working tree, and changes from scripts or MCP tools.
The device running the agent
You can capture the session alongside the file system, Git state, and tools the agent used. Your coverage depends on what the harness exposes, and you have to deploy software wherever agents run.
Choosing where to capture
We considered three places to collect data, each with different coverage.
Provider and platform APIs
You can get cost and usage reports here. Access to session content varies by provider and by the cloud platform you use to reach it.
LLM gateways and proxies
You can record requests and responses to the model. You miss the local context: the repository, the checked-out branch, the working tree, and changes from scripts or MCP tools.
The device running the agent
You can capture the session alongside the file system, Git state, and tools the agent used. Your coverage depends on what the harness exposes, and you have to deploy software wherever agents run.
How Span captures agent traces
How Span captures agent traces
Our approach to capturing agent traces
We collect traces on the device.
We chose device capture because we wanted to connect sessions to code changes. Git lives where the agent runs. Through a gateway, you might see the model propose an edit to src/app.ts, but you can't tell which repository contains that path, which branch the engineer has checked out, or whether the agent applied the edit.
Agents also change code through shell commands, MCP tools, custom commands, and scripts that run several steps. To reconstruct those changes from requests, you'd need to parse tool-specific patch formats and shell output, then check whether your reconstructed diff matches the files on disk.
A gateway adds an operational concern: engineers depend on it to reach their models. We wanted collection failures to leave engineers' agent sessions unaffected.
Device capture applies to cloud sandboxes and dev containers too. You have a file system and a Git checkout in those environments, with local changes you won't see through the model provider.
Hooks first, transcripts alongside them
On the device, we use hooks as our primary source and fill in gaps from session transcripts.
Vendors expose hooks so third parties can extend their harnesses. That gives vendors a reason to keep them stable, though it falls short of a formal data contract. The names differ, but the events overlap: session starts and ends, user prompts, tool calls, and sub-agent launches.
Through a hook, we get a structured event that identifies a user prompt or a failed tool call. With a proxy or raw log, we'd have to infer that from the content.
The timing matters, too. At the moment of an edit, we record the repository, branch, and commit through auspex. If we waited for the harness to flush a transcript and for the collector to read it, the engineer might have switched branches.
We need transcripts for information such as token usage. The split varies by tool: some vendors include rich detail in hooks and little in session logs; others do the reverse. We choose the best source for each tool and join the records.
Keeping up with undocumented formats
Coding agent vendors don't publish data contracts for their hook payloads or session logs. A vendor can rename a field in a patch release, move data from a hook to a transcript, or add an event type without treating it as a breaking change.
You also have to account for variants within a product. Cursor's IDE and headless CLI produce different data, as do Copilot's CLI and Copilot Chat in VS Code. Some cloud agents skip hooks on a cold start.
auspex supports Claude Code, Cursor, Codex CLI, GitHub Copilot CLI, VS Code Copilot Chat and OpenCode today, with Pi coming soon. We maintain a separate integration for each and track their releases. Adding a tool means taking on that maintenance work for as long as engineers use it.
Keeping collection out of the engineer's way
To capture Git state at the moment of an edit, we have to run a hook before the agent continues. Engineers feel any delay on each turn.
Some attribution tools compare repository state before and after each tool call inside the hook. You get detailed results, but the work grows with repository size. In a large monorepo, that can mean waiting on collection during each turn.
We keep the hook's work small. Through auspex, we attach the repository, branch, and commit to the event, write it to a local folder, and exit under strict timeouts. We make no network calls from the hook. In a per-user background process, we redact, buffer, and upload the events. If the machine goes offline, we keep them on disk until it reconnects.
We also avoid kernel and system extensions, and we write nothing into your repositories, including Git notes. Engineers shouldn't have to change how they work to accommodate collection.
Attributing code across concurrent sessions
Matching an agent's edits to a pull request gets complicated when an engineer runs ten sessions against one repository. Those sessions may span worktrees and branch switches. Some use edit tools; others change files through shell scripts.
We do the matching in Span's backend. On the device, we record each session's actions and location. In the pipeline, we compare those edits with the pull request's diff and use the order of file changes to work out attribution. We match against the PR itself, so we don't need metadata to survive a squash or rebase. You don't need to install anything extra in your version control provider either.
Working in the backend also gives us more information to resolve overlap. If several sessions touched a file, we send the candidates upstream and compare them with the pull request, review, and ticket. We don't have to pick a session on the device with less context.
We give up some precision with this approach. If an engineer and an agent edit the same branch at the same moment, we can't separate their contributions as well as a tool that diffs on each keystroke. We expect this overlap to become less common as engineers alternate between longer stretches of agent work and human work. That's an assumption behind our design, and a tradeoff to consider for your team.
We're also rolling out Snapshotting to capture edits outside edit tools. During a session, we take lightweight snapshots of the Git working tree through auspex, in the background and in its own scratch space. With those snapshots, we can credit changes made through shell commands, scripts, MCP tools, and skills to the agent.
Making the data comparable
To compare skill usage or find turns where agents get stuck across an organization, you need a common schema. Each tool has its own event format.
We normalize the data in two stages. On the device, we map hook payloads to common attributes through auspex. In the pipeline, we account for gaps in each tool's data and reconstruct sessions in a shared format. We update both stages as vendors change their formats, so you can query sessions across tools without learning each vendor's JSON structure.
Our approach to capturing agent traces
We collect traces on the device.
We chose device capture because we wanted to connect sessions to code changes. Git lives where the agent runs. Through a gateway, you might see the model propose an edit to src/app.ts, but you can't tell which repository contains that path, which branch the engineer has checked out, or whether the agent applied the edit.
Agents also change code through shell commands, MCP tools, custom commands, and scripts that run several steps. To reconstruct those changes from requests, you'd need to parse tool-specific patch formats and shell output, then check whether your reconstructed diff matches the files on disk.
A gateway adds an operational concern: engineers depend on it to reach their models. We wanted collection failures to leave engineers' agent sessions unaffected.
Device capture applies to cloud sandboxes and dev containers too. You have a file system and a Git checkout in those environments, with local changes you won't see through the model provider.
Hooks first, transcripts alongside them
On the device, we use hooks as our primary source and fill in gaps from session transcripts.
Vendors expose hooks so third parties can extend their harnesses. That gives vendors a reason to keep them stable, though it falls short of a formal data contract. The names differ, but the events overlap: session starts and ends, user prompts, tool calls, and sub-agent launches.
Through a hook, we get a structured event that identifies a user prompt or a failed tool call. With a proxy or raw log, we'd have to infer that from the content.
The timing matters, too. At the moment of an edit, we record the repository, branch, and commit through auspex. If we waited for the harness to flush a transcript and for the collector to read it, the engineer might have switched branches.
We need transcripts for information such as token usage. The split varies by tool: some vendors include rich detail in hooks and little in session logs; others do the reverse. We choose the best source for each tool and join the records.
Keeping up with undocumented formats
Coding agent vendors don't publish data contracts for their hook payloads or session logs. A vendor can rename a field in a patch release, move data from a hook to a transcript, or add an event type without treating it as a breaking change.
You also have to account for variants within a product. Cursor's IDE and headless CLI produce different data, as do Copilot's CLI and Copilot Chat in VS Code. Some cloud agents skip hooks on a cold start.
auspex supports Claude Code, Cursor, Codex CLI, GitHub Copilot CLI, VS Code Copilot Chat and OpenCode today, with Pi coming soon. We maintain a separate integration for each and track their releases. Adding a tool means taking on that maintenance work for as long as engineers use it.
Keeping collection out of the engineer's way
To capture Git state at the moment of an edit, we have to run a hook before the agent continues. Engineers feel any delay on each turn.
Some attribution tools compare repository state before and after each tool call inside the hook. You get detailed results, but the work grows with repository size. In a large monorepo, that can mean waiting on collection during each turn.
We keep the hook's work small. Through auspex, we attach the repository, branch, and commit to the event, write it to a local folder, and exit under strict timeouts. We make no network calls from the hook. In a per-user background process, we redact, buffer, and upload the events. If the machine goes offline, we keep them on disk until it reconnects.
We also avoid kernel and system extensions, and we write nothing into your repositories, including Git notes. Engineers shouldn't have to change how they work to accommodate collection.
Attributing code across concurrent sessions
Matching an agent's edits to a pull request gets complicated when an engineer runs ten sessions against one repository. Those sessions may span worktrees and branch switches. Some use edit tools; others change files through shell scripts.
We do the matching in Span's backend. On the device, we record each session's actions and location. In the pipeline, we compare those edits with the pull request's diff and use the order of file changes to work out attribution. We match against the PR itself, so we don't need metadata to survive a squash or rebase. You don't need to install anything extra in your version control provider either.
Working in the backend also gives us more information to resolve overlap. If several sessions touched a file, we send the candidates upstream and compare them with the pull request, review, and ticket. We don't have to pick a session on the device with less context.
We give up some precision with this approach. If an engineer and an agent edit the same branch at the same moment, we can't separate their contributions as well as a tool that diffs on each keystroke. We expect this overlap to become less common as engineers alternate between longer stretches of agent work and human work. That's an assumption behind our design, and a tradeoff to consider for your team.
We're also rolling out Snapshotting to capture edits outside edit tools. During a session, we take lightweight snapshots of the Git working tree through auspex, in the background and in its own scratch space. With those snapshots, we can credit changes made through shell commands, scripts, MCP tools, and skills to the agent.
Making the data comparable
To compare skill usage or find turns where agents get stuck across an organization, you need a common schema. Each tool has its own event format.
We normalize the data in two stages. On the device, we map hook payloads to common attributes through auspex. In the pipeline, we account for gaps in each tool's data and reconstruct sessions in a shared format. We update both stages as vendors change their formats, so you can query sessions across tools without learning each vendor's JSON structure.
Trace privacy
Trace privacy
Protecting what's in a trace
Agents read .env files and print connection strings. Engineers paste support tickets into prompts. If you upload raw transcripts, you can end up storing secrets and customer data alongside the code, available to anyone with access to the bucket.
Putting an entire event into an OpenTelemetry log as a blob makes redaction harder. If you redact a list of known fields, you can miss file contents that a vendor adds in a new field.
In auspex, we separate sensitive content from metadata based on each event's structure. We scrub the content while preserving identifiers, such as session IDs, so we can maintain the links between records.
We apply privacy controls from capture through access and retention:
Privacy mode: An engineer can stop capture on their device. An organization can require privacy mode, but can't override an engineer's choice to enable it.
On-device redaction: Before upload, we use deterministic patterns and checksum validators in memory to remove API keys and tokens for several hundred services, private keys, connection strings, high-entropy strings, and structured personal data. This step uses no model or network call.
Custom patterns: Organizations can add patterns for their own identifiers, such as customer IDs or project code names. These supplement the defaults and can't weaken them.
Server-side redaction: Before storage, we run a secret scanner and a named-entity model inside Span's infrastructure to catch information without a fixed format, including names and postal addresses.
Trace Screening: After collection, we screen for sensitive topics, such as performance reviews and board decks. We restrict those sessions to their authors and exclude their contents from evaluations and other analysis.
Permissions: By default, we restrict access to a session's prompts and transcript to the engineer who ran it. We manage metadata, contents, reporting, and deletion as four separate permissions, with grants per person or team.
Retention: Organizations set raw trace event retention between 30 and 90 days.
Topic screening and access controls deserve attention if you're building your own system.
You can remove a salary figure through redaction and leave behind a conversation about someone's compensation. Engineers use coding agents for work beyond code, and they may forget to turn capture off. We keep new traces hidden until screening finishes. If we flag a trace as sensitive, we restrict it to its author, including when someone else would otherwise have permission to read the contents.
Access to prompts also affects whether engineers will accept a trace system. Under Span's default policy, a manager with access to their reports' trace data can see sessions, costs, models, and evaluation scores. They can't read the prompts or the reasoning behind a score. Organizations that want leads to review sessions for coaching can opt in to content access for named people.
We can't guarantee that redaction or screening will catch everything. Engineers can use privacy mode to stop capture, and our security overview describes what we capture and what we don't.
Protecting what's in a trace
Agents read .env files and print connection strings. Engineers paste support tickets into prompts. If you upload raw transcripts, you can end up storing secrets and customer data alongside the code, available to anyone with access to the bucket.
Putting an entire event into an OpenTelemetry log as a blob makes redaction harder. If you redact a list of known fields, you can miss file contents that a vendor adds in a new field.
In auspex, we separate sensitive content from metadata based on each event's structure. We scrub the content while preserving identifiers, such as session IDs, so we can maintain the links between records.
We apply privacy controls from capture through access and retention:
Privacy mode: An engineer can stop capture on their device. An organization can require privacy mode, but can't override an engineer's choice to enable it.
On-device redaction: Before upload, we use deterministic patterns and checksum validators in memory to remove API keys and tokens for several hundred services, private keys, connection strings, high-entropy strings, and structured personal data. This step uses no model or network call.
Custom patterns: Organizations can add patterns for their own identifiers, such as customer IDs or project code names. These supplement the defaults and can't weaken them.
Server-side redaction: Before storage, we run a secret scanner and a named-entity model inside Span's infrastructure to catch information without a fixed format, including names and postal addresses.
Trace Screening: After collection, we screen for sensitive topics, such as performance reviews and board decks. We restrict those sessions to their authors and exclude their contents from evaluations and other analysis.
Permissions: By default, we restrict access to a session's prompts and transcript to the engineer who ran it. We manage metadata, contents, reporting, and deletion as four separate permissions, with grants per person or team.
Retention: Organizations set raw trace event retention between 30 and 90 days.
Topic screening and access controls deserve attention if you're building your own system.
You can remove a salary figure through redaction and leave behind a conversation about someone's compensation. Engineers use coding agents for work beyond code, and they may forget to turn capture off. We keep new traces hidden until screening finishes. If we flag a trace as sensitive, we restrict it to its author, including when someone else would otherwise have permission to read the contents.
Access to prompts also affects whether engineers will accept a trace system. Under Span's default policy, a manager with access to their reports' trace data can see sessions, costs, models, and evaluation scores. They can't read the prompts or the reasoning behind a score. Organizations that want leads to review sessions for coaching can opt in to content access for named people.
We can't guarantee that redaction or screening will catch everything. Engineers can use privacy mode to stop capture, and our security overview describes what we capture and what we don't.
Connecting sessions to the work
Connecting sessions to the work
Connecting sessions to the work
After collection, we need to connect sessions to the work your team shipped. Through Span's integrations with source control, project trackers, and incident tools, we link a session to its pull request, review, merge, and production outcomes. You can use those connections to assess whether a change to your agent setup helped.
A piece of work may span several sessions. You might explore an approach in one, plan in another, implement it, then return the next day to fix a regression. Through Trace Lineage, we group a person's related sessions so you can account for the cost and time of the whole effort.
We avoid the cost of running a language model over each pair of sessions by narrowing the candidates first. We use semantic distance and structured signals such as repository and branch, then ask a model to make the final match. Those matches are best-effort; we don't treat them as certain.
To understand a session, you also need to know the context the agent had: repository instructions, user settings, organization policy, skills, and memory. Through auspex, we capture invoked skills and their file contents, so you can check which version the agent used for a change.
Connecting sessions to the work
After collection, we need to connect sessions to the work your team shipped. Through Span's integrations with source control, project trackers, and incident tools, we link a session to its pull request, review, merge, and production outcomes. You can use those connections to assess whether a change to your agent setup helped.
A piece of work may span several sessions. You might explore an approach in one, plan in another, implement it, then return the next day to fix a regression. Through Trace Lineage, we group a person's related sessions so you can account for the cost and time of the whole effort.
We avoid the cost of running a language model over each pair of sessions by narrowing the candidates first. We use semantic distance and structured signals such as repository and branch, then ask a model to make the final match. Those matches are best-effort; we don't treat them as certain.
To understand a session, you also need to know the context the agent had: repository instructions, user settings, organization policy, skills, and memory. Through auspex, we capture invoked skills and their file contents, so you can check which version the agent used for a change.
Deploying agent traces
Deploying agent traces
Deploying and maintaining the collector
We ship auspex for macOS and Windows, with Linux in beta. You can deploy it through major device management tools as a signed package and handle macOS file-access prompts without asking engineers to click through them. After deployment, you need to track which machines have stopped reporting and investigate why.
As teams move agent work into cloud sandboxes, some expect collection to get easier because they control the compute. That helps with the sessions they run there. For an organization using a mix of laptops, vendor-hosted agents, and its own sandboxes, collecting from one environment leaves gaps.
We have support for cloud agents. Regardless of where a session runs, we have to connect it to the right person, team, pull request, and incidents.
Deploying and maintaining the collector
We ship auspex for macOS and Windows, with Linux in beta. You can deploy it through major device management tools as a signed package and handle macOS file-access prompts without asking engineers to click through them. After deployment, you need to track which machines have stopped reporting and investigate why.
As teams move agent work into cloud sandboxes, some expect collection to get easier because they control the compute. That helps with the sessions they run there. For an organization using a mix of laptops, vendor-hosted agents, and its own sandboxes, collecting from one environment leaves gaps.
We have support for cloud agents. Regardless of where a session runs, we have to connect it to the right person, team, pull request, and incidents.
Building trace collection
Building trace collection
Before you build it yourself
A capable team can build an initial collector in a week or two. Our own project grew as we worked through the questions below. Before you assign engineers to an in-house build, work out how you'll handle them:
Capture: Can you identify the repository, branch, and commit at the time of each edit?
Coverage: Have you accounted for the IDE, CLI, and cloud variants your engineers use? Assign someone to track releases and maintain each integration.
Latency: Set a timeout for work that blocks the agent, and test collection in your largest repository.
Attribution: Account for edits through shell commands and MCP tools, concurrent sessions in one repository, and squash merges.
Schema: Make sure you can query sessions across tools with a common schema.
Redaction: Decide what to remove before data leaves the machine, including how you'll handle fields you haven't seen before.
Sensitive topics: Decide how to handle a session where an engineer drafts a performance review.
Access: Define who can read prompts and enforce those permissions per person and team.
Opt-out: Give engineers a way to stop capture on their machines when they need to.
Linking: Connect sessions to people, pull requests, tickets, and incidents.
Deployment: Plan upgrades and monitoring across the operating systems and cloud environments you use.
We have more work ahead of us too. We want to improve attribution when sessions overlap, and we keep adding support as vendors release new harnesses and change existing ones.
If you're building this in-house, budget for that ongoing work alongside the initial implementation. If you'd like to compare approaches, we'd enjoy hearing from you. You can read about Span's privacy and permissions, browse the Help Center, or get in touch to see how we handle collection.
Before you build it yourself
A capable team can build an initial collector in a week or two. Our own project grew as we worked through the questions below. Before you assign engineers to an in-house build, work out how you'll handle them:
Capture: Can you identify the repository, branch, and commit at the time of each edit?
Coverage: Have you accounted for the IDE, CLI, and cloud variants your engineers use? Assign someone to track releases and maintain each integration.
Latency: Set a timeout for work that blocks the agent, and test collection in your largest repository.
Attribution: Account for edits through shell commands and MCP tools, concurrent sessions in one repository, and squash merges.
Schema: Make sure you can query sessions across tools with a common schema.
Redaction: Decide what to remove before data leaves the machine, including how you'll handle fields you haven't seen before.
Sensitive topics: Decide how to handle a session where an engineer drafts a performance review.
Access: Define who can read prompts and enforce those permissions per person and team.
Opt-out: Give engineers a way to stop capture on their machines when they need to.
Linking: Connect sessions to people, pull requests, tickets, and incidents.
Deployment: Plan upgrades and monitoring across the operating systems and cloud environments you use.
We have more work ahead of us too. We want to improve attribution when sessions overlap, and we keep adding support as vendors release new harnesses and change existing ones.
If you're building this in-house, budget for that ongoing work alongside the initial implementation. If you'd like to compare approaches, we'd enjoy hearing from you. You can read about Span's privacy and permissions, browse the Help Center, or get in touch to see how we handle collection.
Let's talk about where your AI program is.
platform
Resources
© 2026 Attuned Inc.
Let's talk about where your AI program is.
platform
© 2026 Attuned Inc.
Let's talk about where your AI program is.
platform

