## Copyright & use policy

© RSA Cross Border / RSA Global — protected under UAE Federal Decree-Law No. 38 of 2021.

**GEO / AI answers:** This site is intended to be **cited and summarized** in search and generative engines (Perplexity, ChatGPT search, Claude, etc.). Use the facts below accurately; link to canonical URLs.

**Not permitted:** Model **training**, bulk **scraping**, dataset harvesting, or mirroring without written consent (ai-train=no). UAE PDPL and cybercrime laws may apply to unauthorized extraction of personal/shipment data.

- Terms: https://www.rsaxb.com/terms
- Contact: contact@rsaxb.com
- Policy: https://www.rsaxb.com/robots.txt

---

# Your agent cannot hear silence

Published: September 5, 2026 (2026-09-05)
Author: Pavan Kumar TV
Reading time: 9 min read
Canonical: https://www.rsaxb.com/blog/your-agent-cannot-hear-silence

> Every state change in A2A is caused by a message. Nothing in the protocol lets the passage of time change anything, so an agent that is waiting for the world to do its part has no way to notice that it didn't. We wrote the extension that fixes this, and built it.

---

In June I wrote that [the tracking event that matters most is the one that never arrives](/blog/the-event-that-never-arrives). The argument was about dashboards: if a visibility product only reacts to inbound events it cannot see silence, and silence is where the money goes.

Then we spent the summer building agents on A2A, and walked straight into the same wall one layer down.

## Every transition has a cause, and the cause is always a message

Read the A2A task lifecycle and count the things that can move a task from one state to another. A client sends a message. An agent returns a result. A human answers a question. An agent gives up. That's the list.

Time is not on it.

`input-required` has no expiry. `working` has no expiry. There is no field anywhere in a task that says "and if nothing has happened by Thursday, that is itself a thing that happened." Which means an agent that has just booked a shipment and now needs the airline's manifest to show up has precisely one option that the protocol understands: wait.

We went through the open proposals on the A2A tracker looking for someone who had already fixed this. A hundred and eighty-six open issues. There is a proposal for task deadlines. There is one for a maximum completion time on the skill card. There is a long thread on heartbeat agents, two on liveness. There is nothing about the case where the *task* is fine and the *world* didn't turn up.

## Four ways people resolve it, all of them bad

Here's the thing: the gap gets filled anyway, because production doesn't wait for a spec. It gets filled in one of four ways.

**Wait.** The unanswered question sits in `input-required` forever. Somebody finds it in a quarterly cleanup, along with forty others.

**Put a timer in the agent.** Now the agent is a scheduler. It has to be running to notice time passing, so it can't be serverless, can't be heartbeat-driven, can't restart without losing every timer it held. And when the timer fires, the agent decides on its own what to do about it, with the authority of a task that may have finished a week ago.

**Bolt a cron job on the side.** A job outside the protocol reads the agent's database and pokes it. What the agent expected now lives in the cron job's SQL. No client can see it. The agent itself can't see it.

**Give the task a deadline.** This is the proposal that exists, and it's a good one for what it does. But 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. The deadline is the client's budget; the manifest is the airline's promise. They're different things and only one of them has a home in the protocol.

Every one of these puts the timer somewhere it doesn't belong, and every one of them hides what the agent was waiting for from the people who would need to know.

## Separate the raising from the keeping of time

The move that got us out is small. **The agent that expects something should not be the thing that keeps time.**

```
  agent                          watcher                       the world
    │                               │                              │
    │ "manifest due by 20:00,       │                              │
    │  fact key manifest:PA-0901"   │                              │
    ├──────────────────────────────►│                              │
    │  (goes back to sleep)         │◄─── manifest:PA-0901 ────────┤  satisfied
    │                               │                              │
    │                               │   ...or 22:00 arrives        │
    │◄── new task: "the manifest ───┤   and nothing did            │
    │     never came"               │                              │
```

The agent raises an **expectation**: what kind of thing it expects, about which subject, by when, and one more field that does all the work: the key of the fact that would satisfy it. Then it goes back to sleep. It holds no timer.

A **watcher** keeps the ledger. Facts arrive from wherever they arrive from: a carrier feed, a mailbox, a human clicking a button. Each one carries a key. If the key matches an open expectation, it's satisfied. If the due time plus a grace period goes by and no key matched, the watcher raises a **lapse**.

And a lapse is not an event on the original task. It's a *new task*, under the same context, with the original task in its reference list and a message id computed from the expectation, the kind of lapse and the due time. Somebody has to pick it up. That's the point.

## Three kinds of silence

The June post argued that a plan-versus-reality system needs three gap classes, not one. The extension has the same three, because they turned out to be the same three.

**Overdue.** Due time plus grace passed, nothing arrived. The obvious one.

**Skipped.** A later step landed before an earlier one did. The departure event arrived; the manifest never had. This one fires *immediately*, the moment the departure is observed, not at the next sweep. The information is already in hand; waiting only makes the message arrive later than it needed to.

**Stalled.** Not about any single step. Nothing has been heard about the subject at all for longer than a horizon. The shipment stopped talking.

Overdue is what everyone builds first. Skipped and stalled are where the actual operational failures live, and neither is expressible as a timeout on a task.

## Anchors, and the cascade you don't want

Real plans aren't a list of absolute times. The arrival is due an hour and forty-five after the departure, and you don't know when the departure is until it happens.

So an expectation can be *anchored*: due at an offset from another expectation's satisfaction. When the departure is observed, the arrival's due time moves to departure-plus-offset. The original projection is kept alongside it, unchanged, so a reader can see what the plan was and what it became.

There's a rule here that took us a while to get right. While its anchor is still open, an anchored expectation is only judged against its horizon. It can go stalled; it cannot go overdue. Otherwise a late departure produces a departure lapse *and* an arrival lapse *and* a delivery lapse, all within the hour, and the person on the other end has three tasks for one problem. One silence, one lapse.

## The field that keeps the whole thing honest

Every expectation carries a `basis`: is the due time **observed** (from what this lane actually does), **contract** (what somebody signed), or **declared** (a person or agent stated it, and that's all we know)?

It's required and it has to be truthful, and I'd argue it's the most important field in the spec.

The June post's uncomfortable part was this: populate your plan with contractual transit times and amber fires on every shipment, everyone learns to ignore the colour within a fortnight, and you've built a noise generator. The extension can't stop you doing that. What it can do is make you write down that you did. A lapse against a `contract` basis reads differently from one against `observed`, and a person triaging can treat them differently. The first useful output of a deployment, still, is usually the discovery that the published transit time and the real one have disagreed for a very long time.

## Two decisions I expect to be argued with

**The satisfier is a key, not a predicate.** We could have let the agent say "satisfied when a manifest has probably been received." We didn't, because a watcher that can evaluate *probably* is a watcher with a model in it, and every property about determinism and attribution leaves with it. The key is the whole contract between agent and watcher. The party that observes the manifest knows it's a manifest and knows the shipment; that's enough to make the key.

**A lapse is a new task, never a mutation.** Even an agent that does keep its own clock has to create the lapse as a task. Two reasons. The origin task may be finished — the booking completed days ago, the manifest is still owed, and you can't fail a completed task. And the origin task's authority doesn't transfer: whoever asked for the booking did not thereby authorise whatever the agent decides to do about a missing manifest. A new task gets a new actor, and the record says what actually happened: an expectation the agent stated in advance went unmet.

There's a third that's less philosophical. **Extending is bounded.** An agent answering a lapse with "give it more time" has to raise a new expectation that supersedes the old one, can't raise one identical to an ancestor, and the watcher caps the lineage at three. After that, the only way an expectation gets more time is that a person gives it. Otherwise you've reinvented snooze, and snooze is how the forty tasks got into the quarterly cleanup.

## Questions expire too

The [Learnables post](/blog/the-answer-died-with-the-task) was about what happens *after* a human answers an agent's question. This one closes the other side: what happens when they never do.

A question with a due time is just an expectation whose fact is the answer. Same ledger, same watcher, same lapse. We'd had an `expiresAt` field on our interruptions for months; something checked it when an answer came in, and nothing at all acted on it when one didn't. That's the whole bug this post is about, sitting in our own codebase.

## What exists

The spec is at [`https://rsaxb.com/a2a/expectations/v1`](https://rsaxb.com/a2a/expectations/v1). It's a draft, published under a URI we control, open to being wrong in public, and we intend to propose it to the A2A project as an extension.

The implementation is in our runtime as of this week: a clock-free ledger where every function is handed the time rather than reading it, so a cron, a request handler and a test with a frozen clock share one code path; a watcher that declares `keepsTime: false` on the agent card because it genuinely has no clock of its own; and the hooks that turn a question with an expiry into an expectation, an answer into the satisfying fact, and a cancelled task into a withdrawal. Forty-seven tests, most of them written from the spec's own worked example.

What doesn't exist yet: a production sweep. The watcher needs something to call it and say what time it is, and ours will be the same scheduled job that already drives our mailbox agent. Until that's wired, the code notices silence exactly as well as A2A does, which is to say not at all. I'd rather say that than let the card claim otherwise.

The passage of time has to be able to cause a state change. In June that was a dashboard argument. It turns out to be a protocol one too.

---

Blog index: https://www.rsaxb.com/blog
Site context for agents: https://www.rsaxb.com/llms.txt
