> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tensor9.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Following Asynchronous Explanations

> Track partial revisions, native completion and causal child work without replaying the origin request.

An origin request and its explanation can finish at different times. The client may receive a native success or failure while the report is still partial, and downstream work can continue after the accepting call returns.

Keep four states separate:

1. **Origin request state:** accepted, refused, failed or completed according to the origin API.
2. **Target operation state:** planned, attempted, accepted, converged, failed or unknown.
3. **Report state:** not yet visible, collecting, sealed, consumed, expired or unavailable.
4. **Owner state:** whether the request owner's authenticated principal has exported or acknowledged the report.

A change in one state does not imply the others changed.

## Lifecycle of an asynchronous explanation

```mermaid theme={null}
sequenceDiagram
    participant App as Application
    participant Adapter as Service adapter
    participant Target as Target service
    participant Worker as Coordinator or worker
    participant Report as Explanation report

    App->>Adapter: Signed origin request + x-t9-explain
    Adapter->>Report: Begin capture, persistence may lag
    opt ExecuteAndExplain
        Adapter->>Target: Target API request
        Target-->>Adapter: Acceptance or current native result
    end
    Adapter->>Report: Record currently known evidence
    Adapter-->>App: Native response + report ID
    Note over App,Report: Receipt identifies capture intent, retrieval may lag or fail
    Worker->>Target: Later work or status observation
    Target-->>Worker: Completion, failure or current state
    Worker->>Report: Append causal evidence and seal revision
    App->>Report: Signed GET for the same report ID
    Report-->>App: Sealed report + acknowledge revision
    App->>Report: Owner acknowledgment after evidence export
    Report-->>App: Consumed
```

Text equivalent: the adapter begins capture and, in `ExecuteAndExplain`, 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:

```text theme={null}
x-t9-explain-id: report-123
x-t9-explain-mode: ExecuteAndExplain
```

Store those values alongside the origin status, body, request identifiers and exact operation identity. The report ID is a correlation locator: it proves neither target completion nor successful report persistence. Conversely, report unavailability is not proof that the operation failed.

If the response body is lost but the headers and report ID were captured:

1. do not immediately replay the origin operation;
2. inspect the origin operation's idempotency contract;
3. query the target or origin status operation by exact identity where supported;
4. poll `report-123`, not the mutating request;
5. retain every observed report revision and native state.

If no HTTP response or report ID survived, there is no safe report identifier to poll. Do not guess one. Use the exact operation or resource identity, the origin status API where supported, target-state inspection and the operation's idempotency contract before deciding whether another mutation is safe.

## Read revisions monotonically

A report can be available before it is sealed. Each read includes a revision. Make the first direct GET without `minimumVersion`. 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.

| Report response               | Interpretation                                                   | Action                                                                                                                                            |
| ----------------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `200`, `Collecting` lifecycle | Evidence exists but can still grow                               | Record revision and poll with bounded backoff                                                                                                     |
| `200`, sealed lifecycle       | Final report revision is available                               | Export evidence; acknowledge only if you own consumption                                                                                          |
| `409 NotYetVisible`           | The stored revision is lower than the requested `minimumVersion` | Keep the previously recorded snapshot, record the current stored revision in the disposition, and poll the same ID; do not rerun the service call |
| `503 Unavailable`             | The report path cannot answer now                                | Preserve native evidence and retry the read only                                                                                                  |
| `410 Consumed`                | The owner already acknowledged the sealed revision               | Use the previously exported bundle; an identical owner acknowledgment is safe to retry                                                            |
| `410 Expired`                 | Retention ended and the expired record is still present          | Use preserved evidence; decide separately whether the operation itself may be repeated                                                            |
| `404 NotFound`                | No report is visible to this principal                           | Verify ID and owner identity; allow for delayed or failed persistence, concealed access, unsupported schema or post-expiry collection             |

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:

```text theme={null}
x-t9-explain-id: report-123
Accept: application/vnd.tensor9.explain+json
x-t9-explain-fmt: Json
```

This is a signed request. It does not use `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;
* `causedBy` event IDs;
* intent and operation references;
* producer-local sequence numbers;
* facts with named supporting facts or events.

Do not attribute pre-existing target activity or unrelated customer traffic to the explained request merely because it appeared during the same time window.

Retries need the same care. Each execution attempt can have its own report, and one report can record multiple target attempts. Preserve attempt and report identities instead of flattening them into “the retry succeeded.” A superseded attempt remains relevant when determining whether an earlier mutation had effects.

## 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.

If a report is incomplete, it can say that completion is unknown. Do not rewrite that as failure. If a provider accepted work but no final observation was captured, the safe next action is a status read or target-state inspection, not a blind replay.

## Cross-service example: one publish, two recipients

Consider an SNS-shaped `Publish` adapted to two SQS-shaped recipients. The evidence model must keep these milestones distinct:

1. the publish request is authenticated and accepted;
2. recipient A is selected;
3. recipient B is selected;
4. delivery to A is attempted;
5. queue A accepts or refuses the message;
6. delivery to B is attempted;
7. queue B accepts or refuses the message;
8. any later consumer processing occurs outside queue acceptance.

One recipient's success does not establish its sibling's success. Queue acceptance does not establish downstream consumption or business processing. Publish acceptance does not establish that both deliveries settled.

In `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:

```text theme={null}
publish-accepted
├── recipient-a-selected
│   └── delivery-a-attempted
│       └── queue-a-accepted
└── recipient-b-selected
    └── delivery-b-attempted
        └── queue-b-refused
```

Do not infer these rows from the topology alone. Use the actual report's events, facts, identities and revisions. A production claim about SNS-to-SQS delivery requires service-specific captured evidence; evidence from an S3 upload or a different messaging path cannot substitute for it.

## 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-revision` supplied 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.

An identical acknowledgment retry by the same owner and sealed revision is idempotent and returns `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](/cloud-adapter/debugging/response-headers) for read and acknowledgment statuses, and [Troubleshooting Explanations](/cloud-adapter/debugging/troubleshooting) for missing receipts, unavailable reports and SDK retry behavior.
