Question
Is the session identifier a hook receives the same value the tool's telemetry export carries?
Decision
It unblocks the whole layer. If the two differ, no task can be attached to its cost by that route, and the design has to fall back on injecting an AIDD identifier before the process starts — which needs a launcher the CLI does not have.
Bounds
- Evidence needed: one real session per tool, the identifier seen by the hook and the one in the export recorded side by side.
- Stop when: the value is compared on at least three tools, or a tool makes the comparison impossible for a documented reason.
Investigation
Run 13-14 August 2026. Bench: a local OTLP collector, a hook that records the payload it receives, one throwaway session per tool in an isolated directory.
Result: the values are identical, on three tools, with a fourth confirmed on the export side only.
| Tool |
Hook field |
Export attribute |
Cost of the test |
| Claude Code |
session_id |
session.id |
two real sessions |
| Codex CLI |
session_id |
conversation.id on codex.sse_event |
zero tokens |
| GitHub Copilot |
sessionId |
gen_ai.conversation.id on invoke_agent |
zero credits |
| OpenCode |
not tested plugin-side |
session.id on ai.streamText |
one session |
| Cursor |
conversation_id |
not measurable |
export is an Enterprise team setting |
A reusable method
Identifiers are minted client-side, before any model call. A provider pointed at an address that answers nothing still opens the session, fires the hook and emits telemetry. Codex and Copilot verify for free. Cursor is the exception: it validates the key, then the model, then workspace trust before opening a session, so checking it costs a real turn.
The check therefore belongs in continuous integration rather than in an end-of-project checkbox. The equality is proven by a session, not by construction, and a tool update can break it without notice.
What the investigation found in passing, and which matters more
No probe worked on the first attempt, and never for a different reason. Writing the hook file is not enough; a gate has to be lifted, and every tool gates differently.
| Tool |
Gate |
Lifted by |
| Codex |
feature flag and persisted hook trust |
--enable hooks + --dangerously-bypass-hook-trust |
| Copilot |
folder trust on .github/hooks/ |
user scope under $COPILOT_HOME/hooks/ |
| Cursor |
workspace trust |
--trust |
| Claude Code |
none |
— |
The finding that reshaped the design
Measured on a real session with the AIDD plugins installed from their marketplace:
| Carrier |
default |
with OTEL_LOG_TOOL_DETAILS=1 |
metric claude_code.token.usage |
skill.name = third-party |
skill.name = third-party |
metric claude_code.cost.usage |
skill.name = third-party |
skill.name = third-party |
event skill_activated |
skill.name = custom_skill |
skill.name = aidd-context:11-explore |
The documentation says it for anyone reading to the end: third-party plugin skill names are replaced. AIDD ships from a third-party marketplace, so every AIDD skill collapses into one label on the metrics, and the un-redaction flag does not separate them — it only affects events.
Three consequences. Per-step cost must be correlated from log events, not filtered from a metric attribute, so the pipeline ingests events from v1. Claude Code stops being the metric-grain exception; that holds for per-session totals only. And the same flag also logs Bash commands and tool inputs, so collector-side redaction becomes a privacy requirement rather than a convenience.
Two measured limits to carry into the design
skill.name is sticky. Once a skill is activated the following datapoints carry it, including those of subagents launched afterwards. Correct for steps that follow one another, wrong for interleaved skills.
- Subagents differ per tool. On Claude Code they share the parent identifier and are told apart by
query_source; Cursor documents the opposite.
A method error, recorded
A first conclusion that OpenCode exported nothing was wrong twice: the switch is a config key (experimental.openTelemetry), not an environment variable, and the run that seemed to confirm the absence was void — the collector had failed to bind its port. The probe's reporter now checks the collector is listening before drawing any conclusion.
Full detail and sources: aidd_docs/brainstorm/2026_08_13-telemetry-layer.md
Question
Is the session identifier a hook receives the same value the tool's telemetry export carries?
Decision
It unblocks the whole layer. If the two differ, no task can be attached to its cost by that route, and the design has to fall back on injecting an AIDD identifier before the process starts — which needs a launcher the CLI does not have.
Bounds
Investigation
Run 13-14 August 2026. Bench: a local OTLP collector, a hook that records the payload it receives, one throwaway session per tool in an isolated directory.
Result: the values are identical, on three tools, with a fourth confirmed on the export side only.
session_idsession.idsession_idconversation.idoncodex.sse_eventsessionIdgen_ai.conversation.idoninvoke_agentsession.idonai.streamTextconversation_idA reusable method
Identifiers are minted client-side, before any model call. A provider pointed at an address that answers nothing still opens the session, fires the hook and emits telemetry. Codex and Copilot verify for free. Cursor is the exception: it validates the key, then the model, then workspace trust before opening a session, so checking it costs a real turn.
The check therefore belongs in continuous integration rather than in an end-of-project checkbox. The equality is proven by a session, not by construction, and a tool update can break it without notice.
What the investigation found in passing, and which matters more
No probe worked on the first attempt, and never for a different reason. Writing the hook file is not enough; a gate has to be lifted, and every tool gates differently.
--enable hooks+--dangerously-bypass-hook-trust.github/hooks/$COPILOT_HOME/hooks/--trustThe finding that reshaped the design
Measured on a real session with the AIDD plugins installed from their marketplace:
OTEL_LOG_TOOL_DETAILS=1claude_code.token.usageskill.name = third-partyskill.name = third-partyclaude_code.cost.usageskill.name = third-partyskill.name = third-partyskill_activatedskill.name = custom_skillskill.name = aidd-context:11-exploreThe documentation says it for anyone reading to the end: third-party plugin skill names are replaced. AIDD ships from a third-party marketplace, so every AIDD skill collapses into one label on the metrics, and the un-redaction flag does not separate them — it only affects events.
Three consequences. Per-step cost must be correlated from log events, not filtered from a metric attribute, so the pipeline ingests events from v1. Claude Code stops being the metric-grain exception; that holds for per-session totals only. And the same flag also logs Bash commands and tool inputs, so collector-side redaction becomes a privacy requirement rather than a convenience.
Two measured limits to carry into the design
skill.nameis sticky. Once a skill is activated the following datapoints carry it, including those of subagents launched afterwards. Correct for steps that follow one another, wrong for interleaved skills.query_source; Cursor documents the opposite.A method error, recorded
A first conclusion that OpenCode exported nothing was wrong twice: the switch is a config key (
experimental.openTelemetry), not an environment variable, and the run that seemed to confirm the absence was void — the collector had failed to bind its port. The probe's reporter now checks the collector is listening before drawing any conclusion.Full detail and sources:
aidd_docs/brainstorm/2026_08_13-telemetry-layer.md