{
  "uri": "https://rsaxb.com/a2a/expectations/v1",
  "name": "Expectations",
  "version": "1",
  "status": "draft",
  "published": "2026-09-05",
  "updated": "2026-09-05",
  "protocol": "A2A",
  "description": "Lets an agent state what it expects to happen next for a subject and by when; lets a watcher satisfy that deterministically from facts, re-anchor dependent steps, and turn an overdue, skipped or stalled expectation into a new task under the same context - so that the passage of time can cause a state change without putting a clock in the agent.",
  "categories": [
    "data-only",
    "profile"
  ],
  "required": false,
  "specification": "https://rsaxb.com/a2a/expectations/v1",
  "specificationMarkdown": "https://rsaxb.com/a2a/expectations/v1.md",
  "publisher": {
    "name": "RSA Cross Border",
    "url": "https://www.rsaxb.com/"
  },
  "license": "CC-BY-4.0",
  "note": "Independent extension. Not published by, endorsed by, or affiliated with the A2A project.",
  "activation": {
    "header": "A2A-Extensions",
    "value": "https://rsaxb.com/a2a/expectations/v1",
    "dataOnly": "The declaration in capabilities.extensions[].params may be read without activation.",
    "profile": "Raising expectations, reporting facts and submitting lapse tasks require activation, and the agent must echo the URI in the response header."
  },
  "declaration": {
    "location": "capabilities.extensions[]",
    "params": {
      "type": "object",
      "required": [
        "keepsTime",
        "lapseSkill",
        "kinds"
      ],
      "properties": {
        "keepsTime": {
          "type": "boolean",
          "description": "Whether the agent runs its own clock. false asserts no expectation it raises will lapse unless a watcher makes it."
        },
        "lapseSkill": {
          "type": "string",
          "description": "The skill id a lapse task invokes. MUST name a skill on the card."
        },
        "kinds": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ExpectationKind"
          }
        }
      }
    }
  },
  "extensionPoints": {
    "artifactMetadata": {
      "expectations": {
        "type": "array",
        "items": {
          "$ref": "#/$defs/Expectation"
        }
      },
      "facts": {
        "type": "array",
        "items": {
          "$ref": "#/$defs/Fact"
        }
      },
      "withdraw": {
        "type": "array",
        "items": {
          "type": "string"
        }
      }
    },
    "messageMetadata": {
      "facts": {
        "type": "array",
        "items": {
          "$ref": "#/$defs/Fact"
        }
      },
      "lapse": {
        "$ref": "#/$defs/Lapse"
      }
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://rsaxb.com/a2a/expectations/v1/extension.json",
  "$defs": {
    "ExpectationKind": {
      "type": "object",
      "required": [
        "kind",
        "subjectType",
        "describes",
        "basis"
      ],
      "properties": {
        "kind": {
          "type": "string",
          "description": "Identifier for this class of expectation. Unique within one declaration."
        },
        "subjectType": {
          "type": "string"
        },
        "describes": {
          "type": "string",
          "minLength": 1,
          "description": "What is expected, in a person's words. MUST NOT be a placeholder."
        },
        "basis": {
          "type": "string",
          "description": "Where due times for this kind come from. \"observed\" (from what the subject class actually does), \"contract\" (from what was sold or agreed), or \"declared\" (a stated default). MUST be truthful.",
          "examples": [
            "observed",
            "contract",
            "declared"
          ]
        },
        "skills": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Skill ids whose tasks may raise this kind. Absent means any."
        }
      }
    },
    "Expectation": {
      "type": "object",
      "required": [
        "expectationId",
        "kind",
        "subject",
        "expects",
        "satisfiedBy",
        "dueAt",
        "basis"
      ],
      "description": "Raised in an Artifact's metadata (or an input-required status Message's metadata), keyed by the extension URI, under \"expectations\". Immutable once raised.",
      "properties": {
        "expectationId": {
          "type": "string",
          "description": "Agent-generated. MUST be unique among expectations the agent has ever raised; never reused."
        },
        "kind": {
          "type": "string",
          "description": "MUST name a kind declared on the card."
        },
        "subject": {
          "$ref": "#/$defs/Subject"
        },
        "expects": {
          "type": "string",
          "description": "What is expected, in a person's words, specific to this subject."
        },
        "satisfiedBy": {
          "type": "object",
          "required": [
            "factKey"
          ],
          "properties": {
            "factKey": {
              "type": "string",
              "description": "The fact key that satisfies this expectation. Exact match; never a model's judgement."
            }
          }
        },
        "dueAt": {
          "type": "string",
          "format": "date-time",
          "description": "UTC. REQUIRED: there is no open-ended expectation. When anchored, this is the projected due time at the moment of raising."
        },
        "grace": {
          "type": "string",
          "format": "duration",
          "description": "ISO 8601 duration after dueAt before an overdue lapse. Absent means zero."
        },
        "anchor": {
          "type": "object",
          "required": [
            "expectationId",
            "offset"
          ],
          "description": "When present, the current due time becomes the anchor's observedAt plus offset once the anchor is satisfied. The projected dueAt is preserved.",
          "properties": {
            "expectationId": {
              "type": "string"
            },
            "offset": {
              "type": "string",
              "format": "duration"
            }
          }
        },
        "after": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Expectations on the same subject expected to be satisfied before this one. Satisfying this one while any of them is open raises a \"skipped\" lapse on it immediately."
        },
        "horizon": {
          "type": "string",
          "format": "date-time",
          "description": "Instant after which silence on the subject is a \"stalled\" lapse."
        },
        "basis": {
          "type": "string",
          "examples": [
            "observed",
            "contract",
            "declared"
          ]
        },
        "supersedes": {
          "type": "string",
          "description": "An open expectation this one replaces. Withdraws it when recorded. Lineage bounded by the watcher."
        }
      }
    },
    "Subject": {
      "type": "object",
      "required": [
        "type",
        "id"
      ],
      "properties": {
        "type": {
          "type": "string",
          "description": "What kind of thing this is. SHOULD match the kind's subjectType."
        },
        "id": {
          "type": "string",
          "description": "Opaque to the watcher."
        }
      }
    },
    "Fact": {
      "type": "object",
      "required": [
        "factKey",
        "observedAt",
        "source"
      ],
      "description": "Reported in an Artifact's or Message's metadata, keyed by the extension URI, under \"facts\"; or over a transport this specification does not define.",
      "properties": {
        "factKey": {
          "type": "string",
          "description": "Matched exactly against satisfiedBy.factKey."
        },
        "observedAt": {
          "type": "string",
          "format": "date-time",
          "description": "When the fact was observed. Used for lateness and ordering, never for lapse."
        },
        "source": {
          "type": "string",
          "description": "Who observed it. Attributed by the watcher to the authorized reporter, not supplied unchecked."
        },
        "subject": {
          "$ref": "#/$defs/Subject"
        }
      }
    },
    "Lapse": {
      "type": "object",
      "required": [
        "expectationId",
        "kind",
        "subject",
        "expects",
        "lapse",
        "projectedDueAt",
        "dueAt",
        "lapsedAt",
        "lineage"
      ],
      "description": "Carried in the lapse task's first Message metadata, keyed by the extension URI, under \"lapse\". The Message MUST reuse the origin task's contextId, reference the origin task in referenceTaskIds, and carry a messageId derived from (expectationId, lapse, dueAt).",
      "properties": {
        "expectationId": {
          "type": "string"
        },
        "kind": {
          "type": "string"
        },
        "subject": {
          "$ref": "#/$defs/Subject"
        },
        "expects": {
          "type": "string"
        },
        "lapse": {
          "type": "string",
          "description": "\"overdue\": due plus grace passed without a matching fact. \"skipped\": a later expectation on the subject was satisfied while this one was open. \"stalled\": the horizon passed with no fact of any kind on the subject. An agent receiving an unrecognised value treats it as \"overdue\".",
          "examples": [
            "overdue",
            "skipped",
            "stalled"
          ]
        },
        "projectedDueAt": {
          "type": "string",
          "format": "date-time",
          "description": "dueAt as raised. Never changes."
        },
        "dueAt": {
          "type": "string",
          "format": "date-time",
          "description": "Current due time at the moment of lapse."
        },
        "lapsedAt": {
          "type": "string",
          "format": "date-time",
          "description": "When the watcher determined the lapse, on the watcher's clock."
        },
        "lastFactAt": {
          "type": "string",
          "format": "date-time",
          "description": "Most recent fact of any kind on the subject. Absent means none since raising."
        },
        "satisfiedAfter": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "For \"skipped\": the later expectations whose satisfaction revealed the gap."
        },
        "lineage": {
          "type": "integer",
          "minimum": 0,
          "description": "How many superseded ancestors this expectation has."
        }
      }
    },
    "ExpectationRecord": {
      "type": "object",
      "required": [
        "expectation",
        "projectedDueAt",
        "dueAt",
        "state"
      ],
      "description": "The watcher's record of one expectation. Informative: describes what a conformant watcher can answer, not a wire object.",
      "properties": {
        "expectation": {
          "$ref": "#/$defs/Expectation"
        },
        "projectedDueAt": {
          "type": "string",
          "format": "date-time"
        },
        "dueAt": {
          "type": "string",
          "format": "date-time"
        },
        "state": {
          "type": "string",
          "examples": [
            "open",
            "satisfied",
            "lapsed",
            "withdrawn"
          ]
        },
        "satisfiedAt": {
          "type": "string",
          "format": "date-time"
        },
        "observedAt": {
          "type": "string",
          "format": "date-time"
        },
        "late": {
          "type": "boolean"
        },
        "satisfiedBy": {
          "$ref": "#/$defs/Fact"
        },
        "lapse": {
          "$ref": "#/$defs/Lapse"
        },
        "lapseTaskId": {
          "type": "string"
        },
        "withdrawnReason": {
          "type": "string"
        }
      }
    }
  }
}