name: a2a-protocol-patterns description: "Enforces correct usage of github.com/a2aproject/a2a-go/v2 for backend↔agent communication in the brainstorm pipeline."
Quick Reference
✅ BrainstormPayload via a2a.NewDataPart only; resolve AgentCard before client ✅ Executor: Submitted→Working→Artifact(DataPart)→Completed ❌ TextPart for state; hardcoded endpoints; 4xx retries Links: multi-agent-role-orchestration, llm-provider-abstraction
Purpose
This skill governs all A2A protocol interactions in backend/internal/platform/a2a/client.go and agent/internal/executor/executor.go. The project uses the a2a-go/v2 SDK in message-based mode — domain context is packed as a DataPart inside a2a.SendMessageRequest, never as a custom task schema (see docs/PLAN.md §8.3).
Rules
Always use
BrainstormPayloadas the single wire format. The struct defined inbackend/internal/platform/a2a/types.gois the sole contract between backend and agent. Fields:Role,SystemPrompt,LLMConfig,State. Never add fields outside this struct.Pack payload as
DataPartonly. WrapBrainstormPayloadwitha2a.NewDataPart()before placing it ina2a.NewMessage(). Do not useTextPartorFilePartfor structured state.Resolve
AgentCardbefore every client instantiation. Inbackend/internal/platform/a2a/client.go, callagentcard.DefaultResolver.Resolve(ctx, agent.Endpoint)thena2aclient.NewFromCard(ctx, card). Never hardcode an A2A endpoint URL directly inSendPayload.Retry only on transient errors. In
SendPayload, retry on 5xx and timeout; fail immediately on 4xx. Reference:docs/PLAN.md §4 (Platform: A2A Layer)validation criteria.Extract state only from
Artifact.PartsDataPart. InExtractStateFromResult, walkresult.Artifact.Parts, find theDataPart, and unmarshal intoCanonicalState. Do not parse raw text content.Emit the canonical 4-event sequence in
executor.go. EveryBrainstormExecutor.Executemust yield:NewSubmittedTask→NewStatusUpdateEvent(Working)→NewArtifactEvent(DataPart(updatedState))→NewStatusUpdateEvent(Completed).Cancelmust yieldTaskStateCanceled.Agent binary receives a fully assembled
SystemPrompt. The agent (agent/internal/executor/executor.go) must not assemble skill fragments or resolve credentials — the backend'sBuildSystemPromptand tiered resolver do this before dispatch. The agent is stateless with respect to skills and credential refs.
Anti-Patterns
Do NOT call
client.SendMessagewith aTextPartpayload — structured state cannot be reliably unmarshalled from text; always useDataPartas specified indocs/PLAN.md §8.3.Do NOT import
backendmodule fromagentmodule — the two are separate Go modules ingo.work. TheLLMProviderinterface is intentionally duplicated inagent/internal/llm/to preserve module isolation.Do NOT skip the
AgentCardresolution step — constructinga2aclientdirectly from a URL bypasses capability negotiation and breaks the A2A discovery contract.Do NOT add business logic in
executor.go— the executor's only job is to extractBrainstormPayload, callLLMProvider.Generate, and emit events. Role logic, merge logic, and convergence checks live in the backend modules.
Checklist
[ ] BrainstormPayload is the only wire struct; no fields added outside types.go
[ ] Payload wrapped with a2a.NewDataPart() before SendMessageRequest
[ ] AgentCard resolved via agentcard.DefaultResolver before client creation
[ ] Retry logic applied on 5xx/timeout only; 4xx causes immediate failure
[ ] State extracted from Artifact.Parts DataPart (not text content)
[ ] executor.go emits all 4 events in order: Submitted → Working → Artifact → Completed
[ ] Cancel emits TaskStateCanceled
[ ] agent/internal/llm/ defines its own LLMProvider copy (no cross-module import)
[ ] Agent binary receives assembled SystemPrompt; no skill or credential resolution inside executor