{
  "schema_version": "1.3.0",
  "slug": "under-what-conditions-must-an-instance-treat-opentelemetry-genai-tool-call-spans-as-non-retryable-evidence-of-side-effects-rather-than-as-retryable-mo",
  "title": "Under what conditions must an instance treat OpenTelemetry GenAI tool-call spans as non-retryable evidence of side effects rather than as retryable model output",
  "date_of_record": "2026 November 13",
  "scope": "Applies to agent runtimes that emit `gen_ai.*` spans (per the OpenTelemetry GenAI semantic conventions) for tool invocations and reconcile those spans against model token accounting. Does not apply to pure inference spans with no tool execution, nor to systems where tool calls are executed by an external orchestrator that owns its own span lifecycle.",
  "projections": {
    "unified_markdown_uri": "https://corpusfetch.com/c/under-what-conditions-must-an-instance-treat-opentelemetry-genai-tool-call-spans-as-non-retryable-evidence-of-side-effects-rather-than-as-retryable-mo.md",
    "method_python_slice_uri": "https://corpusfetch.com/c/under-what-conditions-must-an-instance-treat-opentelemetry-genai-tool-call-spans-as-non-retryable-evidence-of-side-effects-rather-than-as-retryable-mo.py",
    "remnant_warrant_slice_uri": "https://corpusfetch.com/c/under-what-conditions-must-an-instance-treat-opentelemetry-genai-tool-call-spans-as-non-retryable-evidence-of-side-effects-rather-than-as-retryable-mo.warrant.json"
  },
  "token_profile": {
    "full_markdown_tokens": 1192,
    "python_slice_tokens": 187,
    "warrant_slice_tokens": 220
  },
  "remnant_summary": {
    "core_distinction": "The GenAI semantic conventions describe a *model-facing* event: `gen_ai.operation.name = \"execute_tool\"`, a `gen_ai.tool.name`, and a `gen_ai.tool.call.id`. Critically, the span records that the *model proposed* a tool call and that an execution was attempted. It does not, by itself, encode whether the side effect committed. A span with `status = ERROR` and ...",
    "what_was_observed": "During a tool-call fan-out incident, an agent emitted spans for a `create_refund` tool. The model proposed the call, the tool executed against the payment provider, and the provider returned a 502 on the *response* leg after committing the refund. The span closed with `status = ERROR`, `gen_ai.tool.name = \"create_refund\"`, and a populated `gen_ai.tool.call.id`, but no `gen_ai.usage.output_tokens` on the child span (t...",
    "condition_of_validity": "This reasoning holds only when all of the following are true: 1. The runtime emits `gen_ai.tool.call.id` at proposal time, before execution begins, and does not mutate it on retry. 2. Token usage (`gen_ai.usage.input_tokens` / `output_tokens`) is attributed to the parent inference span, so tool spans are usage-free by design. 3. The downstream tool is *not* ...",
    "decision_boundary": "Classify an errored tool span along two axes: argument completeness and call-id stability. - **Complete args + stable call id + non-idempotent tool** → non-retryable; quarantine and surface for human reconciliation. This is the dangerous quadrant. - **Truncated/absent args + any call id** → retryable; the model failed before execution, no side effect. - **Co...",
    "operational_effect": "Quarantining commitment-marked spans converts a silent double-execution bug into an explicit reconciliation queue. The cost is latency: ambiguous spans block until reconciled rather than auto-retrying. The benefit is that token-based cost dashboards no longer mask duplicated side effects, and the invariant \"one call id, one execution attempt\" becomes enforce..."
  },
  "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": "50eea999c5c1f3afa53aeff5fb94630066592bba6242ce3a5f2759c0859d9039",
    "verified_classes": [
      "ToolSpanCommitmentError",
      "ToolSpan"
    ],
    "verified_functions": [
      "classify_retry"
    ],
    "verified_functions_count": 1,
    "defensive_guards_count": 2,
    "raised_exceptions": [
      "ToolSpanCommitmentError"
    ],
    "sample_fail_closed_invariants": [],
    "stdlib_dependencies": [
      "dataclasses"
    ],
    "observation_mode": "EMPIRICAL_BOUNDARY_CAPTURE",
    "zero_external_deps": true
  }
}
