{
  "schema_version": "1.3.0",
  "slug": "how-does-an-instance-detect-token-accounting-divergence-between-agent-spans-and-model-spans-under-opentelemetry-genai-semantic-conventions",
  "title": "How does an instance detect token accounting divergence between agent spans and model spans under OpenTelemetry GenAI semantic conventions",
  "date_of_record": "2026 November 12",
  "scope": "Applies to agents that emit `gen_ai.*` spans per the OpenTelemetry GenAI semantic conventions (agent, tool, and model spans) where token usage is recorded on model spans and rolled up to agent spans. Does not apply to systems that emit only a single flat span per request, nor to providers that report usage only at the request level rather than per model invocation.",
  "projections": {
    "unified_markdown_uri": "https://corpusfetch.com/c/how-does-an-instance-detect-token-accounting-divergence-between-agent-spans-and-model-spans-under-opentelemetry-genai-semantic-conventions.md",
    "method_python_slice_uri": "https://corpusfetch.com/c/how-does-an-instance-detect-token-accounting-divergence-between-agent-spans-and-model-spans-under-opentelemetry-genai-semantic-conventions.py",
    "remnant_warrant_slice_uri": "https://corpusfetch.com/c/how-does-an-instance-detect-token-accounting-divergence-between-agent-spans-and-model-spans-under-opentelemetry-genai-semantic-conventions.warrant.json"
  },
  "token_profile": {
    "full_markdown_tokens": 1436,
    "python_slice_tokens": 359,
    "warrant_slice_tokens": 220
  },
  "remnant_summary": {
    "core_distinction": "The semantic conventions define `gen_ai.usage.input_tokens` and `gen_ai.usage.output_tokens` as attributes of the *model* span — the span representing a single inference call. They do *not* define a normative aggregation rule for an *agent* span that fans out to N model calls and M tool calls. Operators routinely invent one: sum the model-span usage and atta...",
    "what_was_observed": "A planner agent emits one `agent` span per user turn. Under it, three model spans (plan, act, summarize) and one tool span. The runtime attaches a rolled-up `gen_ai.usage.input_tokens` to the agent span at span end. A collector-side processor independently computes the same sum from child spans for the backend. Under a transient 429 on the \"act\" model call, the SDK's retry logic emitted a *second* model span for the ...",
    "condition_of_validity": "The rollup invariant \"agent usage == sum of descendant model-span usage\" holds only when all three conditions are true: 1. Exactly one span is emitted per logical model invocation (retries either reuse the span or are explicitly excluded from both rollups by the same predicate). 2. The agent rollup walks the *entire* descendant subtree, not just direct child...",
    "decision_boundary": "Detect divergence by comparing two quantities scoped to the same trace and same time window: `agent_usage` (attribute on the agent span) versus `subtree_model_usage` (sum over all descendant spans carrying `gen_ai.usage.*`, regardless of depth, filtered to status `OK` or `UNSET`). Emit a diagnostic when `abs(agent_usage - subtree_model_usage) / max(subtree_m...",
    "operational_effect": "Instances that enforce the equality invariant begin emitting false positives on any provider that retries internally; instances that ignore it under-report cost by the retry and nesting factors observed above. The correct operational posture is to treat the agent-span usage as advisory, reconcile against the subtree sum at the collector, and annotate the age..."
  },
  "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": "d59299c4fae7788e64c0b60b4538ef281c706eca0e79bfadcab91e40c7f471f9",
    "verified_classes": [
      "UsageRollupDivergence",
      "Span"
    ],
    "verified_functions": [
      "_descendant_model_spans",
      "_children",
      "reconcile_agent_usage",
      "_descendants",
      "rel"
    ],
    "verified_functions_count": 5,
    "defensive_guards_count": 2,
    "raised_exceptions": [
      "UsageRollupDivergence"
    ],
    "sample_fail_closed_invariants": [],
    "stdlib_dependencies": [
      "dataclasses"
    ],
    "observation_mode": "EMPIRICAL_BOUNDARY_CAPTURE",
    "zero_external_deps": true
  }
}
