feat: add typed inter-agent communication schema

Replace freetext signals (RFR, LGTM, VERDICT: PASS) with YAML
frontmatter envelopes routed by a `signal` field. New message-schema
skill defines 12 message types covering worker submissions, review/audit
verdicts, triage/plan results, research results, and orchestrator
commands. All agents load the skill; qa-checklist enforces compliance;
orchestrate routes by envelope signal.
This commit is contained in:
Bryan Ramos 2026-04-02 07:38:02 -04:00
parent d2fdcbc731
commit 341f500396
14 changed files with 476 additions and 39 deletions

View file

@ -1 +1 @@
- [TODO: inter-agent JSON schema](todo_inter_agent_schema.md) — formal typed schema for all inter-agent messages to replace freetext signals - [Inter-agent communication schema](todo_inter_agent_schema.md) — YAML frontmatter envelopes implemented via message-schema skill

View file

@ -1,11 +1,13 @@
--- ---
name: TODO — formal JSON schema for inter-agent communication name: Inter-agent communication schema — implemented
description: Planned work to replace informal signal/text conventions with a typed JSON schema for all inter-agent messages description: Typed YAML frontmatter envelopes for all inter-agent messages, replacing freetext signals. Defined in skills/message-schema/SKILL.md.
type: project type: project
--- ---
Define a formal JSON schema for all inter-agent communication in the agent team. Formal inter-agent communication schema implemented via the `message-schema` skill.
**Why:** Current protocol relies on freetext signals (RFR, LGTM, REVISE, VERDICT: PASS, etc.) and unstructured prose output. A typed schema would make messages machine-readable, easier to validate, and more reliable for orchestrator parsing — especially as parallelism increases and the orchestrator is managing multiple concurrent agent outputs. **What:** All agent output and orchestrator dispatch uses YAML frontmatter envelopes with a `signal` field as the primary routing key. 12 message types cover worker submissions, review/audit verdicts, triage/plan results, research results, and orchestrator commands.
**How to apply:** Design the schema before any further changes to the orchestrate skill or agent protocols. All agent output formats (reviewer verdict, auditor verdict, worker RFR, architect triage response, etc.) should conform to it. Consider whether the schema lives as a skill, a standalone JSON Schema file, or embedded in agent frontmatter. **Why:** Freetext signals (RFR, LGTM, VERDICT: PASS) were ambiguous and required prose parsing. Typed envelopes give the orchestrator a consistent, unambiguous routing key.
**How to apply:** Every agent loads the `message-schema` skill. The qa-checklist includes schema compliance checks. The orchestrate skill routes by reading the `signal` field from agent output envelopes.

View file

@ -36,6 +36,7 @@ The script symlinks `agents/`, `skills/`, `rules/`, `CLAUDE.md`, and `settings.j
| `conventions` | Core coding conventions and quality priorities shared by all agents | | `conventions` | Core coding conventions and quality priorities shared by all agents |
| `worker-protocol` | Output format, feedback handling, and operational procedures for worker agents | | `worker-protocol` | Output format, feedback handling, and operational procedures for worker agents |
| `qa-checklist` | Self-validation checklist workers run before returning results | | `qa-checklist` | Self-validation checklist workers run before returning results |
| `message-schema` | Typed YAML frontmatter envelopes for all inter-agent communication |
| `project` | Instructs agents to check for and ingest a project-specific skill file before starting work | | `project` | Instructs agents to check for and ingest a project-specific skill file before starting work |
## Rules ## Rules

View file

@ -9,6 +9,7 @@ disallowedTools: Edit
maxTurns: 35 maxTurns: 35
skills: skills:
- conventions - conventions
- message-schema
- project - project
--- ---
@ -47,7 +48,20 @@ Triggered when the orchestrator sends you a raw request without a `## Research C
4. Analyze the codebase to understand what exists and what needs to change 4. Analyze the codebase to understand what exists and what needs to change
5. Identify research questions — things you need verified before you can plan confidently 5. Identify research questions — things you need verified before you can plan confidently
**Return to orchestrator (do not write the plan yet):** **Return to orchestrator** with a `triage_result` envelope (do not write the plan yet):
```yaml
---
type: triage_result
signal: triage_complete
tier: 0 | 1 | 2 | 3
research_needed: true | false
research_count: 3
---
```
Then the markdown body:
``` ```
## Triage ## Triage
@ -65,7 +79,7 @@ For each question:
- **Where to look:** [docs URL, package, API reference] - **Where to look:** [docs URL, package, API reference]
``` ```
If there are no research questions, say so. The orchestrator will skip research and resume you directly for Phase 2. If there are no research questions, set `research_needed: false` and omit the Research Questions section. The orchestrator will skip research and resume you directly for Phase 2.
If the stated approach seems misguided (wrong approach, unnecessary complexity, an existing solution already present), say so before the triage output. Propose the better path. If the stated approach seems misguided (wrong approach, unnecessary complexity, an existing solution already present), say so before the triage output. Propose the better path.
@ -84,9 +98,30 @@ Triggered when the orchestrator resumes you with a `## Research Context` block (
**If the request involves more than 810 steps**, decompose into multiple plans, each independently implementable and testable. State: "This is plan 1 of N." **If the request involves more than 810 steps**, decompose into multiple plans, each independently implementable and testable. State: "This is plan 1 of N."
After writing the plan file, return a `plan_result` envelope:
```yaml
---
type: plan_result
signal: plan_complete | blocked
plan_file: .claude/plans/kebab-case-title.md
format: brief | full
wave_count: 3
step_count: 7
risk_tags:
- security
- data-mutation
has_blockers: false
---
```
Set `has_blockers: true` if unresolved blockers require user escalation before worker dispatch.
Body: One-paragraph summary of what the plan covers.
--- ---
## Output formats ## Plan formats
### Format selection ### Format selection

View file

@ -8,6 +8,7 @@ disallowedTools: Write, Edit
maxTurns: 25 maxTurns: 25
skills: skills:
- conventions - conventions
- message-schema
- project - project
--- ---
@ -61,6 +62,27 @@ For every security finding: explain the attack vector, reference the relevant CW
## Output format ## Output format
Wrap your output in an `audit_verdict` envelope per the message-schema skill:
```yaml
---
type: audit_verdict
signal: pass | pass_with_notes | fail
security_findings:
critical: 0
high: 0
medium: 0
low: 0
build_status: pass | fail | skipped
test_status: pass | fail | partial | skipped
typecheck_status: pass | fail | skipped
---
```
**Hard rule:** `security_findings.critical > 0` or `build_status: fail` or `test_status: fail` requires `signal: fail`.
Then the markdown body:
### Security ### Security
**CRITICAL** — exploitable vulnerability, fix immediately **CRITICAL** — exploitable vulnerability, fix immediately
@ -79,8 +101,6 @@ For every security finding: explain the attack vector, reference the relevant CW
**Passed:** [what succeeded] **Passed:** [what succeeded]
**Failed:** [what failed, with output] **Failed:** [what failed, with output]
**VERDICT: PASS** / **PARTIAL** / **FAIL**
--- ---
If the project has no tests, cannot be built, or the test runner is missing, say so and emit `VERDICT: PARTIAL` with an explanation of what could and could not be verified. Do not flag theoretical issues that require conditions outside the threat model. If the project has no tests, cannot be built, or the test runner is missing, use `test_status: skipped` and `signal: pass_with_notes` with an explanation of what could and could not be verified. Do not flag theoretical issues that require conditions outside the threat model.

View file

@ -8,6 +8,7 @@ maxTurns: 20
skills: skills:
- conventions - conventions
- worker-protocol - worker-protocol
- message-schema
- project - project
--- ---

View file

@ -10,6 +10,7 @@ maxTurns: 20
skills: skills:
- conventions - conventions
- worker-protocol - worker-protocol
- message-schema
- project - project
--- ---

View file

@ -6,6 +6,8 @@ permissionMode: plan
tools: Read, Glob, Grep, Bash, WebFetch, WebSearch tools: Read, Glob, Grep, Bash, WebFetch, WebSearch
disallowedTools: Write, Edit disallowedTools: Write, Edit
maxTurns: 10 maxTurns: 10
skills:
- message-schema
--- ---
You are a researcher. You answer one specific research question with verified facts. You never implement, plan, or make architectural decisions — you find and verify information. You are a researcher. You answer one specific research question with verified facts. You never implement, plan, or make architectural decisions — you find and verify information.
@ -29,6 +31,20 @@ You are a researcher. You answer one specific research question with verified fa
## Output format ## Output format
Wrap your output in a `research_result` envelope per the message-schema skill:
```yaml
---
type: research_result
signal: research_complete
topic: "brief topic identifier"
verified: true | false
has_gotchas: true | false
---
```
Then the markdown body:
``` ```
## Research: [topic] ## Research: [topic]

View file

@ -8,6 +8,7 @@ disallowedTools: Write, Edit
maxTurns: 20 maxTurns: 20
skills: skills:
- conventions - conventions
- message-schema
- project - project
--- ---
@ -39,6 +40,25 @@ On **resubmissions**, the orchestrator will include a delta of what changed. Foc
## Output format ## Output format
Wrap your output in a `review_verdict` envelope per the message-schema skill:
```yaml
---
type: review_verdict
signal: pass | pass_with_notes | fail
critical_count: 0
moderate_count: 0
minor_count: 0
ac_coverage:
AC1: pass | fail
AC2: pass | fail
---
```
**Hard rule:** `critical_count > 0` requires `signal: fail`.
Then the markdown body:
### Review: [scope] ### Review: [scope]
**CRITICAL** — must fix before shipping **CRITICAL** — must fix before shipping
@ -55,10 +75,8 @@ On **resubmissions**, the orchestrator will include a delta of what changed. Foc
- AC2: PASS / FAIL — [one line] - AC2: PASS / FAIL — [one line]
- ... - ...
**VERDICT: PASS** / **PASS WITH NOTES** / **FAIL**
One line summary. One line summary.
--- ---
Keep it tight. One line per issue unless the explanation genuinely needs more. Reference file:line for every finding. If nothing is wrong, return `VERDICT: PASS` + 1-line summary. Keep it tight. One line per issue unless the explanation genuinely needs more. Reference file:line for every finding. If nothing is wrong, return `signal: pass` + 1-line summary.

View file

@ -9,6 +9,7 @@ maxTurns: 25
skills: skills:
- conventions - conventions
- worker-protocol - worker-protocol
- message-schema
- qa-checklist - qa-checklist
- project - project
--- ---

View file

@ -0,0 +1,309 @@
---
name: message-schema
description: Typed envelope schema for all inter-agent communication. Defines message types, required fields, and signal routing contracts.
when_to_use: Automatically loaded by all agents and the orchestrator via skills frontmatter. Reference when producing or consuming agent output.
---
Every agent output and orchestrator dispatch uses a **YAML frontmatter envelope** followed by a **markdown body**. The envelope contains routing metadata; the body contains human-readable content.
```
---
type: <message_type>
signal: <routing_signal>
# ... type-specific fields
---
[markdown body]
```
The `signal` field is the orchestrator's primary routing key. It determines the next action without parsing prose.
---
## Signals
### Agent → Orchestrator
| Signal | Meaning | Emitted by |
|--------|---------|------------|
| `rfr` | Work complete, ready for review | worker, debugger, documenter |
| `pass` | Review/audit passed cleanly | reviewer, auditor |
| `pass_with_notes` | Passed with non-blocking findings | reviewer, auditor |
| `fail` | Review/audit failed, needs rework | reviewer, auditor |
| `triage_complete` | Triage done, research questions identified (or none) | architect |
| `plan_complete` | Plan written to file | architect |
| `research_complete` | Research question answered | researcher |
| `blocked` | Cannot proceed, needs orchestrator intervention | any agent |
| `escalate` | Beyond agent scope, needs user decision | any agent |
### Orchestrator → Agent
| Signal | Meaning | Sent to |
|--------|---------|---------|
| `execute` | Perform this task | worker, debugger, documenter, architect |
| `revise` | Fix listed issues and resubmit | worker, debugger, documenter |
| `lgtm` | Approved, commit now | worker, debugger, documenter |
| `research` | Answer this research question | researcher |
| `plan` | Produce architecture and wave decomposition | architect |
---
## Agent → Orchestrator Message Types
### worker_submission
Emitted by: worker, debugger, documenter
```yaml
---
type: worker_submission
signal: rfr | blocked | escalate
files_changed:
- path/to/file1
- path/to/file2
ac_coverage:
AC1: pass | fail | partial | na
AC2: pass | fail | partial | na
qa_check: pass | fail
---
```
Required: `type`, `signal`, `files_changed`, `qa_check`
Optional: `ac_coverage` (omit when no AC provided in assignment)
Body: `## Result` section with implementation details, then `## Self-Assessment` with per-criterion notes and known limitations.
### review_verdict
Emitted by: reviewer
```yaml
---
type: review_verdict
signal: pass | pass_with_notes | fail
critical_count: 0
moderate_count: 2
minor_count: 1
ac_coverage:
AC1: pass | fail
AC2: pass | fail
---
```
Required: `type`, `signal`, `critical_count`, `ac_coverage`
Optional: `moderate_count`, `minor_count`
**Hard rule:** `critical_count > 0` requires `signal: fail`.
Body: Findings by severity (CRITICAL / MODERATE / MINOR), then AC Coverage details, then one-line summary.
### audit_verdict
Emitted by: auditor
```yaml
---
type: audit_verdict
signal: pass | pass_with_notes | fail
security_findings:
critical: 0
high: 0
medium: 0
low: 0
build_status: pass | fail | skipped
test_status: pass | fail | partial | skipped
typecheck_status: pass | fail | skipped
---
```
Required: `type`, `signal`, `security_findings`, `build_status`, `test_status`
Optional: `typecheck_status`
**Hard rule:** `security_findings.critical > 0` or `build_status: fail` or `test_status: fail` requires `signal: fail`.
Body: Security findings by severity (or CLEAN), then Runtime section with tested/passed/failed.
### triage_result
Emitted by: architect (Phase 1)
```yaml
---
type: triage_result
signal: triage_complete
tier: 0 | 1 | 2 | 3
research_needed: true | false
research_count: 3
---
```
Required: `type`, `signal`, `tier`, `research_needed`
Optional: `research_count` (present when `research_needed: true`)
**Routing:** `research_needed: false` means the orchestrator skips research and resumes architect directly for Phase 2.
Body: Triage section (Tier, Problem, Constraints, Success criteria, Out of scope), then Research Questions if any.
### plan_result
Emitted by: architect (Phase 2)
```yaml
---
type: plan_result
signal: plan_complete | blocked
plan_file: .claude/plans/kebab-case-title.md
format: brief | full
wave_count: 3
step_count: 7
risk_tags:
- security
- data-mutation
has_blockers: false
---
```
Required: `type`, `signal`, `plan_file`, `wave_count`, `risk_tags`
Optional: `format`, `step_count`, `has_blockers`
**Routing:** `has_blockers: true` triggers user escalation before worker dispatch.
Body: One-paragraph summary of what the plan covers.
### research_result
Emitted by: researcher
```yaml
---
type: research_result
signal: research_complete
topic: "brief topic identifier"
verified: true | false
has_gotchas: true | false
---
```
Required: `type`, `signal`, `topic`, `verified`
Optional: `has_gotchas`
**Routing:** `verified: false` flags unverified assumptions to the architect before planning.
Body: Answer, Verified Facts with sources, Version Constraints, Gotchas, Unverified claims.
---
## Orchestrator → Agent Message Types
### task_assignment
Sent to: worker, debugger, documenter
```yaml
---
type: task_assignment
signal: execute
task: "short task title"
plan_file: .claude/plans/kebab-case-title.md
wave: 1
step: 2
---
```
Required: `type`, `signal`
Optional: `task`, `plan_file`, `wave`, `step` (Tier 0 tasks may lack plan context)
Body: Task spec, Acceptance Criteria, Context (interface contracts, constraints, out-of-scope), Files to modify/read.
### revision_request
Sent to: worker, debugger, documenter
```yaml
---
type: revision_request
signal: revise
iteration: 2
max_iterations: 5
fix_severity: critical | critical+moderate | all
---
```
Required: `type`, `signal`, `iteration`
Optional: `max_iterations`, `fix_severity`
`fix_severity` maps to iteration: 1-3 = `all`, 4-5 = `critical`.
Body: Issues to fix (from reviewer and/or auditor), grouped by source, with guidance.
### approval
Sent to: worker, debugger, documenter
```yaml
---
type: approval
signal: lgtm
---
```
Required: `type`, `signal`. Pure control signal — commit using conventional commit format.
### triage_request
Sent to: architect (Phase 1)
```yaml
---
type: triage_request
signal: execute
---
```
Required: `type`, `signal`
Body: Raw user request and any relevant project context.
### architecture_request
Sent to: architect (Phase 2, resume)
```yaml
---
type: architecture_request
signal: plan
---
```
Required: `type`, `signal`
Body: Assembled `## Research Context` block from all researchers, or "No research needed — proceed."
### research_request
Sent to: researcher
```yaml
---
type: research_request
signal: research
topic: "brief topic identifier"
---
```
Required: `type`, `signal`, `topic`
Body: Specific question, why it matters (what decision it gates), where to look, relevant project context.
---
## Schema Compliance
Before returning output, verify:
1. Output starts with a valid YAML frontmatter envelope (`---` delimiters)
2. `type` matches your message type
3. `signal` uses a valid enum value for your direction (agent→orch or orch→agent)
4. All required fields for your message type are present
5. Enum fields use exact values from this schema (no variations like "PASS" vs "pass")
6. Hard rules are satisfied (e.g., `critical_count > 0` implies `signal: fail`)

View file

@ -108,12 +108,17 @@ After each wave, spawn `reviewer` and `auditor` in a single response. They run i
Both receive: worker output, plan file path, acceptance criteria list, risk tags. Both receive: worker output, plan file path, acceptance criteria list, risk tags.
Collect both verdicts before deciding whether to advance to the next wave or send back for fixes. **Routing by envelope:** Read the `signal` field from each reviewer/auditor envelope:
- `signal: pass` → advance to next wave
- `signal: pass_with_notes` → advance, surface notes in final delivery
- `signal: fail` → check `critical_count` / `security_findings` and send worker to fix
Do not advance until both verdicts are collected.
### Step 7 — Feedback loop on issues ### Step 7 — Feedback loop on issues
1. Resume the worker with reviewer findings and instruction to fix 1. Resume the worker with a `revision_request` envelope containing reviewer/auditor findings
2. On resubmission, spawn reviewer again (new instance — stateless) 2. On resubmission (worker returns `signal: rfr`), spawn reviewer again (new instance — stateless)
3. Repeat 3. Repeat
**Severity-aware decisions:** **Severity-aware decisions:**
@ -201,21 +206,28 @@ Spawning agents sequentially when they could run in parallel is a protocol viola
### Git flow ### Git flow
Workers signal `RFR` when done. You control commits: Workers return `signal: rfr` when done. You control commits:
- `LGTM` → worker commits - Send `signal: lgtm` → worker commits
- Mark a step `- [x]` in the plan file **only when every worker assigned to that step has received LGTM** - Mark a step `- [x]` in the plan file **only when every worker assigned to that step has received `signal: lgtm`**
- `REVISE` → worker fixes and resubmits with `RFR` - Send `signal: revise` → worker fixes and resubmits with `signal: rfr`
- Merge worktree branches after individual validation - Merge worktree branches after individual validation
- On Tier 2+: merge each worker's branch after validation, resolve conflicts if branches overlap - On Tier 2+: merge each worker's branch after validation, resolve conflicts if branches overlap
Only the orchestrator updates the plan file. Workers must not modify `.claude/plans/`. Only the orchestrator updates the plan file. Workers must not modify `.claude/plans/`.
### Review signals ### Message schema
| Signal | Direction | Meaning | All agent communication uses typed YAML frontmatter envelopes defined in the `message-schema` skill. The `signal` field is your primary routing key.
| Envelope signal | Direction | Your action |
|---|---|---| |---|---|---|
| `RFR` | worker → orchestrator | Ready for review | | `signal: rfr` | worker → you | Dispatch to reviewer (+ auditor if risk tags match) |
| `LGTM` | orchestrator → worker | Approved, commit your changes | | `signal: pass` | reviewer/auditor → you | Advance to next wave |
| `REVISE` | orchestrator → worker | Fix the listed issues and resubmit | | `signal: pass_with_notes` | reviewer/auditor → you | Advance, surface notes in delivery |
| `VERDICT: PASS / PASS WITH NOTES / FAIL` | reviewer → orchestrator | Review result | | `signal: fail` | reviewer/auditor → you | Send `revision_request` to worker |
| `VERDICT: PASS / PARTIAL / FAIL` | auditor → orchestrator | Runtime validation result | | `signal: triage_complete` | architect → you | Check `research_needed`, spawn researchers or resume architect |
| `signal: plan_complete` | architect → you | Read plan file, begin wave dispatch |
| `signal: research_complete` | researcher → you | Collect, assemble into Research Context |
| `signal: blocked` / `signal: escalate` | any → you | Investigate or escalate to user |
When dispatching agents, use the orchestrator→agent envelope types (`task_assignment`, `revision_request`, `approval`, `triage_request`, `architecture_request`, `research_request`) from the message-schema skill.

View file

@ -39,9 +39,17 @@ Before returning your output, validate against every item below. If you find a v
- If you stated something as fact, can you back it up? Challenge your own claims. - If you stated something as fact, can you back it up? Challenge your own claims.
- If you referenced documentation or source code, did you actually read it or are you recalling from training data? When it matters, verify. - If you referenced documentation or source code, did you actually read it or are you recalling from training data? When it matters, verify.
### Schema compliance
- Does your output start with a valid YAML frontmatter envelope (`---` delimiters)?
- Does the `type` field match your message type?
- Does the `signal` field use a valid enum value from the message-schema skill?
- Are all required fields for your message type present?
- Are hard rules satisfied (e.g., `critical_count > 0` requires `signal: fail`)?
## After validation ## After validation
Set `qa_check: pass` or `qa_check: fail` in your frontmatter envelope. This replaces the old `QA self-check` prose line.
In your Self-Assessment section, include: In your Self-Assessment section, include:
- `QA self-check: [pass/fail]` — did your output survive the checklist? - If qa_check is fail: what you found and fixed before submission
- If fail: what you found and fixed before submission
- If anything remains unverifiable, flag it explicitly as `Unverified: [claim]` - If anything remains unverifiable, flag it explicitly as `Unverified: [claim]`

View file

@ -5,15 +5,28 @@ description: Standard output format, feedback handling, and operational procedur
## Output format ## Output format
Return using this structure. If your orchestrator specifies a different format, use theirs — but always include Self-Assessment. Wrap your output in a `worker_submission` envelope per the message-schema skill:
```yaml
---
type: worker_submission
signal: rfr | blocked | escalate
files_changed:
- path/to/file1
- path/to/file2
ac_coverage:
AC1: pass | fail | partial | na
AC2: pass | fail | partial | na
qa_check: pass | fail
---
```
Then the markdown body:
``` ```
## Result ## Result
[Your deliverable here] [Your deliverable here]
## Files Changed
[List files modified/created, or "N/A" if not a code task]
## Self-Assessment ## Self-Assessment
- Acceptance criteria met: [yes/no per criterion, one line each] - Acceptance criteria met: [yes/no per criterion, one line each]
- Known limitations: [any, or "none"] - Known limitations: [any, or "none"]
@ -37,11 +50,11 @@ Before returning your output, run the `qa-checklist` skill against your work. Fi
## Commits ## Commits
Do not commit until your orchestrator sends `LGTM`. End your output with `RFR` to signal you're ready for review. Do not commit until your orchestrator sends `signal: lgtm`. Your output envelope's `signal: rfr` replaces the old freetext `RFR` — the envelope IS the signal.
- `RFR` — you → orchestrator: work complete, ready for review - `signal: rfr` — you → orchestrator: work complete, ready for review
- `LGTM` — orchestrator → you: approved, commit now - `signal: lgtm` — orchestrator → you: approved, commit now
- `REVISE` — orchestrator → you: needs fixes (issues attached) - `signal: revise` — orchestrator → you: needs fixes (issues attached)
When you receive `LGTM`: When you receive `LGTM`:
- Commit using conventional commit format per project conventions - Commit using conventional commit format per project conventions