Surfaced during the protocol review of #602 (to_wire_dict seam).
Bug
create_a2a_webhook_payload in src/adcp/webhooks.py defaults to pb.TaskState.TASK_STATE_UNSPECIFIED when the input status doesn't match any known AdCP status:
adcp_to_task_state: dict[str, int] = {
"completed": pb.TaskState.TASK_STATE_COMPLETED,
...
}
task_state_enum = adcp_to_task_state.get(status_value, pb.TaskState.TASK_STATE_UNSPECIFIED)
_normalize_a2a_task_state_to_v03 then rewrites TASK_STATE_UNSPECIFIED → unspecified on the wire. unspecified is not a valid A2A v0.3 TaskState — the spec enum is submitted | working | input-required | completed | canceled | failed | rejected | auth-required | unknown. A buyer receiver that validates against the A2A schema will reject the webhook.
Fix options
- Fail fast in the builder. Raise
ValueError on unknown status — callers shouldn't be silently producing payloads buyers will reject.
- Map to
unknown. A2A 0.3 has a defined unknown state; defaulting unknown AdCP statuses to it preserves delivery and signals the ambiguity to the buyer.
(1) is more correct (loud failure beats silent wire violation); (2) is more lenient. Recommend (1) since the AdCP→A2A status table is closed and any miss is a bug in the caller.
Additional spec-walking gap
_normalize_a2a_task_state_to_v03 doesn't traverse Task.artifacts[].parts[] for nested Message parts, nor TaskStatusUpdateEvent.metadata if a future caller stuffs role-bearing payloads there. Rare today (create_a2a_webhook_payload doesn't emit those), but worth tightening if hand-built Task payloads start flowing through to_wire_dict.
References
Surfaced during the protocol review of #602 (
to_wire_dictseam).Bug
create_a2a_webhook_payloadinsrc/adcp/webhooks.pydefaults topb.TaskState.TASK_STATE_UNSPECIFIEDwhen the inputstatusdoesn't match any known AdCP status:_normalize_a2a_task_state_to_v03then rewritesTASK_STATE_UNSPECIFIED→unspecifiedon the wire.unspecifiedis not a valid A2A v0.3TaskState— the spec enum issubmitted | working | input-required | completed | canceled | failed | rejected | auth-required | unknown. A buyer receiver that validates against the A2A schema will reject the webhook.Fix options
ValueErroron unknown status — callers shouldn't be silently producing payloads buyers will reject.unknown. A2A 0.3 has a definedunknownstate; defaulting unknown AdCP statuses to it preserves delivery and signals the ambiguity to the buyer.(1) is more correct (loud failure beats silent wire violation); (2) is more lenient. Recommend (1) since the AdCP→A2A status table is closed and any miss is a bug in the caller.
Additional spec-walking gap
_normalize_a2a_task_state_to_v03doesn't traverseTask.artifacts[].parts[]for nestedMessageparts, norTaskStatusUpdateEvent.metadataif a future caller stuffs role-bearing payloads there. Rare today (create_a2a_webhook_payloaddoesn't emit those), but worth tightening if hand-builtTaskpayloads start flowing throughto_wire_dict.References