mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
create_a2a_notification gated A2A hierarchy only when both ends resolved (`if from_agent and target_agent:`), so an unattributed (from_agent falsy) or unresolvable-target request slipped past the hierarchy matrix and dispatched with from_agent='unknown' / to_agent='' — and a denial came back as a bare ValueError indistinguishable from the missing-task_id ValueError. Require both ends present, then validate via the shared typed validate_a2a_access path (A2AAccessDeniedError + route_hint) so the legacy notification surface enforces the same who-may-talk-to-whom invariant as the conversation path. send() accepts skill= and the gateway callers (qa/doc/pr_gate) pass it expecting the receiver to learn which capability the message is about, but send_chat_message never read it from options — silently dropped. Persist a nullable skill column (migration 054) on a2a_messages, wire it through send_chat_message + _msg_to_model + the A2AChatMessage model, and fix the send() docstring (it claimed 'recorded in message metadata'). TDD: 4 red→green (skill recorded on message + surfaces in inbox; permission denied raises typed A2AAccessDeniedError with route_hint; self-A2A raises typed; missing from_agent raises instead of silent dispatch). 103 a2a integration tests green; ruff/mypy clean; migration 054 verified upgrade/downgrade on throwaway PG.