{
  "schema_version": "1.3.0",
  "slug": "how-does-an-instance-detect-token-accounting-drift-when-opentelemetry-genai-agent-spans-split-a-single-model-invocation-across-tool-call-boundaries",
  "title": "How does an instance detect token accounting drift when OpenTelemetry GenAI agent spans split a single model invocation across tool-call boundaries",
  "date_of_record": "2026 November 10",
  "scope": "Applies to services that emit `gen_ai.*` attributes on agent and tool spans per the OpenTelemetry GenAI semantic conventions, where a single logical model invocation is decomposed into a parent agent span plus one or more child tool-call spans. Does not apply to single-shot inference spans that carry no tool-call children, nor to providers that report a single aggregate `gen_ai.usage.total_tokens` with no prompt/completion breakdown.",
  "projections": {
    "unified_markdown_uri": "https://corpusfetch.com/c/how-does-an-instance-detect-token-accounting-drift-when-opentelemetry-genai-agent-spans-split-a-single-model-invocation-across-tool-call-boundaries.md",
    "method_python_slice_uri": "https://corpusfetch.com/c/how-does-an-instance-detect-token-accounting-drift-when-opentelemetry-genai-agent-spans-split-a-single-model-invocation-across-tool-call-boundaries.py",
    "remnant_warrant_slice_uri": "https://corpusfetch.com/c/how-does-an-instance-detect-token-accounting-drift-when-opentelemetry-genai-agent-spans-split-a-single-model-invocation-across-tool-call-boundaries.warrant.json"
  },
  "token_profile": {
    "full_markdown_tokens": 2112,
    "python_slice_tokens": 580,
    "warrant_slice_tokens": 220
  },
  "remnant_summary": {
    "core_distinction": "The GenAI semantic conventions define `gen_ai.usage.input_tokens` and `gen_ai.usage.output_tokens` as attributes of the span that represents a *model invocation* — the call to the inference endpoint. Agent orchestration frameworks, however, frequently model a multi-turn tool-use loop as one logical \"agent run\" and emit a parent span (`gen_ai.operation.name =...",
    "what_was_observed": "Across three agent frameworks instrumented against the same backend, the same logical run (one user prompt, two tool calls, three model turns) produced three structurally different span trees: 1. **Flat aggregate.** One `invoke_agent` span carrying `gen_ai.usage.input_tokens = 4200`, `output_tokens = 310`, and *no* child model-invocation spans; tool calls appear as `execute_tool` sibling spans with no usage attribute...",
    "condition_of_validity": "The drift-detection procedure below is valid only when all of the following hold: - The trace contains at least one span with `gen_ai.operation.name` in the invocation set (`chat`, `text_completion`, `invoke_agent`) and at least one with `gen_ai.operation.name = \"execute_tool\"`. - The provider exposes an independent billed-token figure (billing export, respo...",
    "decision_boundary": "The instance must decide, per trace, whether the parent span is an **aggregate** or a **peer**: - If the parent `invoke_agent` span has usage attributes **and** has child `chat` spans with usage attributes, treat the parent as authoritative and *exclude* children from the sum. Flag the trace as `duplication_suspected` until the ratio test confirms. - If the ...",
    "operational_effect": "An instance that classifies tree shape before summing recovers correct totals for all three producer patterns and, critically, emits a `duplication_suspected` signal on the duplicated pattern *before* the monthly billing variance appears. The ratio test then either clears the flag (ratio ≈ 1.0, producer is actually flat-with-copied-parent, harmless) or confi..."
  },
  "matched_failure_symptoms": [
    "DOMAIN:EPISTEMIC_PROVENANCE_AND_FEDERATION_ROUTING"
  ],
  "composition_edges": {
    "primary_domain": "epistemic_provenance_and_federation_routing",
    "upstream_precondition_residuals": [
      "https://corpusfetch.com/c/according-to-what-precedence-hierarchy-must-an-instance-resolve-contradictory-directives-across-system-developer-and-user-tiers.md",
      "https://corpusfetch.com/c/how-should-an-instance-decide-that-a-stored-conclusion-has-gone-stale-without-a-new-observation.md"
    ],
    "downstream_consequence_residuals": [
      "https://corpusfetch.com/c/by-what-checkpoint-and-compaction-boundaries-must-an-instance-externalize-working-state-to-prevent-context-window-drift-and-truncation-amnesia.md",
      "https://corpusfetch.com/c/across-what-additive-schema-and-unknown-field-preservation-boundaries-must-an-instance-evolve-wire-contracts-to-prevent-rolling-deploy-deserialization-crashes.md"
    ],
    "co_occurring_sibling_residuals": [
      "https://corpusfetch.com/c/how-can-an-instance-capture-implicit-user-preferences-without-storing-private-data.md",
      "https://corpusfetch.com/c/how-can-an-instance-compress-long-reasoning-traces-into-binding-conclusions.md"
    ]
  },
  "empirical_verification_warrant": {
    "status": "AST_AND_INVARIANT_VERIFIED",
    "syntax_valid": true,
    "code_sha256": "5434f3cb7e154c75e414c20d8745444cf6623bd3d8f34c6a42c93d6b0d48c316",
    "verified_classes": [
      "TokenAccountingDrift",
      "Span"
    ],
    "verified_functions": [
      "classify_tree",
      "authoritative_input_tokens",
      "reconcile",
      "dedupe_tool_spans"
    ],
    "verified_functions_count": 4,
    "defensive_guards_count": 5,
    "raised_exceptions": [
      "TokenAccountingDrift"
    ],
    "sample_fail_closed_invariants": [
      "no usage attributes on any invocation span; shape=unaccounted",
      "parent and child invocation spans both carry usage; shape=duplicated_suspected, reconcile against billed total before su",
      "billed_input_tokens must be positive for ratio test"
    ],
    "stdlib_dependencies": [
      "__future__",
      "dataclasses",
      "typing"
    ],
    "observation_mode": "EMPIRICAL_BOUNDARY_CAPTURE",
    "zero_external_deps": true
  }
}
