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

![Four stages: native response with a separate report receipt, later evidence and seal, owner report reads and export, then acknowledgment of the sealed revision.](https://mintcdn.com/tensor9/ufP2gYsEkScmc1Bt/images/cloud-adapter-early-access/diagrams/explain/async-lifecycle-wide-dark.svg?fit=max\&auto=format\&n=ufP2gYsEkScmc1Bt\&q=85\&s=6d1771175e0e32acbe4e6af3586a5a40)

![Four stages: native response with a separate report receipt, later evidence and seal, owner report reads and export, then acknowledgment of the sealed revision.](https://mintcdn.com/tensor9/ufP2gYsEkScmc1Bt/images/cloud-adapter-early-access/diagrams/explain/async-lifecycle-wide-light.svg?fit=max\&auto=format\&n=ufP2gYsEkScmc1Bt\&q=85\&s=cddd54e15fea2b020ce6259e35d800a4)

![Four stages: native response with a separate report receipt, later evidence and seal, owner report reads and export, then acknowledgment of the sealed revision.](https://mintcdn.com/tensor9/ufP2gYsEkScmc1Bt/images/cloud-adapter-early-access/diagrams/explain/async-lifecycle-stacked-dark.svg?fit=max\&auto=format\&n=ufP2gYsEkScmc1Bt\&q=85\&s=175e80661b4b64429dbf60668a7de1f0)

![Four stages: native response with a separate report receipt, later evidence and seal, owner report reads and export, then acknowledgment of the sealed revision.](https://mintcdn.com/tensor9/ufP2gYsEkScmc1Bt/images/cloud-adapter-early-access/diagrams/explain/async-lifecycle-stacked-light.svg?fit=max\&auto=format\&n=ufP2gYsEkScmc1Bt\&q=85\&s=5f046e9606d45ec38ba9c265f2d6de8c)

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. A completed preview returns a native `409 ExplainedNotPerformed` error with the report receipt; retrieve its hypothetical mapping and constraints separately. 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
x-t9-explain-expires-at: 2026-10-24T12:00:00Z
```

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. Call `T9CloudAdapter.GetExplanation` on the dedicated synthetic AWS `t9` endpoint, authenticated with the report owner's normal IAM credentials and SigV4 signing name `t9`. Each successful AWS JSON response includes a revision and a string `body` containing the rendered report. Decode the outer object first. Omit `minimumVersion` on the first request and while the last recorded revision is zero. Once a positive revision exists, request at least that revision to prevent regression, or request the next revision only when deliberately waiting for strictly newer evidence. `minimumVersion` must be positive. 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.

For a manual read, use the [signed SDK retrieval flow](https://docs.tensor9.com/cloud-adapter-early-access/debugging/explain-sdk.md#retrieve-without-replaying). Decode the outer AWS JSON response's `body` string to read the rendered report. This read neither polls resource status nor acknowledges the report. Omit `minimumVersion` until you have a positive revision; use bounded retries if that requested revision is not yet visible.

## PostgreSQL planning can finish after the response

In the [planned PostgreSQL plan integration](https://docs.tensor9.com/cloud-adapter-early-access/debugging/postgres-query-plans.md), a native response can arrive before bounded plan collection completes. Registered planning work will contribute a complete plan or an explicit unavailable outcome before sealing; a missing early fact is not evidence that no SQL ran. Poll the existing report rather than repeat the operation. A collected plan remains a committed-state estimate from a separate observation, even when the surrounding report is sealed.

## Keep resource status separate from report progress

Use the normal AWS SDK status operation to observe the resource, and `GetExplanation` on `t9` to observe diagnostic progress. Use its configured endpoint and region, a single signed target header, and no query string:

```http theme={null}
POST / HTTP/1.1
Host: t9.example.test
Content-Type: application/x-amz-json-1.1
X-Amz-Target: T9CloudAdapter.GetExplanation
Authorization: AWS4-HMAC-SHA256 ...

{"reportId":"report-123","fmt":"Json","minimumVersion":8}
```

An executing status read keeps its ordinary status, headers and body. It still follows the adapter's configured Explain default unless an explicit capture control replaces it. It does not return an explanation envelope. The legacy `x-t9-explain-id` request selector on a service operation is refused with `400 InvalidExplainConfiguration`; supply the report ID to `GetExplanation` on `t9` instead.

Retain the exact resource and operation identity. A provider-assigned ID recorded by an earlier revision is stronger than a display name. If the ID is not yet known, retain that gap and wait for the relevant observation instead of selecting a similarly named resource.

## Finish within report retention

Reports expire 24 hours from creation by default. Record `x-t9-explain-expires-at` from the operation receipt or `reportExpiresAt` from a JSON report. Reading revisions does not extend the deadline, and background work does not restart it. Capture and retrieval stop at expiry; the underlying operation can continue.

Export evidence before expiry. The owner receives `410 Expired` while the expired record remains, then `404 NotFound` after collection. Physical collection requires the opt-in expiry sweep; see [retention and expiry](https://docs.tensor9.com/cloud-adapter-early-access/debugging/response-headers.md#retention-and-expiry).

## 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 `acknowledgeRevision` supplied by `GetExplanation` as the `revision` in `AcknowledgeExplanation`;
* 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](https://docs.tensor9.com/cloud-adapter-early-access/debugging/response-headers.md) for read and acknowledgment statuses, and [Troubleshooting Explanations](https://docs.tensor9.com/cloud-adapter-early-access/debugging/troubleshooting.md) for missing receipts, unavailable reports and SDK retry behavior.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.