{
  "schema_version": "1.3.0",
  "slug": "which-invariants-prevent-trace-parent-loss-across-a2a-hops-and-mcp-transports-and-where-the-boundary-actually-fails",
  "title": "Which invariants prevent trace-parent loss across A2A hops and MCP transports — and where the boundary actually fails",
  "date_of_record": "2026 November 10",
  "scope": "Applies to agent observability pipelines that propagate W3C Trace Context (`traceparent`/`tracestate`) across agent-to-agent (A2A) task handoffs and Model Context Protocol (MCP) tool-call transports (stdio, SSE, streamable HTTP). Does not apply to single-process agents whose spans never leave one OpenTelemetry SDK instance, nor to pipelines that reconstruct causality purely from an out-of-band event log rather than header propagation.",
  "projections": {
    "unified_markdown_uri": "https://corpusfetch.com/c/which-invariants-prevent-trace-parent-loss-across-a2a-hops-and-mcp-transports-and-where-the-boundary-actually-fails.md",
    "method_python_slice_uri": "https://corpusfetch.com/c/which-invariants-prevent-trace-parent-loss-across-a2a-hops-and-mcp-transports-and-where-the-boundary-actually-fails.py",
    "remnant_warrant_slice_uri": "https://corpusfetch.com/c/which-invariants-prevent-trace-parent-loss-across-a2a-hops-and-mcp-transports-and-where-the-boundary-actually-fails.warrant.json"
  },
  "token_profile": {
    "full_markdown_tokens": 1679,
    "python_slice_tokens": 311,
    "warrant_slice_tokens": 220
  },
  "remnant_summary": {
    "core_distinction": "The distinction that matters is between **context propagation** (carrying `traceparent` as bytes from caller to callee) and **context re-parenting** (the callee correctly continuing the trace with a new `span_id` whose `parent_span_id` equals the received `traceparent`'s span portion). Most pipelines instrument the first and *assume* the second. The failure ...",
    "what_was_observed": "In a pipeline spanning an orchestrator agent → A2A task dispatch → worker agent → MCP tool call over streamable HTTP, the following was reproduced deterministically: 1. **Session-scoped `traceparent` bleed.** The MCP client SDK captured `traceparent` once at session initialization (the `initialize` request) and reused that context for every subsequent `tools/call` on the same session. The result: all N tool calls in ...",
    "condition_of_validity": "The invariant \"a child span's `parent_span_id` equals the `span_id` in the `traceparent` it received\" holds **only when the following three conditions are simultaneously true**: - **(C1) Call-scoped injection.** The `traceparent` is injected per outbound *logical call* (per `tools/call`, per A2A `message/send`), not per connection, session, or task object. -...",
    "decision_boundary": "The operational decision is: **when a span's parent is ambiguous or suspect, do you re-parent, drop, or flag?** - **Do not silently re-parent** to the nearest timestamp-adjacent span. Timestamp adjacency is not causality, and in async fan-out (observation 4) it actively inverts order. - **Do not drop** the span. A mis-parented span still carries duration, st...",
    "operational_effect": "Enforcing C1–C3 and recording call identity yields three concrete effects: 1. **Fan-out becomes auditable.** With per-call injection, a worker's N tool calls appear as N siblings under the triggering task span. A pipeline can then assert `len(children(span)) == expected_call_count` and fail the correlation check loudly when session-scoped bleed collapses the..."
  },
  "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": "73050e4982563c2d74719fd696d4099de2356da13e35ff3874ce7551fcdb3473",
    "verified_classes": [
      "CorrelationInvariantError",
      "ReceivedContext"
    ],
    "verified_functions": [
      "admit_span"
    ],
    "verified_functions_count": 1,
    "defensive_guards_count": 4,
    "raised_exceptions": [
      "CorrelationInvariantError"
    ],
    "sample_fail_closed_invariants": [],
    "stdlib_dependencies": [
      "dataclasses",
      "typing"
    ],
    "observation_mode": "EMPIRICAL_BOUNDARY_CAPTURE",
    "zero_external_deps": true
  }
}
