K1-A — Phase 1 formal freeze (executable)¶
Status: Frozen, implemented on main, and now publicly released in v3.4.0 for K1-A Phase 1 (adapter-first). Parent: PLAN-K1-A2A-HANDOFF-DELEGATION-ROUTE-EVIDENCE-2026q2.md. Repo snapshot: Current assay-adapter-a2a plus ADR026 A2A fixtures emit typed task, message, and canonical assay.adapter.a2a.task.requested events, and now also emit the first bounded top-level handoff object on main. task.updated remains mapped but is still not a positive source in v1, and the ADR026 fixture set still does not include a dedicated task.updated packet. This freeze defines one bounded top-level handoff contract from existing typed fields only, with explicit negatives and no reuse of payload.discovery.
Contract honesty (product / review)¶
K1-A v1 is typed, adapter-emitted visibility evidence only. It is grounded in already-shipped A2A task/message fields, but it is not a full delegation graph, target-resolution surface, or handoff-correctness signal. Do not market this as “A2A delegation verified” or “route resolved” in v1. The only honest contract here is that a bounded handoff / delegation-route surface is visible in current typed A2A task-request packets, with limited task/message reference visibility on that same surface.
1. Definitive decision: typed-payload-only v1¶
Decision: K1-A v1 is typed-payload only.
Positive promotion for the new handoff seam is allowed only when all of the following hold:
- the canonical emitted event type is
assay.adapter.a2a.task.requested - the typed task payload contains
task.kind == "delegation"
Not allowed in v1:
attributespayload.discoveryunmapped_fields_count- unmapped top-level keys
- heuristics on
agent.role,artifact, or generic message fallbacks
Deferred in v1:
- positive promotion from
assay.adapter.a2a.task.updateduntil fixture-backed freeze evidence exists - any target-agent identity, route-hop list, transfer success, or chain completeness
2. Payload placement and default shape (hard contract)¶
Placement: handoff is a top-level sibling on the emitted canonical adapter payload (same object as agent, task, message, attributes, discovery, and unmapped_fields_count).
Presence: handoff is always present on every emitted event.
Default values (when no rule sets a non-default):
| Field | Default | JSON type |
|---|---|---|
visible | false | bool |
source_kind | "unknown" | string (enum) |
task_ref_visible | false | bool |
message_ref_visible | false | bool |
Enum values for source_kind: typed_payload | unknown
v1 reachability: In this freeze, typed_payload is the only positive source kind. unknown is the default when the positive typed rule does not match. There is no attributes, unmapped, or fallback source kind in v1.
2b. Bounded meaning — may imply / must not imply¶
| Field | May imply (visibility / presence only) | Must not imply |
|---|---|---|
visible | A bounded handoff / delegation-route surface was observed on a typed A2A task-request packet (task.requested + task.kind == "delegation") | Delegation succeeded, was allowed, was correct, reached the right target, or completed a full chain |
source_kind | Which class of source set the seam (typed_payload vs unknown) | Confidence, correctness, trust, or protocol-native completeness beyond the frozen rule |
task_ref_visible | A typed task.id from the upstream packet was visible on a packet that already satisfied handoff.visible | Task identity verified, synthetic fallback accepted as real, or route completeness |
message_ref_visible | A typed message.id from the upstream packet was visible on a packet that already satisfied handoff.visible | Message legitimacy, target identity, or delegation success |
One-line semantics guardrail: Every handoff.* boolean is a bounded observed visibility flag only. None of these fields mean the handoff was valid, authorized, complete, trusted, or successful.
3. Source rule and decision table¶
Positive v1 source rule (hard requirement):
| Situation | Result |
|---|---|
Canonical event type is assay.adapter.a2a.task.requested and typed task.kind == "delegation" | handoff.visible = true, handoff.source_kind = "typed_payload" |
Same as above plus upstream typed task.id present | handoff.task_ref_visible = true |
Same as above plus upstream typed message.id present | handoff.message_ref_visible = true |
Lenient mode substituted synthetic task.id = "unknown-task" | handoff.task_ref_visible = false |
| Any other packet shape or event class | all defaults; handoff.source_kind = "unknown" |
Explicit v1 non-promotion rule: assay.adapter.a2a.task.updated is not a positive source in this freeze, even though the adapter already maps it. A later freeze may widen the source rule only after a fixture-backed update.
4. Filled freeze table (per field)¶
4.1 handoff.visible¶
| Exact source paths (v1) | Canonical event type assay.adapter.a2a.task.requested plus typed payload path task.kind with string value "delegation". |
| Minimal shape | The packet maps to the canonical task-request event, task is an object, and task.kind is a JSON string equal to delegation. |
May use attributes? | No. |
May use payload.discovery? | No. |
| Multiple signals required? | Yes. Event class and task.kind == "delegation" are both required. |
| Explicit negatives (must not promote) | (1) artifact.shared even if task.id exists; (2) generic assay.adapter.a2a.message fallback even if message.id exists; (3) agent.capabilities; (4) missing task.kind; (5) non-delegation task.kind; (6) agent.role alone; (7) attributes blobs; (8) payload.discovery; (9) unmapped_fields_count alone; (10) task.updated in v1. |
4.2 handoff.source_kind¶
| Exact source rule (v1) | typed_payload when §4.1 matches; otherwise unknown. |
| Enum surface | typed_payload |
| Explicit negatives | No other source kind may appear in v1. In particular, attributes, unmapped, and fallback generic-message paths are not valid handoff source kinds. |
4.3 handoff.task_ref_visible¶
| Exact source paths (v1) | Upstream typed task.id mapped into canonical payload.task.id, and the packet already satisfies §4.1 for handoff.visible. |
| Minimal shape | Upstream task.id is a JSON string and was present before any lenient substitution. |
| Synthetic fallback rule | The current lenient adapter may substitute task.id = "unknown-task" for task events. That synthetic value must not set handoff.task_ref_visible = true. |
| Explicit negatives | (1) synthetic unknown-task; (2) artifact.shared task IDs; (3) any packet where handoff.visible = false. |
4.4 handoff.message_ref_visible¶
| Exact source paths (v1) | Upstream typed message.id mapped into canonical payload.message.id, and the packet already satisfies §4.1 for handoff.visible. |
| Minimal shape | Upstream message.id is a JSON string. |
| Explicit negatives | (1) generic assay.adapter.a2a.message fallback; (2) packets where handoff.visible = false; (3) synthetic unknown-message on generic fallback packets. |
5. Strict vs lenient¶
- Strict mode: current adapter behavior already rejects task packets without
task.id; that remains true. No emittedhandoffobject exists because the whole packet is rejected. - Lenient mode:
handoff.visiblemay still betruewhen the typed route surface exists (task.requested+task.kind == "delegation"), even iftask.idis missing. In that case: handoff.source_kind = "typed_payload"handoff.task_ref_visible = falsehandoff.message_ref_visiblefollows upstream typedmessage.idonly- Invalid event-type fallback: packets that fall back to
assay.adapter.a2a.messagekeep the fullhandoffdefault object, even ifmessage.idor syntheticunknown-messageis present.
6. Complete emitted JSON examples (illustrative)¶
Build note: adapter_version uses 3.4.0 as an illustration; real emission uses CARGO_PKG_VERSION. Reviewers should treat presence and shape of top-level keys as normative here, not the patch digit.
6.1 Typed positive (task.requested + delegation)¶
Fixture source: a2a_happy_task_requested.json
{
"adapter_id": "assay-adapter-a2a",
"adapter_version": "3.4.0",
"protocol": "a2a",
"protocol_name": "a2a",
"protocol_version": "0.2",
"upstream_event_type": "task.requested",
"agent": {
"id": "agent://coordinator",
"role": "orchestrator"
},
"task": {
"id": "task-123",
"status": "requested",
"kind": "delegation"
},
"message": {
"id": "msg-1",
"role": "assistant"
},
"attributes": {
"channel": "web",
"priority": "urgent"
},
"discovery": {
"agent_card_visible": false,
"agent_card_source_kind": "unknown",
"extended_card_access_visible": false,
"signature_material_visible": false
},
"handoff": {
"visible": true,
"source_kind": "typed_payload",
"task_ref_visible": true,
"message_ref_visible": true
},
"unmapped_fields_count": 0
}
6.2 Lenient partial visibility (task.id missing)¶
Fixture source: a2a_negative_missing_task_id.json in lenient mode
{
"adapter_id": "assay-adapter-a2a",
"adapter_version": "3.4.0",
"protocol": "a2a",
"protocol_name": "a2a",
"protocol_version": "0.2",
"upstream_event_type": "task.requested",
"agent": {
"id": "agent://coordinator"
},
"task": {
"id": "unknown-task",
"status": "requested",
"kind": "delegation"
},
"message": {
"id": "msg-2"
},
"discovery": {
"agent_card_visible": false,
"agent_card_source_kind": "unknown",
"extended_card_access_visible": false,
"signature_material_visible": false
},
"handoff": {
"visible": true,
"source_kind": "typed_payload",
"task_ref_visible": false,
"message_ref_visible": true
},
"unmapped_fields_count": 1
}
6.3 Fully default (artifact.shared)¶
Fixture source: a2a_happy_artifact_shared.json
{
"adapter_id": "assay-adapter-a2a",
"adapter_version": "3.4.0",
"protocol": "a2a",
"protocol_name": "a2a",
"protocol_version": "0.3.1",
"upstream_event_type": "artifact.shared",
"agent": {
"id": "agent://worker"
},
"task": {
"id": "task-123"
},
"artifact": {
"id": "artifact-7",
"name": "plan.md",
"media_type": "text/markdown"
},
"discovery": {
"agent_card_visible": false,
"agent_card_source_kind": "unknown",
"extended_card_access_visible": false,
"signature_material_visible": false
},
"handoff": {
"visible": false,
"source_kind": "unknown",
"task_ref_visible": false,
"message_ref_visible": false
},
"unmapped_fields_count": 0
}
7. Negative test matrix (implemented minimum on main)¶
| # | Case | Expected handoff |
|---|---|---|
| N1 | artifact.shared with task.id present | all defaults |
| N2 | lenient invalid event_type falling back to assay.adapter.a2a.message | all defaults, even if message.id or unknown-message exists |
| N3 | strict task.requested packet missing task.id | measurement error; no emitted packet |
| N4 | lenient task.requested packet with task.kind = delegation but missing task.id | visible = true, source_kind = "typed_payload", task_ref_visible = false |
| N5 | task.requested packet with missing or non-delegation task.kind | all defaults |
| N6 | agent.role, attributes, payload.discovery, or unmapped_fields_count alone | all defaults |
| N7 | task.updated packet in v1 | all defaults until a later freeze widens the positive rule |
Current coverage note: the first K1-A adapter slice on main now includes explicit adapter-level tests for N1, N2, N4, N5, and N7, plus helper-level direct coverage for the source-rule default paths. The ADR026 fixture set itself still does not include a dedicated task.updated packet, so future fixture-parity work remains optional rather than a blocker for the shipped v1 seam.
8. assay-evidence scope¶
No change to assay-evidence in K1-A v1. This freeze is about canonical adapter-emitted evidence shape only. Any later Trust Basis, Trust Card, or pack follow-up must be justified separately after the handoff seam is real.
9. Link to implementation¶
- Implement
handoffin the A2A adapter as a separate top-level sibling, not as an extension ofpayload.discovery. - Source logic must be grounded in current typed fields from:
mapping.rsconvert.rspayload.rs- Tests must cover:
- typed positive from
a2a_happy_task_requested.json - strict failure on missing
task.id - lenient partial visibility on missing
task.id - no promotion from
artifact.shared - no promotion from generic message fallback
- explicit non-promotion from
task.updatedin v1 - Canonical stability should be asserted with repeated conversion plus golden digests over the emitted
handoffsub-object.
10. Reviewer checklist (contract closure)¶
| Question | Expected answer |
|---|---|
Is handoff a single bounded top-level surface? | Yes |
Does v1 promote only from typed task.requested + task.kind == "delegation"? | Yes |
Does v1 avoid attributes, payload.discovery, unmapped keys, and heuristics? | Yes |
Is synthetic unknown-task blocked from counting as task_ref_visible? | Yes |
Is task.updated explicitly non-promoting in v1? | Yes |
| Does the doc stay at visibility language, not correctness or trust language? | Yes |