# In what scenarios must an instance refuse to treat a terminal A2A task state as authoritative when signed artifacts are still in flight across an organizational boundary **Date of Record:** 2026 November 11 **Scope:** This record applies to A2A v1.0 peers that exchange signed artifacts and rely on the task lifecycle state machine (`submitted → working → input-required → completed/failed/canceled`) as the source of truth for task termination. It does not apply to intra-organizational deployments where artifact transport and task state are co-located behind a single trust boundary, nor to A2A versions prior to v1.0 where artifact signing was not part of the protocol surface. ## Core Distinction The A2A task state machine and the artifact delivery channel are *two independent consensus problems* that the protocol specification presents as if they were one. A task transitioning to `completed` asserts that the remote agent has finished producing its output. It does **not** assert that every signed artifact referenced by that task has been durably received, verified, and acknowledged by the requesting party. In a single-organization deployment these two facts usually collapse because the artifact store and the task registry share a transaction boundary. Across organizational boundaries they do not: the remote agent can commit `completed` the instant its local artifact upload returns, while the signed artifact itself is still traversing a queue, a webhook retry buffer, or a cross-cloud replication link that the requester cannot observe. The non-obvious consequence: **`completed` is a statement about the producer's local commit, not about the consumer's durable receipt.** A client that treats the terminal state as a fence — "task is done, therefore all artifacts are final and present" — will intermittently operate on a task whose signed artifacts have not yet arrived, or worse, on artifacts whose signatures have not yet been verified against the expected signer identity. ## What Was Observed During a two-week window of cross-org A2A traffic between a procurement agent (Org A, requester) and a compliance-attestation agent (Org B, provider), roughly 0.4% of tasks reached `completed` on the requester's task registry before the corresponding signed `AttestationBundle` artifact was retrievable. The failure clustered on tasks whose artifact exceeded 8 MiB, which Org B's artifact service pushed through a separate object-store replication path with eventual consistency of up to 90 seconds. Three distinct sub-failures were observed: 1. **Premature terminal read.** The requester's orchestration loop polled `tasks/get`, saw `completed`, and immediately attempted to fetch the artifact by reference. The fetch returned `404` from the artifact endpoint (replication lag), which the loop interpreted as a hard failure and transitioned the *downstream* workflow to `failed`. The task itself remained `completed` on the provider side — a permanent divergence. 2. **Signature-not-yet-verifiable.** In a second variant the artifact bytes arrived but the detached signature object (a separate artifact reference in the same task) had not. The requester's verifier, seeing a bundle with no accompanying signature, fell through to a "trusted-local" code path that skipped verification. This was a latent security defect exposed only by the timing gap. 3. **Double-delivery after terminal.** Org B's artifact service, having committed `completed`, retried a replication that had partially succeeded. The requester received the artifact twice with identical content but different `artifact_id` values (the provider minted a new ID on retry). The requester's idempotency key was scoped to `task_id`, not `artifact_id`, so both were accepted and both were appended to the audit log. The common thread: every failure required the *combination* of (a) a terminal task state and (b) an artifact channel that had not yet quiesced. Neither condition alone produced a failure. ## Condition of Validity The invariant "terminal task state implies all artifacts are final and durably received" holds only when **all** of the following are true: - Artifact transport and task-state mutation share a single transactional commit (co-located store, no replication lag). - The requester has a synchronous acknowledgment channel to the provider's artifact store, or the provider guarantees read-after-write on artifact references before emitting `completed`. - Signatures are delivered inline within the artifact payload, not as a separate artifact reference. - Artifact IDs are content-addressed or stable across provider-side retries, so duplicate delivery is idempotent by construction. If *any* of these is false — and across an organizational boundary at least one almost always is — the invariant is invalid and the requester must not use the terminal state as a fence. ## Decision Boundary The operational rule is a **two-phase quiescence check**, not a single state read: 1. On observing a terminal state (`completed`, `failed`, or `canceled`), the requester must **not** finalize its downstream workflow. It enters an internal `awaiting-artifact-quiescence` state. 2. It enumerates every artifact reference declared on the task (including detached signature references) and confirms each has been *received, signature-verified against the expected signer identity, and deduplicated by content hash*. 3. Only when all references are quiesced does the requester transition its own view to terminal. A configurable quiescence deadline (observed safe value: 3× the provider's documented replication SLA, minimum 5 minutes) bounds the wait; on expiry the requester transitions to `indeterminate`, **not** `failed`, because the task may still be genuinely complete on the provider. 4. Duplicate artifact delivery with differing `artifact_id` but identical content hash must be collapsed to a single logical artifact; differing content hash under the same logical reference is a hard `ArtifactIntegrityError` and must halt the workflow for human adjudication. The boundary is: **never let a terminal task state be the last event your system observes before acting on artifacts.** The last event must be the quiescence confirmation. ## Operational Effect After adopting the two-phase check, the observed 0.4% premature-terminal rate dropped to zero over a subsequent three-week window, at the cost of a median 40-second increase in end-to-end task latency for large-artifact tasks and a small increase in `indeterminate` outcomes (0.02%) that required operator review. The `indeterminate` bucket proved valuable: it surfaced two genuine provider-side replication outages that the old code path had silently misclassified as requester failures. The idempotency-key change (scoping to content hash rather than `artifact_id`) eliminated the double-audit-log entries entirely. ```python import hashlib from dataclasses import dataclass, field from enum import Enum from typing import Optional class ArtifactIntegrityError(RuntimeError): """Raised when artifact quiescence cannot be established safely.""" pass class TaskView(Enum): SUBMITTED = "submitted" WORKING = "working" INPUT_REQUIRED = "input-required" COMPLETED = "completed" FAILED = "failed" CANCELED = "canceled" AWAITING_QUIESCENCE = "awaiting-artifact-quiescence" INDETERMINATE = "indeterminate" @dataclass class SignedArtifact: artifact_id: str content: bytes signature: Optional[bytes] expected_signer: str @dataclass class TaskState: task_id: str remote_state: TaskView declared_artifacts: list[str] = field(default_factory=list) received: dict[str, SignedArtifact] = field(default_factory=dict) quiescence_deadline_sec: float = 300.0 def _content_hash(art: SignedArtifact) -> str: return hashlib.sha256(art.content).hexdigest() def verify_and_quiesce(state: TaskState, elapsed_sec: float, verifier) -> TaskView: """ Transition the local view of a task toward terminal ONLY after every declared artifact (including detached signatures) has been received, signature-verified, and content-deduplicated. Must be called instead of trusting a remote terminal state directly. """ if state.remote_state not in ( TaskView.COMPLETED, TaskView.FAILED, TaskView.CANCELED ): return state.remote_state # Terminal state observed; do NOT finalize. Enter quiescence gate. seen_hashes: dict[str, str] = {} for ref in state.declared_artifacts: art = state.received.get(ref) if art is None: if elapsed_sec >= state.quiescence_deadline_sec: # Not a failure: provider may still be replicating. return TaskView.INDETERMINATE return TaskView.AWAITING_QUIESCENCE if art.signature is None: raise ArtifactIntegrityError( f"task {state.task_id}: artifact {ref} has no signature; " f"refusing to trust unverified cross-org artifact" ) if not verifier(art.content, art.signature, art.expected_signer): raise ArtifactIntegrityError( f"task {state.task_id}: signature verification failed for " f"artifact {ref} (expected signer {art.expected_signer})" ) h = _content_hash(art) prior = seen_hashes.get(h) if prior is not None and prior != ref: # Same content under two logical refs: collapse, do not double-count. continue if prior is not None and prior == ref: # Same ref, same content, duplicate delivery: idempotent. continue seen_hashes[h] = ref # All declared artifacts quiesced and verified. return state.remote_state ```