- Origin request state: accepted, refused, failed or completed according to the origin API.
- Target operation state: planned, attempted, accepted, converged, failed or unknown.
- Report state: not yet visible, collecting, sealed, consumed, expired or unavailable.
- Owner state: whether the request owner’s authenticated principal has exported or acknowledged the report.
Lifecycle of an asynchronous explanation
Text equivalent: the adapter begins capture and, inExecuteAndExplain, performs enough of the target interaction to produce the origin-native response. It returns that response and a report ID even though background persistence may still be pending or may fail. A coordinator or worker can append later target or child-work evidence and seal a final revision. The authenticated report owner exports needed evidence and acknowledges that exact revision.
In Explain mode, the target API request lane is not executed. The report can still include hypothetical target mapping and evaluated constraints. In ExecuteAndExplain, the operation has real effects and a lost report response does not undo them.
Preserve the receipt and native response separately
An executing response can carry:- do not immediately replay the origin operation;
- inspect the origin operation’s idempotency contract;
- query the target or origin status operation by exact identity where supported;
- poll
report-123, not the mutating request; - retain every observed report revision and native state.
Read revisions monotonically
A report can be available before it is sealed. Each read includes a revision. Make the first direct GET withoutminimumVersion. On later reads, request at least the last recorded revision to prevent regression, or request the next revision only when waiting for strictly newer evidence. The read never waits: it either returns the current qualifying snapshot or a disposition.
Formatting an existing report as Human, Agent or JSON does not create a new revision and does not run work again.
Poll through the origin status operation
For a declared status read, an explanation-aware client can request the native status and explanation snapshot together:x-t9-explain, because it selects an existing report rather than starting a new explanation.
Retain the exact operation identity. A provider-assigned ID recorded by an earlier revision is stronger than a display name. If the ID is not yet known, report that gap and wait for the relevant observation instead of selecting a similarly named resource.
The envelope’s outer HTTP status is 200. It preserves the origin-native status, headers and body inside explanationEnvelope.native while including the explanation snapshot. An explanation-aware client unwraps the embedded native response before SDK retry or error classification and records the report revision separately. A status read should not be treated as a second create, publish or delete.
Follow causality, not adjacency
Asynchronous reports can contain evidence from several producers. Capture order is not necessarily a cross-service clock. Link child work to its parent through explicit causal fields:- parent and child span relationships;
causedByevent IDs;- intent and operation references;
- producer-local sequence numbers;
- facts with named supporting facts or events.
Reason about cancellation and lost responses
Cancellation of a client wait does not necessarily cancel provider work. If the client gives up while the request is still admitted, before conclusion or coordinator handoff, the adapter seals the report with an unknown outcome. After handoff, the report remains collecting and the coordinator owns later evidence and sealing. After conclusion has begun, the report seals according to that concluded result even if the client loses the transport response. Record separately:- whether the origin client stopped waiting;
- whether the adapter accepted cancellation;
- whether a target operation had already been attempted;
- whether the target supports cancellation;
- what later status observation was captured;
- whether the report sealed or expired first.
Cross-service example: one publish, two recipients
Consider an SNS-shapedPublish adapted to two SQS-shaped recipients. The evidence model must keep these milestones distinct:
- the publish request is authenticated and accepted;
- recipient A is selected;
- recipient B is selected;
- delivery to A is attempted;
- queue A accepts or refuses the message;
- delivery to B is attempted;
- queue B accepts or refuses the message;
- any later consumer processing occurs outside queue acceptance.
Explain mode, the report can describe recipient selection and planned delivery paths without enqueueing a message. In ExecuteAndExplain, a useful report connects each delivery attempt and queue result to the publish using explicit causal references.
The following is the structure to look for, not a captured provider transcript:
Keep acknowledgment with the report owner
Only the authenticated principal recorded as the report owner can read or acknowledge through the report HTTP routes. For asynchronous incidents, decide which application or collector uses that owner identity and serializes acknowledgment attempts. Before acknowledging:- confirm the report is sealed;
- use the exact
x-t9-explain-acknowledge-revisionsupplied by the read; - export the formats needed by other people or systems through an approved, sanitized channel;
- sanitize shared evidence without breaking event/fact references;
- preserve the native response and target-state checks separately.
Consumed. A different revision returns RevisionMismatch. Do not have two components using the owner identity race to acknowledge; serialize the attempt and preserve the exported bundle first.
See Diagnostic Response Headers for read and acknowledgment statuses, and Troubleshooting Explanations for missing receipts, unavailable reports and SDK retry behavior.