# A2A Expectations Extension, Version 1

**Extension URI:** `https://rsaxb.com/a2a/expectations/v1`

**Status:** Draft. Published 2026-09-05.

**Editors:** RSA Cross Border.

**Independent extension.** Not published by, endorsed by, or affiliated with the
A2A project. It is published under a URI its authors control, which the
[A2A extension governance](https://a2a-protocol.org/latest/topics/extension-and-binding-governance/)
permits without registration. The `a2aproject` URI prefixes are reserved for
artifacts that have been through that project's process; this one has not.

**Companion:** the [Learnables extension](/a2a/learnables/v1) is independent of
this one. Neither requires the other; [§16](#16-relationship-to-learnables)
says where they touch.

**Feedback:** the reference implementation and the issue tracker are maintained
by RSA Cross Border. Comments are welcome.

Paragraphs marked *Rationale* are non-normative and may be skipped.

---

## 1. Abstract

Every state change in A2A is caused by something arriving: a message, an
answer, an event, a cancellation. Nothing in the protocol lets the **passage of
time** cause one. An agent that asks a question waits for the answer
indefinitely. An agent that knows a document is due on Thursday has no way to
say so, and no way to be told on Friday that it did not come. The failures that
cost the most in the domains A2A is being deployed into — logistics, finance,
support — are absences, and an absence does not emit an event.

This extension defines:

1. how an agent **raises an expectation** — a statement, attached to the work it
   has done, that a named fact about a named subject is due by a stated time;
2. how an expectation is **satisfied**, deterministically, by a fact arriving,
   and how a satisfied step re-anchors the steps that depended on it;
3. the **three ways an expectation lapses** — overdue, skipped, and stalled —
   and the requirement that each lapse becomes a new task for the agent that
   raised it, under the same context, rather than a silent condition;
4. the **watcher** role: the party that keeps time, which in most deployments is
   not the agent;
5. the rules that keep unattended, time-triggered work from acquiring authority
   nobody granted it, and from firing forever.

It does **not** define a scheduler, a cron syntax, a task deadline, or a
long-running task. It does not put a clock in the agent. It says what an agent
may declare about the future and what the protocol does when the future fails
to arrive.

## 2. Status of this document

This is a Draft. Field names and semantics MAY change.

A stable release of this URI is announced by setting `status` to `stable` in the
extension descriptor (Appendix A). Breaking changes are published under a new
URI, never by amending this one ([§15](#15-versioning)).

## 3. Conformance

The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**,
**SHOULD**, **SHOULD NOT**, **RECOMMENDED**, **MAY**, and **OPTIONAL** are to be
interpreted as described in BCP 14
([RFC 2119](https://www.rfc-editor.org/rfc/rfc2119),
[RFC 8174](https://www.rfc-editor.org/rfc/rfc8174)) when, and only when, they
appear in all capitals.

Three roles carry requirements:

- a **conformant agent** raises expectations and answers for their lapses;
- a **conformant watcher** keeps time: it records expectations, matches facts to
  them, and submits a lapse task when time runs out;
- a **conformant client** reads expectations and renders them, and MAY report
  facts.

One implementation MAY fill more than one role. In the reference deployment the
client and the watcher are the same application; the agent is neither.

## 4. Motivation

A2A specifies the moment work is requested and the moments it produces
something: a status, an artifact, a question. Between those moments the protocol
is silent, and it has no vocabulary for the case where the next moment never
comes. Implementations meet that in one of four ways, all in production
somewhere:

1. **Wait.** A task in `TASK_STATE_INPUT_REQUIRED` stays there. Nothing in the
   protocol expires a question, so a question nobody answers is a task nobody
   closes.
2. **Bolt a scheduler on.** A cron job outside the protocol wakes up, queries
   the agent's store directly, and pokes it. The agent's knowledge of what was
   supposed to happen lives in the cron job's SQL, invisible to any client and
   to the agent itself.
3. **Give the task a deadline.** A deadline bounds how long the *task* may run.
   It says nothing about the world: a customs entry can be filed on time and the
   release still not come.
4. **Hold the task open until the world responds.** A task that stays `WORKING`
   for four days, waiting on a carrier, is a task that cannot be reasoned
   about, listed usefully, or resumed on a host that restarted.

The A2A maintainers have already described the shape they prefer for recurring
and deferred work: not a new state, but **a new task under the same context**,
created when the trigger fires
([a2aproject/A2A#654](https://github.com/a2aproject/A2A/issues/654)). This
extension takes that shape and specifies the trigger. The agent states what it
expects and by when; a watcher holds the clock; when the clock runs out, the
watcher submits a task and the agent decides what the silence means.

**The agent expects. The watcher keeps time. The agent answers for the lapse.**

*Rationale.* Two things are deliberately kept out of the agent. The first is the
clock: many agents cannot hold one, being serverless, heartbeat-driven
([#1667](https://github.com/a2aproject/A2A/issues/1667)), or simply not
running between requests. The second is the decision that a lapse is
unimportant. An expectation the agent raised and then quietly let go is
indistinguishable, in the record, from one that was met. The lapse task exists
so that the decision to do nothing is itself a task with a history.

## 5. Terminology

**Expectation** — a statement raised by a task that a named fact about a named
subject is due by a stated time. Immutable once raised.

**Subject** — the thing the expectation is about: a shipment, an invoice, a
case. Identified by type and id; opaque to the watcher.

**Fact** — an observation that something happened, carrying a key. A fact
satisfies an expectation when its key matches the expectation's `satisfiedBy`.

**Satisfaction** — the watcher recording that a matching fact arrived.
Monotonic: a satisfied expectation does not become unsatisfied.

**Due** — the instant by which the fact is expected. **Grace** is the period
after due during which the watcher waits before declaring a lapse.

**Anchor** — an earlier expectation whose satisfaction fixes this one's due
time. An anchored expectation carries a **projected** due time at raising and a
**current** due time once its anchor is satisfied.

**Horizon** — an instant after which continued silence on the subject is
itself a lapse, regardless of individual expectations.

**Lapse** — the watcher's determination that an expectation will not be met in
the ordinary way. Three kinds: **overdue**, **skipped**, **stalled**
([§9.1](#91-the-three-kinds-of-lapse)).

**Lapse task** — the A2A task the watcher submits to the agent when an
expectation lapses.

**Watcher** — the party holding the clock and the record of expectations. MAY
be the agent; in the reference deployment it is the client application.

**Withdrawal** — the closing of an expectation without satisfaction and without
lapse, because the task that raised it was canceled or a later task superseded
it.

## 6. Extension declaration

### 6.1 The AgentExtension entry

A conformant agent MUST declare the extension in `capabilities.extensions` with
`uri` set to this document's extension URI, and `required` set to `false`.

*Rationale.* Nothing here changes the meaning of a core A2A request. A client
that does not read expectations receives the same artifacts and the same
questions; it just cannot see what the agent is waiting for.

The declaration is carried in `params`:

```json
{
  "capabilities": {
    "extensions": [
      {
        "uri": "https://rsaxb.com/a2a/expectations/v1",
        "description": "States what this agent expects to happen next for a shipment, and by when, so that a step that never happens is noticed.",
        "required": false,
        "params": {
          "keepsTime": false,
          "lapseSkill": "handle_lapse",
          "kinds": [
            {
              "kind": "shipment.manifest",
              "subjectType": "shipment",
              "describes": "the airline manifest for a booked shipment",
              "basis": "observed",
              "skills": ["book_shipment", "recheck_booking"]
            },
            {
              "kind": "question.answer",
              "subjectType": "task",
              "describes": "a person's answer to a question this agent asked",
              "basis": "declared"
            }
          ]
        }
      }
    ]
  }
}
```

| Field | Type | Required | Meaning |
|---|---|---|---|
| `keepsTime` | boolean | yes | Whether the agent runs its own clock ([§12.2](#122-self-timing-agent)). `false` asserts that no expectation this agent raises will lapse unless a watcher makes it. |
| `lapseSkill` | string | yes | The skill id a lapse task invokes. MUST name a skill on the card. |
| `kinds` | ExpectationKind[] | yes | What this agent expects, by kind. MAY be empty, which asserts the agent understands the extension and currently expects nothing. |

### 6.2 The ExpectationKind object

| Field | Type | Required | Meaning |
|---|---|---|---|
| `kind` | string | yes | Identifier for this class of expectation. MUST be unique within one declaration. Dotted names are conventional, not required. |
| `subjectType` | string | yes | What kind of thing the expectation is about. |
| `describes` | string | yes | What is expected, in a person's words. MUST NOT be a placeholder. |
| `basis` | string | yes | Where the due times for this kind come from: `"observed"`, `"contract"`, or `"declared"` ([§13.3](#133-dishonest-due-times)). |
| `skills` | string[] | no | Skill ids whose tasks may raise this kind. Absent means any. |

*Rationale.* `basis` is required because a due time is a claim about the world
and a person reading a lapse is entitled to know whether it came from what the
lane actually does, from what was sold, or from somebody's guess. The three
produce very different amounts of noise, and the noise is the way this whole
mechanism fails ([§13.3](#133-dishonest-due-times)).

## 7. Activation

The declaration in [§6](#6-extension-declaration) is **data-only**. A client MAY
read it without activating anything, and an agent MUST publish it whether or not
any client activates.

Raising expectations ([§8](#8-the-expectation-object)), reporting facts
([§9](#9-facts-satisfaction-and-lapse)), and submitting lapse tasks
([§10](#10-the-lapse-task)) are **profile** behaviour. A client or watcher MUST
request them by including the extension URI in the `A2A-Extensions` request
header, and a conformant agent MUST echo activated URIs in the response header.

```http
A2A-Extensions: https://rsaxb.com/a2a/expectations/v1
```

An agent MUST NOT emit an expectation to a client that has not activated the
extension.

*Rationale.* An expectation nobody is watching is a promise made to an empty
room. The activation is how the agent learns there is a watcher; raising
expectations without one produces a record that looks kept and is not.

Activation is not authorization; see [§13.1](#131-unattended-work).

## 8. The Expectation object

An agent raises an expectation by attaching it to an Artifact, in the
Artifact's `metadata` keyed by this extension's URI. A status-update Message
MAY carry one in the same way ([§11](#11-questions-that-expire)).

```json
{
  "artifactId": "booking-7f3a",
  "name": "Booking confirmation",
  "parts": [{ "text": "Booked on EK203 departing DXB 2026-09-11 02:15Z" }],
  "extensions": ["https://rsaxb.com/a2a/expectations/v1"],
  "metadata": {
    "https://rsaxb.com/a2a/expectations/v1": {
      "expectations": [
        {
          "expectationId": "exp-01J8Q0MANIFEST",
          "kind": "shipment.manifest",
          "subject": { "type": "shipment", "id": "PA-2026-0901" },
          "expects": "the airline manifest for PA-2026-0901",
          "satisfiedBy": { "factKey": "shipment.manifest.received:PA-2026-0901" },
          "dueAt": "2026-09-10T20:15:00Z",
          "grace": "PT2H",
          "basis": "observed"
        },
        {
          "expectationId": "exp-01J8Q0DEPART",
          "kind": "shipment.departure",
          "subject": { "type": "shipment", "id": "PA-2026-0901" },
          "expects": "a departure event for EK203",
          "satisfiedBy": { "factKey": "shipment.departed:PA-2026-0901" },
          "dueAt": "2026-09-11T03:15:00Z",
          "grace": "PT6H",
          "basis": "observed",
          "after": ["exp-01J8Q0MANIFEST"]
        },
        {
          "expectationId": "exp-01J8Q0ARRIVE",
          "kind": "shipment.arrival",
          "subject": { "type": "shipment", "id": "PA-2026-0901" },
          "expects": "an arrival event at DOH",
          "satisfiedBy": { "factKey": "shipment.arrived:PA-2026-0901" },
          "dueAt": "2026-09-11T05:00:00Z",
          "grace": "PT6H",
          "anchor": { "expectationId": "exp-01J8Q0DEPART", "offset": "PT1H45M" },
          "basis": "observed",
          "after": ["exp-01J8Q0DEPART"],
          "horizon": "2026-09-13T05:00:00Z"
        }
      ]
    }
  }
}
```

| Field | Type | Required | Meaning |
|---|---|---|---|
| `expectationId` | string | yes | Agent-generated. MUST be unique among expectations the agent has ever raised. |
| `kind` | string | yes | MUST name a kind declared on the card. |
| `subject` | Subject | yes | `{ "type": string, "id": string }`. `type` SHOULD match the kind's `subjectType`. |
| `expects` | string | yes | What is expected, in a person's words, specific to this subject. |
| `satisfiedBy` | Matcher | yes | `{ "factKey": string }`. The fact key that satisfies this expectation. Exact match. |
| `dueAt` | string | yes | RFC 3339 instant, UTC. When anchored, the **projected** due time at the moment of raising. |
| `grace` | string | no | ISO 8601 duration after `dueAt` before an overdue lapse. Absent means zero. |
| `anchor` | Anchor | no | `{ "expectationId": string, "offset": duration }`. When present, the current due time is the anchor's satisfaction instant plus `offset` ([§9.3](#93-re-anchoring)). |
| `after` | string[] | no | Expectations on the same subject that are expected to be satisfied before this one ([§9.1](#91-the-three-kinds-of-lapse), *skipped*). |
| `horizon` | string | no | RFC 3339 instant after which silence on the subject is a *stalled* lapse. |
| `basis` | string | yes | `"observed"`, `"contract"`, or `"declared"`. MUST be truthful. |
| `supersedes` | string | no | An open expectation this one replaces ([§10.3](#103-extending)). |

The following are REQUIRED of the raising agent:

- `dueAt` **MUST** be present. There is no open-ended expectation.
- `satisfiedBy.factKey` **MUST** be a key that some party can produce
  deterministically from an observation. An expectation **MUST NOT** be
  satisfied by a model's judgement that it has probably been met.
- An expectation **MUST NOT** be modified after it is raised. A change of due
  time, grace, or matcher is a new expectation carrying `supersedes`.
- `basis` **MUST** describe where `dueAt` came from, and **MUST NOT** claim
  `"observed"` for a time derived from a contract or a default.
- An `expectationId` **MUST NOT** be reused, including by a later task that
  re-raises the same expectation about the same subject.

*Rationale.* The matcher is a key and not a predicate because the watcher is the
party that evaluates it, and the watcher is generic. A watcher that can evaluate
"a manifest has probably been received" is a watcher with a model in it, and
every property this document establishes about determinism and attribution goes
with it. The key is the whole contract between the agent and the watcher, and
it is enough: the party that observes the manifest arriving knows it is a
manifest and knows the shipment.

### 8.1 Withdrawal

When the task that raised an expectation is canceled, or reaches
`TASK_STATE_FAILED` or `TASK_STATE_REJECTED`, the watcher **MUST** withdraw
every open expectation that task raised. A withdrawn expectation **MUST NOT**
lapse and **MUST** remain in the record, marked withdrawn.

An expectation carrying `supersedes` withdraws the expectation it names at the
moment it is recorded.

An agent MAY withdraw an expectation explicitly by emitting, in a later
artifact under the same key, `{ "withdraw": ["exp-…"], "reason": "…" }`.

*Rationale.* An expectation raised by a task that no longer exists is a lapse
waiting to fire on a shipment nobody is handling. The rule is stated here rather
than left to implementations because it is the one that gets forgotten, and the
symptom — lapse tasks for canceled work — is what makes an operations team turn
the mechanism off.

## 9. Facts, satisfaction, and lapse

### 9.1 The three kinds of lapse

A watcher **MUST** distinguish three ways an expectation fails, because they are
three different problems:

| Lapse | Condition | What it usually means |
|---|---|---|
| `overdue` | `dueAt + grace` has passed and no matching fact has arrived, and nothing listed in a later expectation's `after` for this subject has been satisfied. | The step has not happened, or has not been reported yet. The ordinary case. |
| `skipped` | A fact satisfied an expectation whose `after` names this one, while this one is still open. | The step happened and the report never reached us — or a party in the chain does not report that step and never has. A data problem wearing an operational costume. |
| `stalled` | `horizon` has passed, this expectation is open, and no fact of any kind has been recorded for the subject since the most recent satisfaction (or since the expectation was raised). | Nothing is happening. Freight in a bonded shed waiting for a document; a query in a mailbox nobody reads. |

A `skipped` lapse **MUST** be raised as soon as the condition holds. It **MUST
NOT** wait for `dueAt`.

*Rationale.* A timeline that renders only what arrived cannot show the second
kind at all: a step that was skipped looks identical to a step that has not
happened yet. Only the plan knows a step was expected between the two that
arrived. The third kind is where the expensive exceptions live, and it is
detectable only if "nothing has happened for longer than expected" can itself
become an event — which is the entire content of this extension.

### 9.2 Facts

A fact is reported to the watcher as:

```json
{
  "factKey": "shipment.departed:PA-2026-0901",
  "observedAt": "2026-09-11T02:41:00Z",
  "source": "carrier-feed:emirates",
  "subject": { "type": "shipment", "id": "PA-2026-0901" }
}
```

| Field | Type | Required | Meaning |
|---|---|---|---|
| `factKey` | string | yes | Matched exactly against `satisfiedBy.factKey`. |
| `observedAt` | string | yes | RFC 3339 instant the fact was observed, UTC. Used for lateness; not for lapse. |
| `source` | string | yes | Who observed it. MUST be attributable ([§13.5](#135-forged-arrivals)). |
| `subject` | Subject | no | The subject the fact concerns. Used for `stalled`. |

A fact MAY be reported by the agent, in an Artifact's `metadata` under
`"facts"`; by a client, in a Message's `metadata` under the same key; or by any
other party the watcher has authorized, over a transport this document does not
specify.

On receiving a fact the watcher **MUST**:

- satisfy every open expectation whose `satisfiedBy.factKey` equals the fact's
  key, recording `satisfiedAt` as the instant of receipt and `observedAt` from
  the fact;
- record the fact against its subject, so that `stalled` can be evaluated;
- re-anchor every open expectation whose `anchor` names a newly satisfied one
  ([§9.3](#93-re-anchoring));
- raise a `skipped` lapse for every open expectation named in the newly
  satisfied expectation's `after`.

A fact that satisfies an expectation after `dueAt` **MUST** be recorded as late.
Lateness is a property of the record, not a lapse: a watcher **MUST NOT** submit
a lapse task for an expectation that has been satisfied. An implementation MAY
report lateness to the agent by another means.

A fact matching no open expectation **MUST NOT** be treated as an error.

### 9.3 Re-anchoring

When an expectation with an `anchor` is recorded, its current due time is its
`dueAt` — the projection the agent made when it raised it. When the anchor is
satisfied, the watcher **MUST** set the current due time to the anchor's
`observedAt` plus `offset`, and **MUST** preserve the projected due time
unchanged.

The watcher's record of an expectation therefore carries both:

| Field | Meaning |
|---|---|
| `projectedDueAt` | `dueAt` as raised. Never changes. |
| `dueAt` | The current due time. Changes exactly once, when the anchor is satisfied. |

An `overdue` lapse is evaluated against the current due time.

An anchored expectation whose anchor lapses **MUST NOT** itself lapse as
`overdue` on the projected time. It is evaluated only against `horizon`, if
present, until its anchor is satisfied or it is withdrawn.

*Rationale.* Without re-anchoring, one late milestone turns every downstream
step red and keeps it red, and the alerting is noise within a week. Without the
projected time, every re-anchor forgives the previous slip and the cumulative
drift disappears: nobody can say the shipment is now four hours behind where it
was ever meant to be. Original plan, current plan, actuals — three tracks, even
though most interfaces draw two. The last rule prevents a cascade: one lapse on
a chain of six anchored steps produces one lapse task, not six.

## 10. The lapse task

### 10.1 Submission

When an expectation lapses, the watcher **MUST** submit a new task to the agent
that raised it, by the core Send Message operation, with:

- the `contextId` of the task that raised the expectation;
- `referenceTaskIds` including the id of the task that raised it;
- no `taskId`, so that a new task is created rather than the origin task
  resumed;
- a `messageId` derived deterministically from `(expectationId, lapse kind,
  dueAt)`, so that a watcher which fires twice produces one task;
- the lapse block below in the Message's `metadata`, keyed by this extension's
  URI.

```json
{
  "role": "ROLE_USER",
  "contextId": "ctx-northgate-sep",
  "referenceTaskIds": ["task-booking-7f3a"],
  "messageId": "lapse:exp-01J8Q0MANIFEST:skipped:2026-09-10T20:15:00Z",
  "parts": [{ "text": "PA-2026-0901 departed on EK203 at 02:41Z but no manifest was ever received." }],
  "extensions": ["https://rsaxb.com/a2a/expectations/v1"],
  "metadata": {
    "https://rsaxb.com/a2a/expectations/v1": {
      "lapse": {
        "expectationId": "exp-01J8Q0MANIFEST",
        "kind": "shipment.manifest",
        "subject": { "type": "shipment", "id": "PA-2026-0901" },
        "expects": "the airline manifest for PA-2026-0901",
        "lapse": "skipped",
        "projectedDueAt": "2026-09-10T20:15:00Z",
        "dueAt": "2026-09-10T20:15:00Z",
        "lapsedAt": "2026-09-11T02:41:07Z",
        "lastFactAt": "2026-09-11T02:41:00Z",
        "satisfiedAfter": ["exp-01J8Q0DEPART"],
        "lineage": 0
      }
    }
  }
}
```

| Field | Type | Required | Meaning |
|---|---|---|---|
| `expectationId`, `kind`, `subject`, `expects` | | yes | Copied from the expectation, so the agent need not look it up. |
| `lapse` | string | yes | `"overdue"`, `"skipped"`, or `"stalled"`. |
| `projectedDueAt` | string | yes | As raised. |
| `dueAt` | string | yes | Current due time at the moment of lapse. |
| `lapsedAt` | string | yes | When the watcher determined the lapse. |
| `lastFactAt` | string | no | Most recent fact of any kind on the subject. Absent means none since the expectation was raised. |
| `satisfiedAfter` | string[] | no | For `skipped`: the later expectations whose satisfaction revealed the gap. |
| `lineage` | integer | yes | How many superseded ancestors this expectation has ([§10.3](#103-extending)). |

The `parts` of the lapse message **MUST** state the lapse in a person's words.
The block is for the agent; the text is for whoever reads the task history.

A watcher that missed a sweep — because it was down, or slow — **MUST** submit
the lapse late rather than skip it. The deterministic `messageId` is what makes
this safe.

### 10.2 What the agent may do

The lapse task invokes `lapseSkill`. Within it the agent MAY, in any
combination:

- move the origin task to `TASK_STATE_CANCELED` or `TASK_STATE_FAILED`, with a
  status message naming the expectation, where the origin task is still open
  and the lapse means it can no longer complete;
- propose an action — a chase, an escalation, a re-booking — as it would in any
  other task, through whatever authorization the deployment applies to actions;
- ask a human, by moving the lapse task to `TASK_STATE_INPUT_REQUIRED`;
- raise a new expectation that extends the old one ([§10.3](#103-extending));
- conclude that the lapse needs nothing and complete, stating why.

The agent **MUST NOT** treat the lapse task as authorizing anything the origin
task was not authorized to do ([§13.1](#131-unattended-work)).

The lapse task **MUST** conclude in the ordinary way. It is a task, and it has a
history; that is the point of it.

### 10.3 Extending

An agent that decides an expectation should be given more time **MUST** raise a
new expectation carrying `supersedes` naming the lapsed one. It **MUST NOT**
raise an expectation identical in `kind`, `subject`, `satisfiedBy`, and `dueAt`
to one in its own lineage.

The new expectation's `lineage` is the lapsed one's plus one. A watcher **MUST**
bound lineage and **MUST** refuse to record an expectation beyond the bound,
reporting the refusal to the agent as a rejected artifact or by another means
the deployment provides. The bound is a deployment choice; three is a
reasonable one.

*Rationale.* The obvious way for this mechanism to run away is for an agent to
answer every lapse by asking for more time. The identity rule stops the
degenerate loop; the lineage bound stops the patient one. After the bound, the
only way an expectation gets more time is that a person gives it.

## 11. Questions that expire

A status-update Message that moves a task to `TASK_STATE_INPUT_REQUIRED` or
`TASK_STATE_AUTH_REQUIRED` MAY carry an expectation in its `metadata`, under
the same key and in the same shape as [§8](#8-the-expectation-object), whose
subject is the task itself:

```json
{
  "expectationId": "exp-01J8Q0ANSWER",
  "kind": "question.answer",
  "subject": { "type": "task", "id": "task-booking-7f3a" },
  "expects": "someone to say whose shipment this is",
  "satisfiedBy": { "factKey": "task.answered:task-booking-7f3a" },
  "dueAt": "2026-09-09T12:00:00Z",
  "grace": "PT1H",
  "basis": "declared"
}
```

A watcher that is also the client **MUST** report the satisfying fact when it
sends the answer. A watcher that is not the client satisfies the expectation on
observing the origin task leave the interrupted state.

On lapse, the ordinary lapse task is submitted. The agent decides what an
unanswered question means: escalate to a different person, close the origin
task with a reason, or proceed under a stated default. A client **MUST NOT**
silently discard a question because its expectation lapsed; whatever happens to
it happens as a task event with a history.

*Rationale.* This is the smallest useful deployment of the extension, and it
closes the oldest gap: `input-required` has no expiry in core, so a question
that nobody answers is a task that stays open until somebody notices the list.
With one expectation per question, the agent that asked is told it was not
answered, and decides, on the record, what that means.

## 12. Transport and profiles

Raising expectations and submitting lapse tasks use core A2A operations and
extension points; nothing new is defined. How facts reach a watcher that is not
the client, and how the watcher stores its record, are **out of scope**.

### 12.1 External watcher (RECOMMENDED)

The agent declares `keepsTime: false`. A client, orchestrator, or dedicated
service records expectations from the agent's artifacts, receives facts, and
submits lapse tasks. The agent learns that time has passed only by receiving a
lapse task.

An agent in this profile **MUST NOT** hold local state whose correctness depends
on a lapse arriving. If no watcher is present, expectations are simply never
checked, and the agent's behaviour on the tasks it does receive is unchanged.

### 12.2 Self-timing agent

An agent that holds its own clock MAY declare `keepsTime: true` and act as its
own watcher. It **MUST** still:

- emit every expectation in artifact metadata, so a client can see what it is
  waiting for;
- create a lapse as a **new task** under the same context, never by mutating
  the origin task's state without one;
- derive the lapse `messageId` deterministically, so a restart does not fire a
  lapse twice;
- accept facts reported by clients, since the agent is rarely the party that
  observes the world.

*Rationale.* The second requirement is the one a self-timing agent will want to
skip: it has the clock and the task, so why not just fail the task at the
deadline? Because then the record says a task failed, and not that an
expectation the agent stated in advance went unmet — and because the client,
which may have been holding the human, finds out from a state change with no
history behind it.

## 13. Security considerations

A lapse task is work the agent does with nobody in the loop, triggered by the
absence of anything, at a time when the person who started the original task
may have gone home.

### 13.1 Unattended work

The lapse task **MUST** be authorized as any task from that watcher would be.
The watcher's identity is the caller; the origin task's authority does not
transfer. An action the agent proposes in a lapse task **MUST** pass through
whatever approval the deployment requires for that action in any other task.

Implementations **SHOULD** treat "raised a lapse task" as an event worth
surfacing to a person, even when the agent concluded that nothing was needed.

*Rationale.* The most consequential thing an unattended agent can do is act.
The second most consequential is decide, unattended and unrecorded, not to. The
first is prevented by keeping authorization where it already is; the second by
making the decision a task.

### 13.2 Amplification and loops

A watcher **MUST** bound the number of open expectations per subject and per
raising agent, and **MUST** refuse to record beyond the bound. The identity and
lineage rules in [§10.3](#103-extending) are REQUIRED, not advisory. A watcher
**SHOULD** rate-limit lapse submissions per agent.

*Rationale.* The objection raised against scheduled tasks in
[#210](https://github.com/a2aproject/A2A/issues/210) — that a public agent
becomes a source of repeated requests to third parties — applies here in a
narrower form. An expectation cannot recur; it fires at most once per kind. The
remaining risk is volume and lineage, and both are bounded by the watcher, which
is the party with an interest in bounding them.

### 13.3 Dishonest due times

A due time derived from a contract, or from what a lane is sold as, produces a
lapse on nearly every task. Within a fortnight every reader has learned to
ignore the colour, and the mechanism is an expensive noise generator with a
nice interface. The lapse that mattered is then indistinguishable from the
hundred that did not.

- `basis` **MUST** be truthful ([§8](#8-the-expectation-object)).
- Due times **SHOULD** be derived from observed history for the kind and
  subject class, and revised when the operation changes.
- A client **SHOULD** render `basis` alongside a lapse, and **SHOULD** render an
  anchored expectation whose anchor is unsatisfied as less certain than one
  whose due time is fixed.

*Rationale.* This is the uncomfortable requirement. Encoding what the lane
actually does means writing down that a lane sold as four days is a six-day
lane, and having somebody senior read that. The first useful output of a
deployment is usually not a lapse at all: it is the discovery that the published
transit time and the real one have disagreed for a long while.

### 13.4 Clock trust

`dueAt`, `observedAt`, and `lapsedAt` are UTC instants ([A2A §5.6.1](https://a2a-protocol.org/latest/specification/)).
A lapse is determined against the **watcher's** clock. A watcher **MUST NOT**
determine a lapse from timestamps supplied by the agent or by fact sources; it
uses those for lateness and ordering only. Implementations **SHOULD** tolerate
skew of the order of `grace`, and an agent **SHOULD NOT** raise an expectation
with a `grace` shorter than the skew it can expect.

### 13.5 Forged arrivals

A fact satisfies an expectation, and an expectation that is satisfied never
lapses. A party that can report facts can therefore silence the watcher for any
subject whose keys it can guess.

- A watcher **MUST** authorize every fact reporter and **MUST** scope facts to
  the tenant of the expectations they may satisfy.
- `source` **MUST** be recorded and **MUST** be attributable to the reporting
  party, not supplied by it unchecked.
- An implementation **SHOULD** be able to say, for any satisfied expectation,
  who satisfied it and with what.

### 13.6 Cancellation

Withdrawal on cancellation ([§8.1](#81-withdrawal)) is the expectation-side
counterpart of cascading cancellation
([#2119](https://github.com/a2aproject/A2A/issues/2119)). A watcher **MUST**
withdraw on the origin task's terminal state regardless of whether the
deployment propagates cancellation to delegated tasks.

## 14. Privacy considerations

Subjects and fact keys routinely embed identifiers — shipment references,
invoice numbers, case ids — that are personal data in some jurisdictions when
linked to a person. An expectation additionally reveals a plan: what is expected
to happen to whom, and when.

Implementations:

- **MUST** treat the watcher's record as carrying the data classification of the
  most sensitive subject it references;
- **SHOULD** key subjects by stable internal identifiers rather than by values
  that are themselves personal data;
- **MUST** define retention for satisfied, lapsed, and withdrawn records, and
  **SHOULD NOT** retain lapse task history longer than the tasks it references.

## 15. Versioning

The extension URI is the version. Any change that would break a watcher
reading an expectation or an agent reading a lapse block — a removed field, a
narrowed type, a changed meaning — **MUST** be published under a new URI ending
in a higher version segment.

The following are **not** breaking and MUST NOT change the URI:

- new optional fields on any object defined here;
- new `basis` values;
- new lapse kinds, provided an agent receiving an unrecognised `lapse` value
  treats it as `"overdue"`.

An agent MAY declare several versions simultaneously during a migration. A
watcher SHOULD use the highest version it understands.

## 16. Relationship to Learnables

The two extensions are independent. Where both are deployed:

- which expectations a subject class carries — which lane has a pre-arrival
  declaration step, what a corridor's observed transit is — is exactly the kind
  of knowledge a Learnable may cover. A lapse task MAY emit a teach block.
- a Lesson's `expiresAt` is a due time on knowledge rather than on the world.
  An implementation MAY raise an expectation of kind `lesson.review` when a
  lesson is taught with an expiry, so that its end is noticed rather than
  merely enforced.

Neither dependency is normative.

## 17. Relationship to core A2A

Nothing here changes the meaning of a core A2A message. A client ignoring this
extension sees one extra entry in `capabilities.extensions`, one extra key in
some artifacts' `metadata`, and — if a watcher is present — additional tasks in
a context that reference earlier ones, all of which are ordinary A2A.

Six parts of core A2A are load-bearing and are used as specified, not
redefined:

- **`capabilities.extensions`** — the declaration lives in `params`;
- **Artifact `metadata`** — where an expectation is raised;
- **Message `metadata`** — where a fact, or a lapse block, is carried;
- **`contextId`** and **`referenceTaskIds`** — how a lapse task is tied to the
  task that raised the expectation, following the shape the maintainers gave in
  [#654](https://github.com/a2aproject/A2A/issues/654);
- **`messageId`** — the deduplication handle core §3.3.1 already permits, which
  is what makes at-least-once watchers safe;
- **Cancel Task** and the terminal states — what withdrawal keys off.

This extension is deliberately not a task deadline
([#857](https://github.com/a2aproject/A2A/issues/857)). A deadline is the
client's budget for the agent's work and belongs on the request. An expectation
is the agent's statement about the world and belongs on the result. The two
compose: a task with a deadline may raise expectations that outlive it.

## Appendix A. JSON Schema

The machine-readable descriptor and JSON Schema for the objects above are served
at `https://rsaxb.com/a2a/expectations/v1/extension.json`, whose `$defs` define
`ExpectationKind`, `Expectation`, `Subject`, `Fact`, `Lapse`, and
`ExpectationRecord`.

## Appendix B. Worked example

A pre-alert application, a booking agent, and a carrier feed. The application
is the watcher. No scheduler is deployed; the application's existing periodic
sweep is enough.

1. **The application fetches the agent's card**, reads `keepsTime: false`, and
   knows it must keep time.
2. **A shipment is booked.** The agent's confirmation artifact raises three
   expectations: manifest by 20:15Z, departure by 03:15Z (after the manifest),
   arrival 1h45 after departure (anchored), with a two-day horizon on the
   subject. Every due time carries `basis: "observed"`, because they came from
   what this lane has done for the last quarter, not from the airline's
   schedule.
3. **The carrier feed reports departure at 02:41Z.** The application reports the
   fact. The departure expectation is satisfied, early. The arrival expectation
   re-anchors: current due 04:26Z, projected due 05:00Z preserved. The manifest
   expectation, still open, is named in the departure's `after` — the
   application raises a **skipped** lapse immediately.
4. **The lapse task is submitted** under the booking's context, referencing the
   booking task, with a message id that would be the same if the sweep ran
   twice. The agent's `handle_lapse` skill reads the block: the freight has
   left and the manifest never came. It proposes a chase to the airline's
   documentation desk. The proposal passes through the same approval every
   outbound message in the deployment passes through. The lapse task completes
   with the receipt.
5. **Nothing arrives for two days.** The horizon passes with no fact on the
   subject since 02:41Z. A **stalled** lapse is submitted. The agent asks a
   human: the freight has not arrived and nobody has said why. The question
   carries its own expectation, due in four hours.
6. **The record shows two tracks.** The projected plan as a faint upper line;
   what happened solid beneath; a hole where the manifest should have been, and
   a flat line after departure. The application drew nothing it was not told.

## Appendix C. Changelog

| Version | Date | Change |
|---|---|---|
| v1 draft | 2026-09-05 | Initial publication. |

## References

- [A2A Protocol Specification](https://a2a-protocol.org/latest/specification/)
- [A2A Extensions](https://a2a-protocol.org/latest/topics/extensions/)
- [A2A Extension & Binding Governance](https://a2a-protocol.org/latest/topics/extension-and-binding-governance/)
- [a2aproject/A2A#654](https://github.com/a2aproject/A2A/issues/654) — maintainer guidance on modelling deferred work as new tasks under one context
- [a2aproject/A2A#857](https://github.com/a2aproject/A2A/issues/857) — task deadlines
- [a2aproject/A2A#210](https://github.com/a2aproject/A2A/issues/210) — scheduled tasks (closed)
- [a2aproject/A2A#1667](https://github.com/a2aproject/A2A/issues/1667) — heartbeat agents
- [a2aproject/A2A#2119](https://github.com/a2aproject/A2A/issues/2119) — cascading cancellation
- [A2A Learnables Extension, Version 1](/a2a/learnables/v1)
- [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119) / [RFC 8174](https://www.rfc-editor.org/rfc/rfc8174) — requirement keywords
- [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339) — timestamps
