{
  "schema_version": "1.3.0",
  "slug": "when-must-an-instance-attribute-cost-to-a-nested-agent-span-rather-than-the-enclosing-trace-root",
  "title": "When must an instance attribute cost to a nested agent span rather than the enclosing trace root",
  "date_of_record": "2026 November 12",
  "scope": "Applies to GenAI observability pipelines that compute per-request token cost and wall-clock latency from OpenTelemetry-style trace trees where LLM calls, tool invocations, and retriever calls are emitted as nested spans under an agent orchestration span. Does not apply to flat single-span instrumentation, nor to billing reconciliation against provider invoices, which must use provider-side usage records rather than trace-derived estimates.",
  "projections": {
    "unified_markdown_uri": "https://corpusfetch.com/c/when-must-an-instance-attribute-cost-to-a-nested-agent-span-rather-than-the-enclosing-trace-root.md",
    "method_python_slice_uri": "https://corpusfetch.com/c/when-must-an-instance-attribute-cost-to-a-nested-agent-span-rather-than-the-enclosing-trace-root.py",
    "remnant_warrant_slice_uri": "https://corpusfetch.com/c/when-must-an-instance-attribute-cost-to-a-nested-agent-span-rather-than-the-enclosing-trace-root.warrant.json"
  },
  "token_profile": {
    "full_markdown_tokens": 1499,
    "python_slice_tokens": 288,
    "warrant_slice_tokens": 220
  },
  "remnant_summary": {
    "core_distinction": "The non-obvious boundary is between *span-local cost accrual* and *trace-root cost aggregation*. A nested LLM span carries its own `gen_ai.usage.input_tokens`, `gen_ai.usage.output_tokens`, and model identifier. The enclosing agent span carries none of these; it carries only orchestration timing. The correct attribution rule is that cost is *accrued* at the ...",
    "what_was_observed": "In a three-service agent deployment (orchestrator → retrieval service → LLM gateway), trace trees showed the orchestrator root span with `gen_ai.request.model` set from a default config and zero usage attributes on children emitted by the gateway. Cost dashboards attributed 100% of spend to the orchestrator root at the default model's rate. Two concrete anomalies surfaced: 1. A single user turn that triggered a six-i...",
    "condition_of_validity": "The span-local accrual rule is valid if and only if all three hold: - Every span that issues a metered model call emits `gen_ai.usage.input_tokens` and `gen_ai.usage.output_tokens` (or provider-equivalent) at emission time, before any response transformation. - Model identity (`gen_ai.request.model` or `gen_ai.response.model`) is attached to that same span, ...",
    "decision_boundary": "Attribute cost to the nested span when: the span issued the model call, holds the usage attributes at emission, and holds model identity. Roll up to ancestors as a derived sum only, and mark the rollup as derived (`cost.derived=true`) so downstream consumers do not double-count against span-local values. Attribute cost to the trace root only when: the root i...",
    "operational_effect": "The practical consequence is that cost dashboards built on trace rollups must be validated against provider invoices at the trace level, not the aggregate level, because aggregate agreement can hide compensating errors (one over-attributed root masking several under-attributed children). The second consequence is that the gateway service becomes a first-clas..."
  },
  "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": "eeb41ae88d0879697b368a80d3a6c5dabdc99b3d60d9a032d381eb34e4d9a4be",
    "verified_classes": [
      "CostAttributionError",
      "Span"
    ],
    "verified_functions": [
      "attribute_span_cost",
      "rollup_trace_cost"
    ],
    "verified_functions_count": 2,
    "defensive_guards_count": 4,
    "raised_exceptions": [
      "CostAttributionError"
    ],
    "sample_fail_closed_invariants": [],
    "stdlib_dependencies": [
      "dataclasses",
      "typing"
    ],
    "observation_mode": "EMPIRICAL_BOUNDARY_CAPTURE",
    "zero_external_deps": true
  }
}
