{
  "id": "2026-04-28-agent-wire-protocol-702b45175e",
  "scope": "redkey",
  "source_of_truth": "repo",
  "source_path": "docs/specs/2026-04-28-agent-wire-protocol.md",
  "source_kind": "markdown",
  "visibility": "internal",
  "renderer_id": "design_doc.dreamborn-forge.generated.v1",
  "design_system": "dreamborn-design-system:forge",
  "generated_at": "2026-05-09T13:00:55.688Z",
  "artifact_type": "design_doc",
  "schema_version": "design_doc.generated.v1",
  "title": "Agent Wire Protocol + Agent Card — Design Spec",
  "summary": "Agent Wire Protocol + Agent Card — Design Spec Date: 2026 04 28 Status: Approved — pending implementation Affects: All agent definitions, runner, dispatch scripts, inbox table, HCS message schemas Why The RedKey platform currently communicates between agents using prose: briefs are prose paragraphs, inbox messages are unstructured strings, and agent personas...",
  "format_source": "markdown",
  "sections": [
    {
      "title": "Agent Wire Protocol + Agent Card — Design Spec",
      "level": 1,
      "body": "**Date:** 2026-04-28\n**Status:** Approved — pending implementation\n**Affects:** All agent definitions, runner, dispatch scripts, inbox table, HCS message schemas\n\n---"
    },
    {
      "title": "Why",
      "level": 2,
      "body": "The RedKey platform currently communicates between agents using prose: briefs are prose paragraphs, inbox messages are unstructured strings, and agent personas are large prose blobs. This works, but it has three compounding problems:\n\n1. **No contract.** A downstream agent receiving a prose brief has no guaranteed structure to parse. If the upstream agent omits a field, there is no validation — the gap surfaces at runtime as ambiguity or a blocked task.\n\n2. **No versioning.** A prose brief cannot be versioned. When the format changes, there is no migration path — old briefs and new expectations silently diverge.\n\n3. **Inefficiency.** Prose personas waste tokens on filler. An LLM parsing \"You should always make sure to...\" is doing work that a structured constraint list eliminates. Large persona blobs also mix identity with procedure — a step-by-step execution protocol (Mindy's 900-word persona) doesn't belong in an identity document.\n\n**Agent Wire** is the typed coordination protocol for all A2A communication on the RedKey platform. Every brief, inbox message, output signal, and blocked signal is a Wire message — defined schema, versioned, machine-readable.\n\n**Agent Card** is the typed identity document for every agent. Structured fields for identity, capabilities, constraints, escalation rules, and output contracts. Short prose is reserved for voice and working style only.\n\n---"
    },
    {
      "title": "Principles",
      "level": 3,
      "body": "1. **Every coordination message is a Wire.** Briefs, inbox messages, complete signals, blocked signals — all typed.\n2. **Envelope wraps payload.** Every Wire has a standard envelope. The payload schema varies by type.\n3. **Versioned.** Every Wire carries a `wire` version field. Consumers can reject unknown versions gracefully.\n4. **Prose lives at the edges.** The `context` field in a Brief Wire may contain prose background. Every other field is typed.\n5. **Lean messages.** Role topic messages stay under 1024 bytes (pointer only). Brief Wire lives in Supabase. HCS carries the envelope pointer, not the full payload."
    },
    {
      "title": "Wire Envelope",
      "level": 3,
      "body": "All Agent Wire messages share this envelope:\n\n```json\n{\n  \"wire\": \"1.0\",\n  \"type\": \"message_type\",\n  \"sender\": \"agent_slug | 'system' | 'human'\",\n  \"ts\": \"ISO8601\",\n  \"payload\": {}\n}\n```\n\n| Field | Type | Description |\n|---|---|---|\n| `wire` | string | Protocol version. Current: `\"1.0\"` |\n| `type` | string | Message type (see below) |\n| `sender` | string | Agent slug, `\"system\"`, or `\"human\"` |\n| `ts` | ISO8601 | Creation timestamp |\n| `payload` | object | Type-specific payload |\n\n---"
    },
    {
      "title": "Message Type: `brief` — Task Brief",
      "level": 3,
      "body": "Replaces the current prose `agent_tasks.brief` field. Stored in Supabase. Role topic carries only the `task_id` pointer (unchanged).\n\n```json\n{\n  \"wire\": \"1.0\",\n  \"type\": \"brief\",\n  \"sender\": \"system\",\n  \"ts\": \"2026-04-28T00:00:00Z\",\n  \"payload\": {\n    \"goal\": \"One sentence: what must be true when this task is done.\",\n    \"context\": \"Background information. Prose allowed. Max 200 words.\",\n    \"constraints\": [\n      \"Specific constraint on this task instance\"\n    ],\n    \"acceptance_criteria\": [\n      \"Testable condition for completion\"\n    ],\n    \"output_contract\": {\n      \"type\": \"file | json | pr | message | report\",\n      \"schema_ref\": \"output_contract_slug | null\",\n      \"output_ref_required\": true,\n      \"min_length\": 0,\n      \"evaluate\": \"Semantic check description for Haiku eval, or null to skip\"\n    },\n    \"escalation_triggers\": [\n      \"Condition that should cause task.blocked\"\n    ],\n    \"env\": {\n      \"client_id\": \"bezeliq\",\n      \"task_id\": \"0.0.XXXXX\",\n      \"project_id\": \"0.0.XXXXX\",\n      \"repo_url\": null,\n      \"worktree_path\": null\n    }\n  }\n}\n```\n\n**Field rules:**\n\n| Field | Rule |\n|---|---|\n| `goal` | Required. One sentence. Declarative — \"X is built\" not \"Build X\". |\n| `context` | Optional. Prose allowed. 200 word cap. Background only — no instructions here. |\n| `constraints` | Task-level constraints only. Agent-level constraints live in the Agent Card. |\n| `acceptance_criteria` | Testable. No prose rationale. Each item is pass/fail. |\n| `output_contract.evaluate` | The string passed verbatim to Haiku eval. Null = mechanical check only. |\n| `escalation_triggers` | Conditions the agent should catch and use to trigger `task.blocked`. Prevents ambiguity at runtime. |\n\n---"
    },
    {
      "title": "Message Type: `inbox` — Agent-to-Agent Message",
      "level": 3,
      "body": "Replaces the current unstructured `inbox.payload` field. The outer inbox table row remains; `payload` becomes a Wire Message.\n\n```json\n{\n  \"wire\": \"1.0\",\n  \"type\": \"inbox\",\n  \"sender\": \"agent_slug | 'human'\",\n  \"ts\": \"ISO8601\",\n  \"payload\": {\n    \"to_agent\": \"agent_slug\",\n    \"priority\": 1,\n    \"message_type\": \"task.feedback | status.update | task.blocked | exec.gate | question | info\",\n    \"ref_task_id\": \"0.0.XXXXX | null\",\n    \"subject\": \"string\",\n    \"body\": \"string\",\n    \"action_required\": true\n  }\n}\n```\n\n**Priority levels:**\n\n| Value | Meaning |\n|---|---|\n| `1` | Requires immediate attention — blocked task, exec gate |\n| `2` | Requires response before next task — question, feedback |\n| `3` | Informational — status update, FYI |\n\n**Message types:**\n\n| Type | Direction | Use |\n|---|---|---|\n| `task.feedback` | Vikram → Quinn | Review findings, numbered issues to address |\n| `status.update` | Mindy → any | Context change, project state shift |\n| `task.blocked` | Runner → Mindy + Justin | Task stuck, needs intervention |\n| `exec.gate` | Runner → Justin | Approval required before workflow advances |\n| `question` | Any → any | Specific question before proceeding (not a hard blocker) |\n| `info` | Any → any | Informational, no action required |\n\n---"
    },
    {
      "title": "Message Type: `complete` — Task Completion",
      "level": 3,
      "body": "Posted by the runner to the task's own HCS topic after successful verification.\n\n```json\n{\n  \"wire\": \"1.0\",\n  \"type\": \"complete\",\n  \"sender\": \"runner\",\n  \"ts\": \"ISO8601\",\n  \"payload\": {\n    \"task_id\": \"0.0.XXXXX\",\n    \"agent\": \"agent_slug\",\n    \"output_ref\": \"supabase-storage-path\",\n    \"summary\": \"Max 150 words: what was built, what changed, output_ref location.\",\n    \"cost_usd\": 0.00,\n    \"duration_seconds\": 0,\n    \"verification\": {\n      \"mechanical\": \"pass | fail\",\n      \"semantic\": \"pass | fail | skipped\",\n      \"attempts\": 1\n    }\n  }\n}\n```\n\n---"
    },
    {
      "title": "Message Type: `blocked` — Task Blocked",
      "level": 3,
      "body": "Posted by the runner to the task's own HCS topic when verification fails or the agent posts `task.blocked`.\n\n```json\n{\n  \"wire\": \"1.0\",\n  \"type\": \"blocked\",\n  \"sender\": \"runner\",\n  \"ts\": \"ISO8601\",\n  \"payload\": {\n    \"task_id\": \"0.0.XXXXX\",\n    \"agent\": \"agent_slug\",\n    \"reason\": \"Specific description of the blocker.\",\n    \"blocker_type\": \"spec_gap | dependency | tool_failure | ambiguity | resource | verification_fail\",\n    \"suggested_resolution\": \"string | null\",\n    \"attempts\": 1\n  }\n}\n```\n\n`blocker_type` enables Iris to route and categorize without parsing prose:\n- `spec_gap` → Priya (BA)\n- `tool_failure` → Atlas / infra\n- `ambiguity` → Mindy (PM)\n- `verification_fail` → Mindy (supervisor)\n- `dependency` → Engine (dep-graph check)\n\n---"
    },
    {
      "title": "Message Type: `claim` — Work Claim",
      "level": 3,
      "body": "Already partially structured on HCS. Adding Wire envelope for protocol consistency.\n\n```json\n{\n  \"wire\": \"1.0\",\n  \"type\": \"claim\",\n  \"sender\": \"agent_slug\",\n  \"ts\": \"ISO8601\",\n  \"payload\": {\n    \"task_id\": \"0.0.XXXXX\",\n    \"agent\": \"agent_slug\",\n    \"role\": \"role_slug\"\n  }\n}\n```\n\n---"
    },
    {
      "title": "Agent Card",
      "level": 2,
      "body": "**Agent Card** replaces the prose `persona` field in `agent_definitions`. It is the typed identity contract for an agent. Structured fields carry identity, capabilities, constraints, and escalation routing. Prose fields are capped and reserved for behavioral nuance that cannot be structured."
    },
    {
      "title": "Why Constraints Beat Prose",
      "level": 3,
      "body": "LLMs hold explicit prohibitions (\"I never X\") more reliably than advisory prose (\"you should avoid X\"). Every constraint that currently lives buried in a persona paragraph becomes a first-class constraint entry. The model doesn't have to extract it from a paragraph — it's listed."
    },
    {
      "title": "What Moves Out of Persona → Skills",
      "level": 3,
      "body": "Current personas mix identity with procedure. Procedure belongs in skills, not the identity document.\n\n| Content type | Current location | New location |\n|---|---|---|\n| Who I am, constraints, voice | `persona` prose | Agent Card |\n| Step-by-step execution procedures | `persona` prose | Agent-specific skill markdown |\n| VPS environment setup | Repeated in every persona | `platform-env` shared skill |\n| Inbox read/write protocol | Repeated in every persona | `inbox-protocol` shared skill |\n| Done/blocked criteria | `persona` prose | `output_contract` in Brief Wire |\n\nThis dramatically shrinks Mindy's 900-word persona. Her Step 1–7 execution protocol becomes a `mindy-supervisor-protocol.md` skill. Her Agent Card is ~40 lines."
    },
    {
      "title": "Agent Card Schema",
      "level": 3,
      "body": "```json\n{\n  \"card_version\": \"1.0\",\n  \"identity\": {\n    \"slug\": \"string\",\n    \"name\": \"string\",\n    \"department\": \"string\",\n    \"reports_to\": \"agent_slug | 'human'\",\n    \"model\": \"string\"\n  },\n  \"scope\": \"One sentence: what I own and what I don't.\",\n  \"capabilities\": [\n    \"What I can do — one phrase per item\"\n  ],\n  \"constraints\": [\n    \"I never X — declarative, not advisory\"\n  ],\n  \"escalation_rules\": [\n    {\n      \"trigger\": \"Condition that fires this rule\",\n      \"route_to\": \"role_slug | agent_slug | 'exec'\",\n      \"via\": \"task.blocked | inbox | exec.gate\"\n    }\n  ],\n  \"output_types\": [\"output_contract_slug\"],\n  \"working_style\": \"2–3 sentences max. How I approach problems.\",\n  \"voice\": {\n    \"description\": \"1–2 sentences on communication style.\",\n    \"example\": \"One example of how I communicate.\"\n  },\n  \"values\": [\n    \"What I optimize for — one phrase per item\"\n  ]\n}\n```"
    },
    {
      "title": "Field Rules",
      "level": 3,
      "body": "| Field | Constraint | Rationale |\n|---|---|---|\n| `scope` | 1 sentence max | Forces precision. If you need 2 sentences, the scope is unclear. |\n| `constraints` | \"I never X\" format | Declarative prohibitions hold better than advisory prose. |\n| `escalation_rules` | Must reference real role slugs or agent slugs | Links identity to the routing layer — not abstract. |\n| `output_types` | Must reference real Wire output contract slugs | Links identity to the Wire protocol. |\n| `working_style` | 3 sentences max | Forces brevity. Move procedure detail to skills. |\n| `voice.example` | 1 example only | Show > tell for tone calibration. |\n\n---"
    },
    {
      "title": "Quinn — Reference Agent Card",
      "level": 3,
      "body": "```json\n{\n  \"card_version\": \"1.0\",\n  \"identity\": {\n    \"slug\": \"quinn\",\n    \"name\": \"Quinn\",\n    \"department\": \"dev\",\n    \"reports_to\": \"vikram\",\n    \"model\": \"claude-sonnet-4-6\"\n  },\n  \"scope\": \"I implement tasks in the developer work queue — nothing more, nothing less.\",\n  \"capabilities\": [\n    \"Full-stack code implementation (Python, JS/TS, SQL)\",\n    \"Git workflow — feature branches, commits, PRs\",\n    \"Supabase schema and query work\",\n    \"VPS deployment via scp + systemd\",\n    \"HCS message posting\"\n  ],\n  \"constraints\": [\n    \"I never commit directly to main or marketplace\",\n    \"I never extend scope beyond the task brief\",\n    \"I never fill in spec gaps — spec gaps go to Priya via task.blocked\",\n    \"I never self-review after completing a task\",\n    \"I never pick up the next task without runner triggering a new cycle\",\n    \"I never use sudo on the VPS\"\n  ],\n  \"escalation_rules\": [\n    {\n      \"trigger\": \"Brief is missing required information I cannot infer from the codebase\",\n      \"route_to\": \"roles.ba\",\n      \"via\": \"task.blocked\"\n    },\n    {\n      \"trigger\": \"Brief is ambiguous but I could proceed with a potentially wrong assumption\",\n      \"route_to\": \"mindy\",\n      \"via\": \"inbox\"\n    }\n  ],\n  \"output_types\": [\"code_commit\", \"output_ref_file\"],\n  \"working_style\": \"I read the brief, implement exactly what it specifies, verify my output, and stop. I do not improvise. I do not extend scope.\",\n  \"voice\": {\n    \"description\": \"Terse and technical. Facts, not feelings.\",\n    \"example\": \"Implemented auth middleware. Tests pass. output_ref written to Storage. Branch: feature/auth-v2.\"\n  },\n  \"values\": [\n    \"Exactness over improvisation\",\n    \"Scope discipline\",\n    \"Clean commits\",\n    \"No broken builds\"\n  ]\n}\n```\n\n---"
    },
    {
      "title": "Runner Integration",
      "level": 2,
      "body": "The runner assembles the agent system prompt from Agent Card fields in this order:\n\n```\n1.  Identity block:      \"You are {name}. {scope}\"\n2.  Capabilities:        \"## Capabilities\\n- ...\"  \n3.  Constraints:         \"## Constraints\\n- ...\"\n4.  Escalation rules:    \"## When to Escalate\\n- ...\"\n5.  Working style:       {working_style}\n6.  Voice calibration:   {voice.description}\\nExample: {voice.example}\n7.  [Injected skills markdown — agent-specific + shared]\n8.  [Injected memory context — pgvector nearest-neighbor]\n9.  [Injected inbox messages — unread, ordered by priority]\n10. [Wire Brief — goal, context, constraints, acceptance_criteria, output_contract]\n```\n\n**Shared skills injected for all agents:**\n- `platform-env` — VPS environment, ccos user, /opt/redk3y, no sudo\n- `inbox-protocol` — how to read and send inbox messages\n\n**Brief injection:** The runner fetches `agent_tasks.brief` (a Wire Brief JSON blob), parses the envelope, validates the version, then injects the payload fields as a structured section. The agent receives a clean, formatted brief — not raw JSON.\n\n**Fallback:** If `agent_card` column is null in `agent_definitions`, runner falls back to the `persona` field. This allows incremental migration without downtime.\n\n---"
    },
    {
      "title": "Naming Glossary",
      "level": 2,
      "body": "| Term | Definition |\n|---|---|\n| **Agent Wire** | The typed coordination protocol for all A2A communication on RedKey |\n| **Wire** | A single message conforming to the Agent Wire Protocol |\n| **Wire envelope** | The standard wrapper: `wire`, `type`, `sender`, `ts`, `payload` |\n| **Brief Wire** | A task brief conforming to the `brief` Wire message type |\n| **Inbox Wire** | An agent-to-agent message conforming to the `inbox` Wire message type |\n| **Agent Card** | The typed identity document for an agent — replaces the prose `persona` field |\n| **Output contract** | Typed specification of what an agent produces — referenced in Brief Wire and Agent Card |\n| **card_version** | Schema version of the Agent Card format. Current: `\"1.0\"` |\n\n---"
    },
    {
      "title": "Phase 1: Schema definitions",
      "level": 3,
      "body": "Define Wire schemas as JSON files in `definitions/wire/`. Define Agent Card schema. No code changes. No risk."
    }
  ],
  "html_path": "artifacts/2026-04-28-agent-wire-protocol-702b45175e.html",
  "json_path": "artifacts/2026-04-28-agent-wire-protocol-702b45175e.json"
}