From 197b19a554598d3afd35209d02347b394896759a Mon Sep 17 00:00:00 2001 From: Brady Gaster Date: Tue, 21 Jul 2026 00:04:49 -0700 Subject: [PATCH 01/15] fix: add three-state coordinator canary Add a HEAD canary near the start of the coordinator prompt while retaining the EOF canary. Update the Canary Check to skip non-coordinator agents, accept fully loaded coordinators, and halt only on positive truncation detection. Closes #1461 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09d05820-0b7e-482b-b942-c86b1225dcf9 --- .changeset/canary-false-positives.md | 6 + .github/agents/squad.agent.md | 2 + .squad-templates/copilot-instructions.md | 10 +- .squad-templates/squad.agent.md | 2 + .../templates/copilot-instructions.md | 10 +- .../templates/squad.agent.md.template | 2 + ...orkflow-wiring-appendix-a-code-reviewer.md | 262 ++++----- .../workflow-wiring-appendix-b-documenter.md | 280 ++++----- .../templates/workflow-wiring-guide.md | 552 +++++++++--------- .../templates/copilot-instructions.md | 10 +- .../templates/squad.agent.md.template | 2 + ...orkflow-wiring-appendix-a-code-reviewer.md | 262 ++++----- .../workflow-wiring-appendix-b-documenter.md | 280 ++++----- .../templates/workflow-wiring-guide.md | 552 +++++++++--------- templates/copilot-instructions.md | 10 +- templates/squad.agent.md.template | 2 + ...orkflow-wiring-appendix-a-code-reviewer.md | 262 ++++----- .../workflow-wiring-appendix-b-documenter.md | 280 ++++----- templates/workflow-wiring-guide.md | 552 +++++++++--------- 19 files changed, 1685 insertions(+), 1653 deletions(-) create mode 100644 .changeset/canary-false-positives.md diff --git a/.changeset/canary-false-positives.md b/.changeset/canary-false-positives.md new file mode 100644 index 000000000..61a1da19a --- /dev/null +++ b/.changeset/canary-false-positives.md @@ -0,0 +1,6 @@ +--- +"@bradygaster/squad-cli": patch +"@bradygaster/squad-sdk": patch +--- + +Fix #1461 canary false-positives: add HEAD canary to coordinator prompt and make the Canary Check three-state + coordinator-scoped so spawned/non-Squad agents no longer false-halt and truncation is positively detected. diff --git a/.github/agents/squad.agent.md b/.github/agents/squad.agent.md index eb545668f..2c9b59932 100644 --- a/.github/agents/squad.agent.md +++ b/.github/agents/squad.agent.md @@ -4,6 +4,8 @@ description: "Your AI team. Describe what you're building, get a team of special tools: ["*"] --- + + You are **Squad (Coordinator)** — the orchestrator for this project's AI team. diff --git a/.squad-templates/copilot-instructions.md b/.squad-templates/copilot-instructions.md index 2ba998c8e..851913676 100644 --- a/.squad-templates/copilot-instructions.md +++ b/.squad-templates/copilot-instructions.md @@ -6,13 +6,17 @@ You are working on a project that uses **Squad**, an AI team framework. When pic **IMPORTANT — run this check on EVERY session start, before doing any work:** -Look for the exact token `SQUAD_COORDINATOR_CANARY_a8f3` in your `agent_instructions`. This token is placed at the end of `squad.agent.md`. If it is missing, the coordinator file is missing or truncated. +Inspect your `agent_instructions` for the coordinator canaries: -**When the token is missing, you MUST:** +1. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **absent**, you are **not** running the Squad coordinator prompt. You are a spawned sub-agent, a `squad:{member}` coding agent, or a non-Squad/third-party agent. **Skip this check entirely. Do not warn, do not halt.** Proceed with your assigned task normally. +2. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **present** and the EOF canary `SQUAD_COORDINATOR_CANARY_a8f3` is **present**, the coordinator prompt loaded fully. Proceed with normal Squad behavior. +3. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **present** but the EOF canary `SQUAD_COORDINATOR_CANARY_a8f3` is **absent**, the coordinator prompt loaded but was **truncated** before the end. + +**When the coordinator prompt is truncated, you MUST:** 1. **STOP** — do not proceed with standard Squad behavior. 2. **WARN the user** with this exact message: ``` - ⚠️ Squad coordinator (squad.agent.md) appears to be missing or truncated. The canary token was not found. Do NOT proceed with standard Squad behavior — Squad's safety rails are not loaded. Please restart your session. + ⚠️ Squad coordinator (squad.agent.md) loaded but appears to be truncated before the EOF canary. Do NOT proceed with standard Squad behavior — Squad's safety rails are incomplete. Please restart your session. ``` 3. Do not continue with normal Squad routing, spawning, PR, or branch-protection behavior after emitting the warning. diff --git a/.squad-templates/squad.agent.md b/.squad-templates/squad.agent.md index eb545668f..2c9b59932 100644 --- a/.squad-templates/squad.agent.md +++ b/.squad-templates/squad.agent.md @@ -4,6 +4,8 @@ description: "Your AI team. Describe what you're building, get a team of special tools: ["*"] --- + + You are **Squad (Coordinator)** — the orchestrator for this project's AI team. diff --git a/packages/squad-cli/templates/copilot-instructions.md b/packages/squad-cli/templates/copilot-instructions.md index 2ba998c8e..851913676 100644 --- a/packages/squad-cli/templates/copilot-instructions.md +++ b/packages/squad-cli/templates/copilot-instructions.md @@ -6,13 +6,17 @@ You are working on a project that uses **Squad**, an AI team framework. When pic **IMPORTANT — run this check on EVERY session start, before doing any work:** -Look for the exact token `SQUAD_COORDINATOR_CANARY_a8f3` in your `agent_instructions`. This token is placed at the end of `squad.agent.md`. If it is missing, the coordinator file is missing or truncated. +Inspect your `agent_instructions` for the coordinator canaries: -**When the token is missing, you MUST:** +1. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **absent**, you are **not** running the Squad coordinator prompt. You are a spawned sub-agent, a `squad:{member}` coding agent, or a non-Squad/third-party agent. **Skip this check entirely. Do not warn, do not halt.** Proceed with your assigned task normally. +2. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **present** and the EOF canary `SQUAD_COORDINATOR_CANARY_a8f3` is **present**, the coordinator prompt loaded fully. Proceed with normal Squad behavior. +3. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **present** but the EOF canary `SQUAD_COORDINATOR_CANARY_a8f3` is **absent**, the coordinator prompt loaded but was **truncated** before the end. + +**When the coordinator prompt is truncated, you MUST:** 1. **STOP** — do not proceed with standard Squad behavior. 2. **WARN the user** with this exact message: ``` - ⚠️ Squad coordinator (squad.agent.md) appears to be missing or truncated. The canary token was not found. Do NOT proceed with standard Squad behavior — Squad's safety rails are not loaded. Please restart your session. + ⚠️ Squad coordinator (squad.agent.md) loaded but appears to be truncated before the EOF canary. Do NOT proceed with standard Squad behavior — Squad's safety rails are incomplete. Please restart your session. ``` 3. Do not continue with normal Squad routing, spawning, PR, or branch-protection behavior after emitting the warning. diff --git a/packages/squad-cli/templates/squad.agent.md.template b/packages/squad-cli/templates/squad.agent.md.template index eb545668f..2c9b59932 100644 --- a/packages/squad-cli/templates/squad.agent.md.template +++ b/packages/squad-cli/templates/squad.agent.md.template @@ -4,6 +4,8 @@ description: "Your AI team. Describe what you're building, get a team of special tools: ["*"] --- + + You are **Squad (Coordinator)** — the orchestrator for this project's AI team. diff --git a/packages/squad-cli/templates/workflow-wiring-appendix-a-code-reviewer.md b/packages/squad-cli/templates/workflow-wiring-appendix-a-code-reviewer.md index c447f7f9c..324e0ef60 100644 --- a/packages/squad-cli/templates/workflow-wiring-appendix-a-code-reviewer.md +++ b/packages/squad-cli/templates/workflow-wiring-appendix-a-code-reviewer.md @@ -1,131 +1,131 @@ -# Appendix A: Wiring a Code Reviewer — Complete Walkthrough - -> End-to-end example of adding a code reviewer to your squad and wiring their gate so it actually gets enforced. This walkthrough addresses a common failure: a reviewer is on the roster but never reviews a single PR because the gate wasn't wired. - -## The Problem This Solves - -Adding a reviewer to `team.md` gives them an identity. It does NOT: -- Tell the coordinator to route PRs to them -- Prevent PRs from being merged without their approval -- Prevent issues from being closed before review happens - -**What goes wrong without enforcement:** A reviewer can be on the roster as "Reviewer" from day one. Their charter says they review PRs. The routing table says "PR code review → {ReviewerName}." But PRs get merged and issues get closed without them ever being spawned. Why? - -Because the routing table says WHO handles what — it's for incoming requests ("review PR #42"). It does NOT say "after every agent completes work, route their output to {ReviewerName}." The coordinator routes work TO agents, but nothing tells it to route COMPLETED work to a reviewer. The "After Agent Work" flow in `squad.agent.md` says: collect results → present → spawn Scribe. No review step. - -**The fix has three layers:** - -| Layer | What it does | Where it lives | -|-------|-------------|----------------| -| Identity | Reviewer exists and knows how to review | `team.md` roster + `charter.md` | -| Routing | User can explicitly request "review this" | `routing.md` routing table | -| **Enforcement** | Coordinator MUST route every PR to reviewer before merge | `routing.md` Rules section + `issue-lifecycle.md` post-work steps | - -Most squads get layers 1 and 2 right. Layer 3 — enforcement — is what's usually missing. - -## Step-by-Step Walkthrough - -### Step 1: Create the reviewer's identity - -Create `.squad/agents/{name}/charter.md`: - -```markdown -# {Name} — Code Reviewer - -## Identity -- **Name:** {Name} -- **Role:** Code Reviewer -- **Expertise:** Code quality, correctness, test coverage, security, patterns -- **Style:** Thorough, fair, specific. Provides actionable feedback. - -## What I Own -- Reviewing PRs for code quality, correctness, and test coverage -- Identifying bugs, security issues, and design problems -- Providing specific, actionable feedback (not vague suggestions) - -## How I Review -1. Read the PR diff completely -2. Check: does it do what the issue asked for? -3. Check: are there tests? Do they cover the important cases? -4. Check: are there bugs, edge cases, or security issues? -5. Check: does it follow project patterns and conventions? -6. Verdict: APPROVE or REJECT with specific feedback - -## Boundaries -**I handle:** Code review, PR review, quality gates -**I don't handle:** Implementation, design, research, documentation - -## On REJECT -I provide specific feedback: what's wrong, why, and what to do instead. -The original author fixes their work. I re-review after fixes. -``` - -Create `.squad/agents/{name}/history.md` seeded with project context. - -### Step 2: Add to team.md roster - -```markdown -| 👑 {Name} | Code Reviewer | `.squad/agents/{name}/charter.md` | ✅ Active | -``` - -### Step 3: Add routing table entry - -In `routing.md` → routing table: - -```markdown -| PR code review | 👑 {Name} | — | "Review PR #42", code quality, finding reports | -``` - -**⚠️ This is necessary but NOT sufficient.** This only handles explicit review requests. It does NOT enforce automatic review of every PR. - -### Step 4: Add enforcement rule (THIS IS THE CRITICAL STEP) - -In `routing.md` → `## Rules` section, add a numbered rule: - -```markdown -N. **{Name} PR Gate** — every PR created by any agent MUST be reviewed by {Name} - before merge. The coordinator spawns {Name} (sync) with the PR diff after - the author pushes and creates the PR. On REJECT, the original author addresses - feedback. On APPROVE, the coordinator merges via `gh pr merge`. No PR merges - without {Name}'s approval. -``` - -**Why this works when the routing table alone didn't:** The routing table is for matching incoming work to agents. Rules are behavioral constraints the coordinator must follow AFTER work completes. The rule says "after a PR exists, you MUST do X before proceeding." The routing table says "if someone asks for a review, route to X." - -### Step 5: Wire into issue-lifecycle.md - -In `.squad/templates/issue-lifecycle.md`, the "Coordinator Post-Work Steps" section should reference your reviewer by name: - -```markdown -4. **Route to reviewer.** Spawn {Name} (sync) with the PR diff for code review. -``` - -This is the operational detail — the step-by-step instructions the coordinator follows after an agent completes issue work. The routing rule (Step 4) is the mandate; the lifecycle template is the procedure. - -### Step 6: Add to casting registry - -Update `.squad/casting/registry.json` with the new entry. - -### Step 7: Verify - -Ask yourself these questions: - -- [ ] If a clean session coordinator reads `routing.md` Rules, will it know to route PRs to this reviewer? → Check rule N exists. -- [ ] If an agent completes work and pushes a PR, does the coordinator's post-work flow include a review step? → Check `issue-lifecycle.md` step 4. -- [ ] Can the coordinator merge a PR without the reviewer's approval? → The rule should say "No PR merges without {Name}'s approval." -- [ ] Can the coordinator close an issue without a merged PR? → Check the issue closure rule exists. - -If any answer is wrong, you have a gap. - -## What Each File Controls (Summary) - -| File | What it contributes to the reviewer gate | -|------|----------------------------------------| -| `charter.md` | WHO the reviewer is and HOW they review | -| `team.md` | That the reviewer EXISTS on the team | -| `routing.md` routing table | That explicit review requests go to this reviewer | -| `routing.md` Rules section | That the coordinator MUST route EVERY PR to this reviewer (enforcement) | -| `issue-lifecycle.md` | The step-by-step procedure for the post-work review flow | -| `casting/registry.json` | Persistent name tracking | - -**Remove any one of these and the gate has a hole.** The most commonly missed piece is the Rules section entry (Step 4). +# Appendix A: Wiring a Code Reviewer — Complete Walkthrough + +> End-to-end example of adding a code reviewer to your squad and wiring their gate so it actually gets enforced. This walkthrough addresses a common failure: a reviewer is on the roster but never reviews a single PR because the gate wasn't wired. + +## The Problem This Solves + +Adding a reviewer to `team.md` gives them an identity. It does NOT: +- Tell the coordinator to route PRs to them +- Prevent PRs from being merged without their approval +- Prevent issues from being closed before review happens + +**What goes wrong without enforcement:** A reviewer can be on the roster as "Reviewer" from day one. Their charter says they review PRs. The routing table says "PR code review → {ReviewerName}." But PRs get merged and issues get closed without them ever being spawned. Why? + +Because the routing table says WHO handles what — it's for incoming requests ("review PR #42"). It does NOT say "after every agent completes work, route their output to {ReviewerName}." The coordinator routes work TO agents, but nothing tells it to route COMPLETED work to a reviewer. The "After Agent Work" flow in `squad.agent.md` says: collect results → present → spawn Scribe. No review step. + +**The fix has three layers:** + +| Layer | What it does | Where it lives | +|-------|-------------|----------------| +| Identity | Reviewer exists and knows how to review | `team.md` roster + `charter.md` | +| Routing | User can explicitly request "review this" | `routing.md` routing table | +| **Enforcement** | Coordinator MUST route every PR to reviewer before merge | `routing.md` Rules section + `issue-lifecycle.md` post-work steps | + +Most squads get layers 1 and 2 right. Layer 3 — enforcement — is what's usually missing. + +## Step-by-Step Walkthrough + +### Step 1: Create the reviewer's identity + +Create `.squad/agents/{name}/charter.md`: + +```markdown +# {Name} — Code Reviewer + +## Identity +- **Name:** {Name} +- **Role:** Code Reviewer +- **Expertise:** Code quality, correctness, test coverage, security, patterns +- **Style:** Thorough, fair, specific. Provides actionable feedback. + +## What I Own +- Reviewing PRs for code quality, correctness, and test coverage +- Identifying bugs, security issues, and design problems +- Providing specific, actionable feedback (not vague suggestions) + +## How I Review +1. Read the PR diff completely +2. Check: does it do what the issue asked for? +3. Check: are there tests? Do they cover the important cases? +4. Check: are there bugs, edge cases, or security issues? +5. Check: does it follow project patterns and conventions? +6. Verdict: APPROVE or REJECT with specific feedback + +## Boundaries +**I handle:** Code review, PR review, quality gates +**I don't handle:** Implementation, design, research, documentation + +## On REJECT +I provide specific feedback: what's wrong, why, and what to do instead. +The original author fixes their work. I re-review after fixes. +``` + +Create `.squad/agents/{name}/history.md` seeded with project context. + +### Step 2: Add to team.md roster + +```markdown +| 👑 {Name} | Code Reviewer | `.squad/agents/{name}/charter.md` | ✅ Active | +``` + +### Step 3: Add routing table entry + +In `routing.md` → routing table: + +```markdown +| PR code review | 👑 {Name} | — | "Review PR #42", code quality, finding reports | +``` + +**⚠️ This is necessary but NOT sufficient.** This only handles explicit review requests. It does NOT enforce automatic review of every PR. + +### Step 4: Add enforcement rule (THIS IS THE CRITICAL STEP) + +In `routing.md` → `## Rules` section, add a numbered rule: + +```markdown +N. **{Name} PR Gate** — every PR created by any agent MUST be reviewed by {Name} + before merge. The coordinator spawns {Name} (sync) with the PR diff after + the author pushes and creates the PR. On REJECT, the original author addresses + feedback. On APPROVE, the coordinator merges via `gh pr merge`. No PR merges + without {Name}'s approval. +``` + +**Why this works when the routing table alone didn't:** The routing table is for matching incoming work to agents. Rules are behavioral constraints the coordinator must follow AFTER work completes. The rule says "after a PR exists, you MUST do X before proceeding." The routing table says "if someone asks for a review, route to X." + +### Step 5: Wire into issue-lifecycle.md + +In `.squad/templates/issue-lifecycle.md`, the "Coordinator Post-Work Steps" section should reference your reviewer by name: + +```markdown +4. **Route to reviewer.** Spawn {Name} (sync) with the PR diff for code review. +``` + +This is the operational detail — the step-by-step instructions the coordinator follows after an agent completes issue work. The routing rule (Step 4) is the mandate; the lifecycle template is the procedure. + +### Step 6: Add to casting registry + +Update `.squad/casting/registry.json` with the new entry. + +### Step 7: Verify + +Ask yourself these questions: + +- [ ] If a clean session coordinator reads `routing.md` Rules, will it know to route PRs to this reviewer? → Check rule N exists. +- [ ] If an agent completes work and pushes a PR, does the coordinator's post-work flow include a review step? → Check `issue-lifecycle.md` step 4. +- [ ] Can the coordinator merge a PR without the reviewer's approval? → The rule should say "No PR merges without {Name}'s approval." +- [ ] Can the coordinator close an issue without a merged PR? → Check the issue closure rule exists. + +If any answer is wrong, you have a gap. + +## What Each File Controls (Summary) + +| File | What it contributes to the reviewer gate | +|------|----------------------------------------| +| `charter.md` | WHO the reviewer is and HOW they review | +| `team.md` | That the reviewer EXISTS on the team | +| `routing.md` routing table | That explicit review requests go to this reviewer | +| `routing.md` Rules section | That the coordinator MUST route EVERY PR to this reviewer (enforcement) | +| `issue-lifecycle.md` | The step-by-step procedure for the post-work review flow | +| `casting/registry.json` | Persistent name tracking | + +**Remove any one of these and the gate has a hole.** The most commonly missed piece is the Rules section entry (Step 4). diff --git a/packages/squad-cli/templates/workflow-wiring-appendix-b-documenter.md b/packages/squad-cli/templates/workflow-wiring-appendix-b-documenter.md index fb8cd26aa..870e61ccb 100644 --- a/packages/squad-cli/templates/workflow-wiring-appendix-b-documenter.md +++ b/packages/squad-cli/templates/workflow-wiring-appendix-b-documenter.md @@ -1,140 +1,140 @@ -# Appendix B: Wiring a Documenter/Librarian — Complete Walkthrough - -> End-to-end example of adding a documenter role that ensures significant changes are documented. This is a FOLLOW-UP TRIGGER pattern — not a gate (which blocks), but an automatic downstream task that fires after work completes. - -## The Problem This Solves - -Your project has agents building features, fixing bugs, and writing tools. But nobody documents what was built, how to use it, or what changed. Documentation happens only when someone explicitly asks — and by then, the context is lost. - -A documenter/librarian role solves this by automatically evaluating whether completed work needs documentation and producing it if so. - -## Gate vs Follow-Up Trigger - -| Pattern | Blocks work? | When it runs | Example | -|---------|-------------|-------------|---------| -| **Gate** (Appendix A) | Yes — work cannot proceed without approval | Before merge | Code reviewer must approve PR | -| **Follow-up trigger** | No — work proceeds, documentation happens in parallel | After merge | Documenter evaluates if docs are needed | - -A documenter is typically a follow-up trigger, not a gate. You don't want documentation review to block a hotfix from merging. But you DO want documentation to happen automatically after significant changes. - -## Step-by-Step Walkthrough - -### Step 1: Create the documenter's identity - -Create `.squad/agents/{name}/charter.md`: - -```markdown -# {Name} — Documenter - -## Identity -- **Name:** {Name} -- **Role:** Documenter / Librarian -- **Expertise:** Documentation, guides, READMEs, changelogs, knowledge management -- **Style:** Clear, thorough, user-focused. Makes complex things understandable. - -## What I Own -- Evaluating whether completed work needs documentation -- Writing/updating READMEs, guides, and runbooks -- Maintaining a docs index so nothing gets lost -- Summarizing design decisions and architectural changes - -## How I Work -1. Read the PR diff or agent output -2. Assess: does this change user-facing behavior? Add a new feature? Change configuration? -3. If yes: write or update the relevant documentation -4. If no: report "no docs needed" with brief justification - -## Boundaries -**I handle:** Documentation, guides, READMEs, summaries, knowledge management -**I don't handle:** Code implementation, code review, research, operations -``` - -Create `.squad/agents/{name}/history.md` seeded with project context. - -### Step 2: Add to team.md roster - -```markdown -| 📝 {Name} | Documenter | `.squad/agents/{name}/charter.md` | ✅ Active | -``` - -### Step 3: Add routing table entry - -In `routing.md` → routing table: - -```markdown -| Documentation, reports, summaries | 📝 {Name} | `docs/` | "Write docs for X", "Summarize this", guides, READMEs | -``` - -### Step 4: Add follow-up trigger rule - -In `routing.md` → `## Rules` section, add a numbered rule: - -```markdown -N. **Documentation follow-up** — after any PR is merged that adds or modifies - user-facing features, scripts, tools, or configuration, the coordinator - spawns {Name} (background) to evaluate whether documentation is needed. - {Name} reads the merged PR diff and either writes/updates docs or reports - "no docs needed." This is a follow-up, not a gate — it does not block - the merge. -``` - -**Why a rule and not a ceremony:** Ceremonies are structured multi-participant meetings. This is a single-agent follow-up task. A routing rule is simpler and more appropriate. - -**Why background, not sync:** Documentation doesn't block other work. The documenter runs in parallel with whatever comes next. - -### Step 5: Wire into the coordinator's post-merge flow - -This is the trickiest part. The coordinator's After Agent Work flow doesn't currently have a "post-merge" hook. You wire this through the issue-lifecycle template. - -In `.squad/templates/issue-lifecycle.md`, after the merge step, add: - -```markdown -8. **Documentation follow-up.** After merge, check routing.md Rules for - documentation follow-up rule. If present, spawn the documenter (background) - with the merged PR diff to evaluate whether docs are needed. -``` - -Alternatively, you can wire this as an `after` ceremony in `ceremonies.md`: - -```yaml -- name: "Documentation Check" - when: "after" - condition: "PR merged that adds features, scripts, tools, or config changes" - facilitator: "{DocumenterName}" - participants: ["{DocumenterName}"] - output: "Docs written/updated, or 'no docs needed' with justification" -``` - -### Step 6: Worktree for doc changes - -If the documenter produces files, they need a worktree — docs are files too. The coordinator should: -1. Create a worktree for the doc update (e.g., `squad/{issue}-docs`) -2. The documenter commits and pushes -3. A PR is created for the docs -4. The docs PR goes through the normal review flow (including the code reviewer if you have one) - -This means doc changes also get reviewed. The documenter is not exempt from the review gate. - -### Step 7: Add to casting registry - -Update `.squad/casting/registry.json` with the new entry. - -### Step 8: Verify - -- [ ] After a feature PR merges, does the coordinator spawn the documenter? → Check the routing rule exists. -- [ ] Does the documenter get a worktree for their work? → Check the worktree rule covers docs. -- [ ] Do doc changes go through the review gate? → They should — docs are files, files need PRs, PRs need review. -- [ ] Is the follow-up non-blocking? → The documenter should be background, not sync. - -## What Each File Controls (Summary) - -| File | What it contributes | -|------|-------------------| -| `charter.md` | WHO the documenter is and HOW they evaluate | -| `team.md` | That the documenter EXISTS | -| `routing.md` routing table | That explicit doc requests go to this member | -| `routing.md` Rules section | That the coordinator MUST spawn docs evaluation after merges (enforcement) | -| `issue-lifecycle.md` or `ceremonies.md` | The procedural hook: when exactly the follow-up fires | -| `casting/registry.json` | Persistent name tracking | - -**The most commonly missed piece:** The Rules section entry (Step 4). Without it, the documenter only runs when someone explicitly says "write docs for X." The whole point is that it runs automatically. +# Appendix B: Wiring a Documenter/Librarian — Complete Walkthrough + +> End-to-end example of adding a documenter role that ensures significant changes are documented. This is a FOLLOW-UP TRIGGER pattern — not a gate (which blocks), but an automatic downstream task that fires after work completes. + +## The Problem This Solves + +Your project has agents building features, fixing bugs, and writing tools. But nobody documents what was built, how to use it, or what changed. Documentation happens only when someone explicitly asks — and by then, the context is lost. + +A documenter/librarian role solves this by automatically evaluating whether completed work needs documentation and producing it if so. + +## Gate vs Follow-Up Trigger + +| Pattern | Blocks work? | When it runs | Example | +|---------|-------------|-------------|---------| +| **Gate** (Appendix A) | Yes — work cannot proceed without approval | Before merge | Code reviewer must approve PR | +| **Follow-up trigger** | No — work proceeds, documentation happens in parallel | After merge | Documenter evaluates if docs are needed | + +A documenter is typically a follow-up trigger, not a gate. You don't want documentation review to block a hotfix from merging. But you DO want documentation to happen automatically after significant changes. + +## Step-by-Step Walkthrough + +### Step 1: Create the documenter's identity + +Create `.squad/agents/{name}/charter.md`: + +```markdown +# {Name} — Documenter + +## Identity +- **Name:** {Name} +- **Role:** Documenter / Librarian +- **Expertise:** Documentation, guides, READMEs, changelogs, knowledge management +- **Style:** Clear, thorough, user-focused. Makes complex things understandable. + +## What I Own +- Evaluating whether completed work needs documentation +- Writing/updating READMEs, guides, and runbooks +- Maintaining a docs index so nothing gets lost +- Summarizing design decisions and architectural changes + +## How I Work +1. Read the PR diff or agent output +2. Assess: does this change user-facing behavior? Add a new feature? Change configuration? +3. If yes: write or update the relevant documentation +4. If no: report "no docs needed" with brief justification + +## Boundaries +**I handle:** Documentation, guides, READMEs, summaries, knowledge management +**I don't handle:** Code implementation, code review, research, operations +``` + +Create `.squad/agents/{name}/history.md` seeded with project context. + +### Step 2: Add to team.md roster + +```markdown +| 📝 {Name} | Documenter | `.squad/agents/{name}/charter.md` | ✅ Active | +``` + +### Step 3: Add routing table entry + +In `routing.md` → routing table: + +```markdown +| Documentation, reports, summaries | 📝 {Name} | `docs/` | "Write docs for X", "Summarize this", guides, READMEs | +``` + +### Step 4: Add follow-up trigger rule + +In `routing.md` → `## Rules` section, add a numbered rule: + +```markdown +N. **Documentation follow-up** — after any PR is merged that adds or modifies + user-facing features, scripts, tools, or configuration, the coordinator + spawns {Name} (background) to evaluate whether documentation is needed. + {Name} reads the merged PR diff and either writes/updates docs or reports + "no docs needed." This is a follow-up, not a gate — it does not block + the merge. +``` + +**Why a rule and not a ceremony:** Ceremonies are structured multi-participant meetings. This is a single-agent follow-up task. A routing rule is simpler and more appropriate. + +**Why background, not sync:** Documentation doesn't block other work. The documenter runs in parallel with whatever comes next. + +### Step 5: Wire into the coordinator's post-merge flow + +This is the trickiest part. The coordinator's After Agent Work flow doesn't currently have a "post-merge" hook. You wire this through the issue-lifecycle template. + +In `.squad/templates/issue-lifecycle.md`, after the merge step, add: + +```markdown +8. **Documentation follow-up.** After merge, check routing.md Rules for + documentation follow-up rule. If present, spawn the documenter (background) + with the merged PR diff to evaluate whether docs are needed. +``` + +Alternatively, you can wire this as an `after` ceremony in `ceremonies.md`: + +```yaml +- name: "Documentation Check" + when: "after" + condition: "PR merged that adds features, scripts, tools, or config changes" + facilitator: "{DocumenterName}" + participants: ["{DocumenterName}"] + output: "Docs written/updated, or 'no docs needed' with justification" +``` + +### Step 6: Worktree for doc changes + +If the documenter produces files, they need a worktree — docs are files too. The coordinator should: +1. Create a worktree for the doc update (e.g., `squad/{issue}-docs`) +2. The documenter commits and pushes +3. A PR is created for the docs +4. The docs PR goes through the normal review flow (including the code reviewer if you have one) + +This means doc changes also get reviewed. The documenter is not exempt from the review gate. + +### Step 7: Add to casting registry + +Update `.squad/casting/registry.json` with the new entry. + +### Step 8: Verify + +- [ ] After a feature PR merges, does the coordinator spawn the documenter? → Check the routing rule exists. +- [ ] Does the documenter get a worktree for their work? → Check the worktree rule covers docs. +- [ ] Do doc changes go through the review gate? → They should — docs are files, files need PRs, PRs need review. +- [ ] Is the follow-up non-blocking? → The documenter should be background, not sync. + +## What Each File Controls (Summary) + +| File | What it contributes | +|------|-------------------| +| `charter.md` | WHO the documenter is and HOW they evaluate | +| `team.md` | That the documenter EXISTS | +| `routing.md` routing table | That explicit doc requests go to this member | +| `routing.md` Rules section | That the coordinator MUST spawn docs evaluation after merges (enforcement) | +| `issue-lifecycle.md` or `ceremonies.md` | The procedural hook: when exactly the follow-up fires | +| `casting/registry.json` | Persistent name tracking | + +**The most commonly missed piece:** The Rules section entry (Step 4). Without it, the documenter only runs when someone explicitly says "write docs for X." The whole point is that it runs automatically. diff --git a/packages/squad-cli/templates/workflow-wiring-guide.md b/packages/squad-cli/templates/workflow-wiring-guide.md index 853a13d0a..e6b775ae1 100644 --- a/packages/squad-cli/templates/workflow-wiring-guide.md +++ b/packages/squad-cli/templates/workflow-wiring-guide.md @@ -1,276 +1,276 @@ -# Squad Workflow Wiring Guide - -> How to wire up new team members, reviewer gates, and custom workflows so they actually get enforced by the coordinator — even in a clean session with no prior memory. - -## Why This Guide Exists - -The Squad framework (`squad.agent.md`) provides generic orchestration primitives. **It does not prescribe a specific workflow.** Your project's workflow — whether that's "all code goes through PRs and reviews" or "just commit to main" — must be wired into project-level configuration files. - -If a workflow rule exists only in someone's memory, in a chat transcript, or in `decisions.md` but NOT in a configuration file the coordinator reads at decision time — **it will not be followed in a clean session.** - -### Why Existing Patterns Aren't Enough - -The Squad framework already has concepts for routing tables, reviewer roles, and ceremonies. But having these concepts does NOT mean they work automatically: - -- **Adding a reviewer to the roster ≠ enforcing reviews.** A reviewer can be on the roster with "Reviewer" as their role and never review a single PR — because no RULE in `routing.md` tells the coordinator to route PRs to them. The roster says WHO exists. Rules say WHAT they enforce. - -- **Capturing a decision ≠ enforcing it.** `decisions.md` may contain "every change must go through a PR" and "only {ReviewerName} closes PRs." These can get buried in a large file that the coordinator reads for context but doesn't treat as enforcement rules. A decision is a historical record. A routing rule is an enforceable constraint. - -- **Describing a lifecycle ≠ wiring it.** `squad.agent.md` describes issue→branch→PR→review→merge. But if the After Agent Work section (the flow the coordinator actually follows after every agent completes) has no push/PR/review step, the lifecycle is described conceptually but never connected to the coordinator's actual decision flow. - -**The pattern that works:** A numbered rule in `routing.md` → Rules section. The coordinator reads this section, treats each rule as a constraint, and follows them. If your workflow isn't a numbered rule, it's a suggestion. - ---- - -## Configuration Surface Area - -The coordinator reads these files to decide how to behave. If your workflow isn't encoded in one of these, it doesn't exist. - -| File | What It Controls | Read When | -|------|-----------------|-----------| -| `routing.md` | WHO handles what, behavioral RULES, reviewer GATES | Every session start, before every routing decision | -| `ceremonies.md` | Auto-triggered ceremonies (before/after work batches) | Before spawning work batches, after completion | -| `templates/issue-lifecycle.md` | Git workflow: push, PR, review, merge, issue closure | When spawning agents for issue-linked work | -| Agent `charter.md` | Per-agent identity, boundaries, behavior | Inlined into every spawn prompt | -| `team.md` | Roster, member capabilities | Session start | -| `decisions.md` | Captured decisions and directives | Read by agents at spawn time | - -### How They Interact - -``` -User request arrives - → Coordinator reads routing.md (WHO handles this?) - → Coordinator checks ceremonies.md (any auto-triggered "before" ceremony?) - → Coordinator reads agent charter.md (inline into spawn prompt) - → If issue-linked: coordinator reads issue-lifecycle.md (add ISSUE CONTEXT to spawn prompt) - → Agent works - → Coordinator follows After Agent Work flow - → Coordinator checks ceremonies.md (any auto-triggered "after" ceremony?) - → Coordinator checks routing.md Rules section (any post-work rules to enforce?) -``` - -**The critical insight:** `routing.md` Rules section and `ceremonies.md` are the two enforcement mechanisms. If a rule isn't in one of these, the coordinator has no way to know about it. - ---- - -## How to Wire Up a New Team Member - -### Step 1: Create the member (files) - -``` -.squad/agents/{name}/ - charter.md ← Identity, role, boundaries, what they own - history.md ← Seeded with project context from team.md -``` - -### Step 2: Add to roster (`team.md`) - -Add a row to the `## Members` table: -``` -| {emoji} {Name} | {Role} | `.squad/agents/{name}/charter.md` | ✅ Active | -``` - -### Step 3: Add routing entry (`routing.md`) - -Add a row to the routing table: -``` -| {Work Type} | {emoji} {Name} | {Output Location} | {Examples} | -``` - -### Step 4: Add issue routing (if applicable) - -Add to the Issue Routing table in `routing.md`: -``` -| squad:{name} | {Description of work} | {emoji} {Name} | -``` - -### Step 5: Add to casting registry - -Update `.squad/casting/registry.json` with the new entry. - -### Step 6: Wire any gates (if this member is a reviewer/gate) - -**This is the step most people miss.** If the new member should review or gate other members' work, you need to wire enforcement. See "How to Wire Up a Reviewer Gate" below. - ---- - -## How to Wire Up a Reviewer Gate - -A reviewer gate means: "Agent X must review Agent Y's output before it proceeds." The framework supports this but does NOT automatically enforce it. You must wire it. - -### Option A: Routing Rule (recommended for simple gates) - -Add to `routing.md` → `## Rules` section: - -```markdown -N. **{GateName} Gate** — Every {output type} from {Author} MUST be reviewed by {ReviewerName} before {next step}. The coordinator routes {Author}'s output to {ReviewerName} (sync spawn), collects the verdict, and only proceeds if approved. On rejection, {Author} revises based on {ReviewerName}'s feedback. -``` - -**Example — reviewer for all PRs:** -```markdown -9. **{ReviewerName} PR Gate** — Every PR created by any agent MUST be reviewed by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) with the PR diff, collects APPROVE/REJECT verdict. On rejection, the original author addresses feedback. -``` - -**Example — design review gate:** -```markdown -10. **{DesignReviewer} Design Gate** — Every design doc produced by the architect MUST be reviewed by {DesignReviewer} before implementation begins. {DesignReviewer} always rejects the first draft on concept/approach. Implementation is BLOCKED until {DesignReviewer} approves. -``` - -**Why this works:** The coordinator reads the Rules section before and after every work batch. Rules are behavioral constraints the coordinator must follow. - -### Option B: Ceremony (recommended for multi-participant gates) - -Add to `ceremonies.md` using the Markdown table format the file uses: - -```markdown -## Design Review - -| Field | Value | -|-------|-------| -| **Trigger** | auto | -| **When** | before | -| **Condition** | task involves implementing a design doc | -| **Facilitator** | {DesignReviewer} | -| **Participants** | Architect, {DesignReviewer} | -| **Time budget** | focused | -| **Enabled** | ✅ yes | - -**Agenda:** -1. Read the design doc -2. Challenge the premise and approach -3. Demand alternatives and evidence -4. Verdict: APPROVE or REJECT -``` - -**Why this works:** The coordinator checks ceremonies.md for `before` ceremonies whose condition matches the current task. If matched, the ceremony runs before work begins. - -### Option A vs Option B - -| Use Case | Use Routing Rule | Use Ceremony | -|----------|-----------------|--------------| -| Simple 1-on-1 review (reviewer → author) | ✅ | Overkill | -| Multi-participant alignment (3+ agents) | Too simple | ✅ | -| Needs structured facilitation | No | ✅ | -| Must run automatically before specific work | Either works | ✅ | -| One-line behavioral constraint | ✅ | Overkill | - ---- - -## How to Wire Up an Issue Lifecycle (Git Workflow) - -This is where you define what happens after an agent completes work on a GitHub issue. The framework references `.squad/templates/issue-lifecycle.md` but does NOT create it — you must create it yourself. - -> **⚠️ This file is required if your project uses GitHub Issues Mode.** Without it, the coordinator has no post-work steps for push/PR/review and will treat agent commit as "done." - -See `.squad/templates/issue-lifecycle.md` for the full template if your project already has one. If not, create it following the pattern below. - -### Step 1: Create `templates/issue-lifecycle.md` - -Create `.squad/templates/issue-lifecycle.md` with your project's git workflow. At minimum it should include: - -- An ISSUE CONTEXT block template (for spawn prompts) -- Coordinator post-work steps (verify push → verify PR → route to reviewer → merge on approval) -- Issue closure rules (PR merge auto-close vs manual close) -- Worktree requirements (if applicable) - -### Step 2: Add enforcement rules to `routing.md` - -Add numbered rules to the `## Rules` section that reference the lifecycle: - -```markdown -N. **Issue lifecycle enforcement** — all issue-linked work follows the lifecycle - in `.squad/templates/issue-lifecycle.md`. The coordinator adds the ISSUE CONTEXT - block to spawn prompts and follows the post-work steps (verify push → verify PR - → route to reviewer → merge on approval). Read `issue-lifecycle.md` before - spawning any agent for issue work. - -N+1. **{ReviewerName} PR Gate** — every PR created by any agent MUST be reviewed - by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) - with the PR diff. On REJECT, the original author addresses feedback. On APPROVE, - the coordinator merges. No PR merges without {ReviewerName}'s approval. - -N+2. **Issue closure restriction** — issues that produced files (code, docs, scripts, - designs, tests) close ONLY via PR merge auto-close ("Closes #N" in PR body). - Never use `gh issue close` for file-producing work. Exception: tracking/strategic - issues and superseded issues may be closed with a comment. - -N+3. **Worktree for all file-producing work** — every task that creates or modifies - files (including documentation) requires a worktree. Exceptions: read-only queries, - Scribe (.squad/ state), pure analysis producing no files. -``` - -### Step 3: Verify your wiring - -After creating both files, run the verification checklist (below) to confirm a clean session coordinator would follow the lifecycle. - ---- - -## How to Wire Up a Custom Workflow Step - -If you need something that isn't a reviewer gate or issue lifecycle — for example, "always run tests before pushing" or "docs must be reviewed by the author before merge" — here's where to put it: - -### If it's a behavioral rule the coordinator should always follow: -→ Add to `routing.md` → `## Rules` section - -### If it should trigger automatically before/after specific work: -→ Add to `ceremonies.md` as a `before` or `after` ceremony - -### If it's something agents should do as part of their work: -→ Add to the agent's `charter.md` under a new section - -### If it's something that applies only to issue-linked work: -→ Add to `templates/issue-lifecycle.md` - -### If it's a team-wide constraint that should be visible to all agents: -→ Capture as a decision in `decisions.md` (via directive or decision inbox) - ---- - -## Verification Checklist - -After wiring any new member, gate, or workflow, verify: - -- [ ] **Clean session test:** Start a new session (no memory). Give a task. Does the coordinator follow the new rule? -- [ ] **File completeness:** Is the rule/gate/workflow encoded in a file the coordinator reads? (routing.md, ceremonies.md, issue-lifecycle.md, charter.md) -- [ ] **No verbal-only rules:** Is there anything the coordinator should do that's only in chat history or your memory? If yes, it will be lost on session restart. -- [ ] **Gate enforcement:** If you added a reviewer gate, does the routing.md Rules section or ceremonies.md explicitly say the coordinator must route to the reviewer? "Having a reviewer on the roster" is not the same as "enforcing that they review." -- [ ] **Issue lifecycle:** If your project uses PRs, does `templates/issue-lifecycle.md` exist? Does routing.md reference it? - ---- - -## Common Mistakes - -1. **Adding a reviewer to the roster but not wiring a gate.** Having a reviewer on the team doesn't mean they review anything. You must add a rule in routing.md that says "route PRs to {ReviewerName}." - -2. **Closing issues via `gh issue close` instead of PR merge.** If your project uses PRs, issue closure should happen via "Closes #N" in the PR body. Wire this in issue-lifecycle.md. - -3. **Writing docs/scripts directly on main.** If your project requires branches for all changes, the worktree gate must apply to ALL file-producing work — including docs. Make this explicit in routing.md Rules. - -4. **Assuming the coordinator remembers verbal instructions.** Each session starts fresh. If you told the coordinator "always use opus" in session 1, session 2 won't know unless it's in decisions.md or routing.md. - -5. **Not creating `issue-lifecycle.md`.** The framework references it but doesn't create it. If your project uses GitHub Issues Mode, create this template. - -6. **Capturing a decision but never encoding it as a rule.** `decisions.md` is a historical record. The coordinator reads it for context but doesn't treat entries as enforceable constraints. If a decision should be enforced, it must become a numbered rule in `routing.md` Rules section. - ---- - -## Decisions Audit - -Periodically scan `decisions.md` for directives that should be routing rules but aren't: - -1. Search for phrases like "always", "never", "must", "every", "required" -2. For each match, ask: "Is this enforced by a numbered rule in routing.md?" -3. If no → either add a rule, or accept that it's advisory-only -4. If yes → verify the rule text matches the decision - -This prevents `decisions.md` from becoming a graveyard of good intentions that the coordinator reads but doesn't act on. - ---- - -## Appendices - -For detailed end-to-end walkthroughs of specific wiring scenarios, see: - -- **[Appendix A: Wiring a Code Reviewer](workflow-wiring-appendix-a-code-reviewer.md)** — Full walkthrough of adding a code reviewer member and wiring their gate so it actually gets enforced. Includes every file that needs modification with exact content. - -- **[Appendix B: Wiring a Documenter/Librarian](workflow-wiring-appendix-b-documenter.md)** — Full walkthrough of adding a documenter role that ensures all significant changes are documented. Shows a follow-up trigger pattern rather than a gate pattern. +# Squad Workflow Wiring Guide + +> How to wire up new team members, reviewer gates, and custom workflows so they actually get enforced by the coordinator — even in a clean session with no prior memory. + +## Why This Guide Exists + +The Squad framework (`squad.agent.md`) provides generic orchestration primitives. **It does not prescribe a specific workflow.** Your project's workflow — whether that's "all code goes through PRs and reviews" or "just commit to main" — must be wired into project-level configuration files. + +If a workflow rule exists only in someone's memory, in a chat transcript, or in `decisions.md` but NOT in a configuration file the coordinator reads at decision time — **it will not be followed in a clean session.** + +### Why Existing Patterns Aren't Enough + +The Squad framework already has concepts for routing tables, reviewer roles, and ceremonies. But having these concepts does NOT mean they work automatically: + +- **Adding a reviewer to the roster ≠ enforcing reviews.** A reviewer can be on the roster with "Reviewer" as their role and never review a single PR — because no RULE in `routing.md` tells the coordinator to route PRs to them. The roster says WHO exists. Rules say WHAT they enforce. + +- **Capturing a decision ≠ enforcing it.** `decisions.md` may contain "every change must go through a PR" and "only {ReviewerName} closes PRs." These can get buried in a large file that the coordinator reads for context but doesn't treat as enforcement rules. A decision is a historical record. A routing rule is an enforceable constraint. + +- **Describing a lifecycle ≠ wiring it.** `squad.agent.md` describes issue→branch→PR→review→merge. But if the After Agent Work section (the flow the coordinator actually follows after every agent completes) has no push/PR/review step, the lifecycle is described conceptually but never connected to the coordinator's actual decision flow. + +**The pattern that works:** A numbered rule in `routing.md` → Rules section. The coordinator reads this section, treats each rule as a constraint, and follows them. If your workflow isn't a numbered rule, it's a suggestion. + +--- + +## Configuration Surface Area + +The coordinator reads these files to decide how to behave. If your workflow isn't encoded in one of these, it doesn't exist. + +| File | What It Controls | Read When | +|------|-----------------|-----------| +| `routing.md` | WHO handles what, behavioral RULES, reviewer GATES | Every session start, before every routing decision | +| `ceremonies.md` | Auto-triggered ceremonies (before/after work batches) | Before spawning work batches, after completion | +| `templates/issue-lifecycle.md` | Git workflow: push, PR, review, merge, issue closure | When spawning agents for issue-linked work | +| Agent `charter.md` | Per-agent identity, boundaries, behavior | Inlined into every spawn prompt | +| `team.md` | Roster, member capabilities | Session start | +| `decisions.md` | Captured decisions and directives | Read by agents at spawn time | + +### How They Interact + +``` +User request arrives + → Coordinator reads routing.md (WHO handles this?) + → Coordinator checks ceremonies.md (any auto-triggered "before" ceremony?) + → Coordinator reads agent charter.md (inline into spawn prompt) + → If issue-linked: coordinator reads issue-lifecycle.md (add ISSUE CONTEXT to spawn prompt) + → Agent works + → Coordinator follows After Agent Work flow + → Coordinator checks ceremonies.md (any auto-triggered "after" ceremony?) + → Coordinator checks routing.md Rules section (any post-work rules to enforce?) +``` + +**The critical insight:** `routing.md` Rules section and `ceremonies.md` are the two enforcement mechanisms. If a rule isn't in one of these, the coordinator has no way to know about it. + +--- + +## How to Wire Up a New Team Member + +### Step 1: Create the member (files) + +``` +.squad/agents/{name}/ + charter.md ← Identity, role, boundaries, what they own + history.md ← Seeded with project context from team.md +``` + +### Step 2: Add to roster (`team.md`) + +Add a row to the `## Members` table: +``` +| {emoji} {Name} | {Role} | `.squad/agents/{name}/charter.md` | ✅ Active | +``` + +### Step 3: Add routing entry (`routing.md`) + +Add a row to the routing table: +``` +| {Work Type} | {emoji} {Name} | {Output Location} | {Examples} | +``` + +### Step 4: Add issue routing (if applicable) + +Add to the Issue Routing table in `routing.md`: +``` +| squad:{name} | {Description of work} | {emoji} {Name} | +``` + +### Step 5: Add to casting registry + +Update `.squad/casting/registry.json` with the new entry. + +### Step 6: Wire any gates (if this member is a reviewer/gate) + +**This is the step most people miss.** If the new member should review or gate other members' work, you need to wire enforcement. See "How to Wire Up a Reviewer Gate" below. + +--- + +## How to Wire Up a Reviewer Gate + +A reviewer gate means: "Agent X must review Agent Y's output before it proceeds." The framework supports this but does NOT automatically enforce it. You must wire it. + +### Option A: Routing Rule (recommended for simple gates) + +Add to `routing.md` → `## Rules` section: + +```markdown +N. **{GateName} Gate** — Every {output type} from {Author} MUST be reviewed by {ReviewerName} before {next step}. The coordinator routes {Author}'s output to {ReviewerName} (sync spawn), collects the verdict, and only proceeds if approved. On rejection, {Author} revises based on {ReviewerName}'s feedback. +``` + +**Example — reviewer for all PRs:** +```markdown +9. **{ReviewerName} PR Gate** — Every PR created by any agent MUST be reviewed by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) with the PR diff, collects APPROVE/REJECT verdict. On rejection, the original author addresses feedback. +``` + +**Example — design review gate:** +```markdown +10. **{DesignReviewer} Design Gate** — Every design doc produced by the architect MUST be reviewed by {DesignReviewer} before implementation begins. {DesignReviewer} always rejects the first draft on concept/approach. Implementation is BLOCKED until {DesignReviewer} approves. +``` + +**Why this works:** The coordinator reads the Rules section before and after every work batch. Rules are behavioral constraints the coordinator must follow. + +### Option B: Ceremony (recommended for multi-participant gates) + +Add to `ceremonies.md` using the Markdown table format the file uses: + +```markdown +## Design Review + +| Field | Value | +|-------|-------| +| **Trigger** | auto | +| **When** | before | +| **Condition** | task involves implementing a design doc | +| **Facilitator** | {DesignReviewer} | +| **Participants** | Architect, {DesignReviewer} | +| **Time budget** | focused | +| **Enabled** | ✅ yes | + +**Agenda:** +1. Read the design doc +2. Challenge the premise and approach +3. Demand alternatives and evidence +4. Verdict: APPROVE or REJECT +``` + +**Why this works:** The coordinator checks ceremonies.md for `before` ceremonies whose condition matches the current task. If matched, the ceremony runs before work begins. + +### Option A vs Option B + +| Use Case | Use Routing Rule | Use Ceremony | +|----------|-----------------|--------------| +| Simple 1-on-1 review (reviewer → author) | ✅ | Overkill | +| Multi-participant alignment (3+ agents) | Too simple | ✅ | +| Needs structured facilitation | No | ✅ | +| Must run automatically before specific work | Either works | ✅ | +| One-line behavioral constraint | ✅ | Overkill | + +--- + +## How to Wire Up an Issue Lifecycle (Git Workflow) + +This is where you define what happens after an agent completes work on a GitHub issue. The framework references `.squad/templates/issue-lifecycle.md` but does NOT create it — you must create it yourself. + +> **⚠️ This file is required if your project uses GitHub Issues Mode.** Without it, the coordinator has no post-work steps for push/PR/review and will treat agent commit as "done." + +See `.squad/templates/issue-lifecycle.md` for the full template if your project already has one. If not, create it following the pattern below. + +### Step 1: Create `templates/issue-lifecycle.md` + +Create `.squad/templates/issue-lifecycle.md` with your project's git workflow. At minimum it should include: + +- An ISSUE CONTEXT block template (for spawn prompts) +- Coordinator post-work steps (verify push → verify PR → route to reviewer → merge on approval) +- Issue closure rules (PR merge auto-close vs manual close) +- Worktree requirements (if applicable) + +### Step 2: Add enforcement rules to `routing.md` + +Add numbered rules to the `## Rules` section that reference the lifecycle: + +```markdown +N. **Issue lifecycle enforcement** — all issue-linked work follows the lifecycle + in `.squad/templates/issue-lifecycle.md`. The coordinator adds the ISSUE CONTEXT + block to spawn prompts and follows the post-work steps (verify push → verify PR + → route to reviewer → merge on approval). Read `issue-lifecycle.md` before + spawning any agent for issue work. + +N+1. **{ReviewerName} PR Gate** — every PR created by any agent MUST be reviewed + by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) + with the PR diff. On REJECT, the original author addresses feedback. On APPROVE, + the coordinator merges. No PR merges without {ReviewerName}'s approval. + +N+2. **Issue closure restriction** — issues that produced files (code, docs, scripts, + designs, tests) close ONLY via PR merge auto-close ("Closes #N" in PR body). + Never use `gh issue close` for file-producing work. Exception: tracking/strategic + issues and superseded issues may be closed with a comment. + +N+3. **Worktree for all file-producing work** — every task that creates or modifies + files (including documentation) requires a worktree. Exceptions: read-only queries, + Scribe (.squad/ state), pure analysis producing no files. +``` + +### Step 3: Verify your wiring + +After creating both files, run the verification checklist (below) to confirm a clean session coordinator would follow the lifecycle. + +--- + +## How to Wire Up a Custom Workflow Step + +If you need something that isn't a reviewer gate or issue lifecycle — for example, "always run tests before pushing" or "docs must be reviewed by the author before merge" — here's where to put it: + +### If it's a behavioral rule the coordinator should always follow: +→ Add to `routing.md` → `## Rules` section + +### If it should trigger automatically before/after specific work: +→ Add to `ceremonies.md` as a `before` or `after` ceremony + +### If it's something agents should do as part of their work: +→ Add to the agent's `charter.md` under a new section + +### If it's something that applies only to issue-linked work: +→ Add to `templates/issue-lifecycle.md` + +### If it's a team-wide constraint that should be visible to all agents: +→ Capture as a decision in `decisions.md` (via directive or decision inbox) + +--- + +## Verification Checklist + +After wiring any new member, gate, or workflow, verify: + +- [ ] **Clean session test:** Start a new session (no memory). Give a task. Does the coordinator follow the new rule? +- [ ] **File completeness:** Is the rule/gate/workflow encoded in a file the coordinator reads? (routing.md, ceremonies.md, issue-lifecycle.md, charter.md) +- [ ] **No verbal-only rules:** Is there anything the coordinator should do that's only in chat history or your memory? If yes, it will be lost on session restart. +- [ ] **Gate enforcement:** If you added a reviewer gate, does the routing.md Rules section or ceremonies.md explicitly say the coordinator must route to the reviewer? "Having a reviewer on the roster" is not the same as "enforcing that they review." +- [ ] **Issue lifecycle:** If your project uses PRs, does `templates/issue-lifecycle.md` exist? Does routing.md reference it? + +--- + +## Common Mistakes + +1. **Adding a reviewer to the roster but not wiring a gate.** Having a reviewer on the team doesn't mean they review anything. You must add a rule in routing.md that says "route PRs to {ReviewerName}." + +2. **Closing issues via `gh issue close` instead of PR merge.** If your project uses PRs, issue closure should happen via "Closes #N" in the PR body. Wire this in issue-lifecycle.md. + +3. **Writing docs/scripts directly on main.** If your project requires branches for all changes, the worktree gate must apply to ALL file-producing work — including docs. Make this explicit in routing.md Rules. + +4. **Assuming the coordinator remembers verbal instructions.** Each session starts fresh. If you told the coordinator "always use opus" in session 1, session 2 won't know unless it's in decisions.md or routing.md. + +5. **Not creating `issue-lifecycle.md`.** The framework references it but doesn't create it. If your project uses GitHub Issues Mode, create this template. + +6. **Capturing a decision but never encoding it as a rule.** `decisions.md` is a historical record. The coordinator reads it for context but doesn't treat entries as enforceable constraints. If a decision should be enforced, it must become a numbered rule in `routing.md` Rules section. + +--- + +## Decisions Audit + +Periodically scan `decisions.md` for directives that should be routing rules but aren't: + +1. Search for phrases like "always", "never", "must", "every", "required" +2. For each match, ask: "Is this enforced by a numbered rule in routing.md?" +3. If no → either add a rule, or accept that it's advisory-only +4. If yes → verify the rule text matches the decision + +This prevents `decisions.md` from becoming a graveyard of good intentions that the coordinator reads but doesn't act on. + +--- + +## Appendices + +For detailed end-to-end walkthroughs of specific wiring scenarios, see: + +- **[Appendix A: Wiring a Code Reviewer](workflow-wiring-appendix-a-code-reviewer.md)** — Full walkthrough of adding a code reviewer member and wiring their gate so it actually gets enforced. Includes every file that needs modification with exact content. + +- **[Appendix B: Wiring a Documenter/Librarian](workflow-wiring-appendix-b-documenter.md)** — Full walkthrough of adding a documenter role that ensures all significant changes are documented. Shows a follow-up trigger pattern rather than a gate pattern. diff --git a/packages/squad-sdk/templates/copilot-instructions.md b/packages/squad-sdk/templates/copilot-instructions.md index 2ba998c8e..851913676 100644 --- a/packages/squad-sdk/templates/copilot-instructions.md +++ b/packages/squad-sdk/templates/copilot-instructions.md @@ -6,13 +6,17 @@ You are working on a project that uses **Squad**, an AI team framework. When pic **IMPORTANT — run this check on EVERY session start, before doing any work:** -Look for the exact token `SQUAD_COORDINATOR_CANARY_a8f3` in your `agent_instructions`. This token is placed at the end of `squad.agent.md`. If it is missing, the coordinator file is missing or truncated. +Inspect your `agent_instructions` for the coordinator canaries: -**When the token is missing, you MUST:** +1. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **absent**, you are **not** running the Squad coordinator prompt. You are a spawned sub-agent, a `squad:{member}` coding agent, or a non-Squad/third-party agent. **Skip this check entirely. Do not warn, do not halt.** Proceed with your assigned task normally. +2. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **present** and the EOF canary `SQUAD_COORDINATOR_CANARY_a8f3` is **present**, the coordinator prompt loaded fully. Proceed with normal Squad behavior. +3. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **present** but the EOF canary `SQUAD_COORDINATOR_CANARY_a8f3` is **absent**, the coordinator prompt loaded but was **truncated** before the end. + +**When the coordinator prompt is truncated, you MUST:** 1. **STOP** — do not proceed with standard Squad behavior. 2. **WARN the user** with this exact message: ``` - ⚠️ Squad coordinator (squad.agent.md) appears to be missing or truncated. The canary token was not found. Do NOT proceed with standard Squad behavior — Squad's safety rails are not loaded. Please restart your session. + ⚠️ Squad coordinator (squad.agent.md) loaded but appears to be truncated before the EOF canary. Do NOT proceed with standard Squad behavior — Squad's safety rails are incomplete. Please restart your session. ``` 3. Do not continue with normal Squad routing, spawning, PR, or branch-protection behavior after emitting the warning. diff --git a/packages/squad-sdk/templates/squad.agent.md.template b/packages/squad-sdk/templates/squad.agent.md.template index eb545668f..2c9b59932 100644 --- a/packages/squad-sdk/templates/squad.agent.md.template +++ b/packages/squad-sdk/templates/squad.agent.md.template @@ -4,6 +4,8 @@ description: "Your AI team. Describe what you're building, get a team of special tools: ["*"] --- + + You are **Squad (Coordinator)** — the orchestrator for this project's AI team. diff --git a/packages/squad-sdk/templates/workflow-wiring-appendix-a-code-reviewer.md b/packages/squad-sdk/templates/workflow-wiring-appendix-a-code-reviewer.md index c447f7f9c..324e0ef60 100644 --- a/packages/squad-sdk/templates/workflow-wiring-appendix-a-code-reviewer.md +++ b/packages/squad-sdk/templates/workflow-wiring-appendix-a-code-reviewer.md @@ -1,131 +1,131 @@ -# Appendix A: Wiring a Code Reviewer — Complete Walkthrough - -> End-to-end example of adding a code reviewer to your squad and wiring their gate so it actually gets enforced. This walkthrough addresses a common failure: a reviewer is on the roster but never reviews a single PR because the gate wasn't wired. - -## The Problem This Solves - -Adding a reviewer to `team.md` gives them an identity. It does NOT: -- Tell the coordinator to route PRs to them -- Prevent PRs from being merged without their approval -- Prevent issues from being closed before review happens - -**What goes wrong without enforcement:** A reviewer can be on the roster as "Reviewer" from day one. Their charter says they review PRs. The routing table says "PR code review → {ReviewerName}." But PRs get merged and issues get closed without them ever being spawned. Why? - -Because the routing table says WHO handles what — it's for incoming requests ("review PR #42"). It does NOT say "after every agent completes work, route their output to {ReviewerName}." The coordinator routes work TO agents, but nothing tells it to route COMPLETED work to a reviewer. The "After Agent Work" flow in `squad.agent.md` says: collect results → present → spawn Scribe. No review step. - -**The fix has three layers:** - -| Layer | What it does | Where it lives | -|-------|-------------|----------------| -| Identity | Reviewer exists and knows how to review | `team.md` roster + `charter.md` | -| Routing | User can explicitly request "review this" | `routing.md` routing table | -| **Enforcement** | Coordinator MUST route every PR to reviewer before merge | `routing.md` Rules section + `issue-lifecycle.md` post-work steps | - -Most squads get layers 1 and 2 right. Layer 3 — enforcement — is what's usually missing. - -## Step-by-Step Walkthrough - -### Step 1: Create the reviewer's identity - -Create `.squad/agents/{name}/charter.md`: - -```markdown -# {Name} — Code Reviewer - -## Identity -- **Name:** {Name} -- **Role:** Code Reviewer -- **Expertise:** Code quality, correctness, test coverage, security, patterns -- **Style:** Thorough, fair, specific. Provides actionable feedback. - -## What I Own -- Reviewing PRs for code quality, correctness, and test coverage -- Identifying bugs, security issues, and design problems -- Providing specific, actionable feedback (not vague suggestions) - -## How I Review -1. Read the PR diff completely -2. Check: does it do what the issue asked for? -3. Check: are there tests? Do they cover the important cases? -4. Check: are there bugs, edge cases, or security issues? -5. Check: does it follow project patterns and conventions? -6. Verdict: APPROVE or REJECT with specific feedback - -## Boundaries -**I handle:** Code review, PR review, quality gates -**I don't handle:** Implementation, design, research, documentation - -## On REJECT -I provide specific feedback: what's wrong, why, and what to do instead. -The original author fixes their work. I re-review after fixes. -``` - -Create `.squad/agents/{name}/history.md` seeded with project context. - -### Step 2: Add to team.md roster - -```markdown -| 👑 {Name} | Code Reviewer | `.squad/agents/{name}/charter.md` | ✅ Active | -``` - -### Step 3: Add routing table entry - -In `routing.md` → routing table: - -```markdown -| PR code review | 👑 {Name} | — | "Review PR #42", code quality, finding reports | -``` - -**⚠️ This is necessary but NOT sufficient.** This only handles explicit review requests. It does NOT enforce automatic review of every PR. - -### Step 4: Add enforcement rule (THIS IS THE CRITICAL STEP) - -In `routing.md` → `## Rules` section, add a numbered rule: - -```markdown -N. **{Name} PR Gate** — every PR created by any agent MUST be reviewed by {Name} - before merge. The coordinator spawns {Name} (sync) with the PR diff after - the author pushes and creates the PR. On REJECT, the original author addresses - feedback. On APPROVE, the coordinator merges via `gh pr merge`. No PR merges - without {Name}'s approval. -``` - -**Why this works when the routing table alone didn't:** The routing table is for matching incoming work to agents. Rules are behavioral constraints the coordinator must follow AFTER work completes. The rule says "after a PR exists, you MUST do X before proceeding." The routing table says "if someone asks for a review, route to X." - -### Step 5: Wire into issue-lifecycle.md - -In `.squad/templates/issue-lifecycle.md`, the "Coordinator Post-Work Steps" section should reference your reviewer by name: - -```markdown -4. **Route to reviewer.** Spawn {Name} (sync) with the PR diff for code review. -``` - -This is the operational detail — the step-by-step instructions the coordinator follows after an agent completes issue work. The routing rule (Step 4) is the mandate; the lifecycle template is the procedure. - -### Step 6: Add to casting registry - -Update `.squad/casting/registry.json` with the new entry. - -### Step 7: Verify - -Ask yourself these questions: - -- [ ] If a clean session coordinator reads `routing.md` Rules, will it know to route PRs to this reviewer? → Check rule N exists. -- [ ] If an agent completes work and pushes a PR, does the coordinator's post-work flow include a review step? → Check `issue-lifecycle.md` step 4. -- [ ] Can the coordinator merge a PR without the reviewer's approval? → The rule should say "No PR merges without {Name}'s approval." -- [ ] Can the coordinator close an issue without a merged PR? → Check the issue closure rule exists. - -If any answer is wrong, you have a gap. - -## What Each File Controls (Summary) - -| File | What it contributes to the reviewer gate | -|------|----------------------------------------| -| `charter.md` | WHO the reviewer is and HOW they review | -| `team.md` | That the reviewer EXISTS on the team | -| `routing.md` routing table | That explicit review requests go to this reviewer | -| `routing.md` Rules section | That the coordinator MUST route EVERY PR to this reviewer (enforcement) | -| `issue-lifecycle.md` | The step-by-step procedure for the post-work review flow | -| `casting/registry.json` | Persistent name tracking | - -**Remove any one of these and the gate has a hole.** The most commonly missed piece is the Rules section entry (Step 4). +# Appendix A: Wiring a Code Reviewer — Complete Walkthrough + +> End-to-end example of adding a code reviewer to your squad and wiring their gate so it actually gets enforced. This walkthrough addresses a common failure: a reviewer is on the roster but never reviews a single PR because the gate wasn't wired. + +## The Problem This Solves + +Adding a reviewer to `team.md` gives them an identity. It does NOT: +- Tell the coordinator to route PRs to them +- Prevent PRs from being merged without their approval +- Prevent issues from being closed before review happens + +**What goes wrong without enforcement:** A reviewer can be on the roster as "Reviewer" from day one. Their charter says they review PRs. The routing table says "PR code review → {ReviewerName}." But PRs get merged and issues get closed without them ever being spawned. Why? + +Because the routing table says WHO handles what — it's for incoming requests ("review PR #42"). It does NOT say "after every agent completes work, route their output to {ReviewerName}." The coordinator routes work TO agents, but nothing tells it to route COMPLETED work to a reviewer. The "After Agent Work" flow in `squad.agent.md` says: collect results → present → spawn Scribe. No review step. + +**The fix has three layers:** + +| Layer | What it does | Where it lives | +|-------|-------------|----------------| +| Identity | Reviewer exists and knows how to review | `team.md` roster + `charter.md` | +| Routing | User can explicitly request "review this" | `routing.md` routing table | +| **Enforcement** | Coordinator MUST route every PR to reviewer before merge | `routing.md` Rules section + `issue-lifecycle.md` post-work steps | + +Most squads get layers 1 and 2 right. Layer 3 — enforcement — is what's usually missing. + +## Step-by-Step Walkthrough + +### Step 1: Create the reviewer's identity + +Create `.squad/agents/{name}/charter.md`: + +```markdown +# {Name} — Code Reviewer + +## Identity +- **Name:** {Name} +- **Role:** Code Reviewer +- **Expertise:** Code quality, correctness, test coverage, security, patterns +- **Style:** Thorough, fair, specific. Provides actionable feedback. + +## What I Own +- Reviewing PRs for code quality, correctness, and test coverage +- Identifying bugs, security issues, and design problems +- Providing specific, actionable feedback (not vague suggestions) + +## How I Review +1. Read the PR diff completely +2. Check: does it do what the issue asked for? +3. Check: are there tests? Do they cover the important cases? +4. Check: are there bugs, edge cases, or security issues? +5. Check: does it follow project patterns and conventions? +6. Verdict: APPROVE or REJECT with specific feedback + +## Boundaries +**I handle:** Code review, PR review, quality gates +**I don't handle:** Implementation, design, research, documentation + +## On REJECT +I provide specific feedback: what's wrong, why, and what to do instead. +The original author fixes their work. I re-review after fixes. +``` + +Create `.squad/agents/{name}/history.md` seeded with project context. + +### Step 2: Add to team.md roster + +```markdown +| 👑 {Name} | Code Reviewer | `.squad/agents/{name}/charter.md` | ✅ Active | +``` + +### Step 3: Add routing table entry + +In `routing.md` → routing table: + +```markdown +| PR code review | 👑 {Name} | — | "Review PR #42", code quality, finding reports | +``` + +**⚠️ This is necessary but NOT sufficient.** This only handles explicit review requests. It does NOT enforce automatic review of every PR. + +### Step 4: Add enforcement rule (THIS IS THE CRITICAL STEP) + +In `routing.md` → `## Rules` section, add a numbered rule: + +```markdown +N. **{Name} PR Gate** — every PR created by any agent MUST be reviewed by {Name} + before merge. The coordinator spawns {Name} (sync) with the PR diff after + the author pushes and creates the PR. On REJECT, the original author addresses + feedback. On APPROVE, the coordinator merges via `gh pr merge`. No PR merges + without {Name}'s approval. +``` + +**Why this works when the routing table alone didn't:** The routing table is for matching incoming work to agents. Rules are behavioral constraints the coordinator must follow AFTER work completes. The rule says "after a PR exists, you MUST do X before proceeding." The routing table says "if someone asks for a review, route to X." + +### Step 5: Wire into issue-lifecycle.md + +In `.squad/templates/issue-lifecycle.md`, the "Coordinator Post-Work Steps" section should reference your reviewer by name: + +```markdown +4. **Route to reviewer.** Spawn {Name} (sync) with the PR diff for code review. +``` + +This is the operational detail — the step-by-step instructions the coordinator follows after an agent completes issue work. The routing rule (Step 4) is the mandate; the lifecycle template is the procedure. + +### Step 6: Add to casting registry + +Update `.squad/casting/registry.json` with the new entry. + +### Step 7: Verify + +Ask yourself these questions: + +- [ ] If a clean session coordinator reads `routing.md` Rules, will it know to route PRs to this reviewer? → Check rule N exists. +- [ ] If an agent completes work and pushes a PR, does the coordinator's post-work flow include a review step? → Check `issue-lifecycle.md` step 4. +- [ ] Can the coordinator merge a PR without the reviewer's approval? → The rule should say "No PR merges without {Name}'s approval." +- [ ] Can the coordinator close an issue without a merged PR? → Check the issue closure rule exists. + +If any answer is wrong, you have a gap. + +## What Each File Controls (Summary) + +| File | What it contributes to the reviewer gate | +|------|----------------------------------------| +| `charter.md` | WHO the reviewer is and HOW they review | +| `team.md` | That the reviewer EXISTS on the team | +| `routing.md` routing table | That explicit review requests go to this reviewer | +| `routing.md` Rules section | That the coordinator MUST route EVERY PR to this reviewer (enforcement) | +| `issue-lifecycle.md` | The step-by-step procedure for the post-work review flow | +| `casting/registry.json` | Persistent name tracking | + +**Remove any one of these and the gate has a hole.** The most commonly missed piece is the Rules section entry (Step 4). diff --git a/packages/squad-sdk/templates/workflow-wiring-appendix-b-documenter.md b/packages/squad-sdk/templates/workflow-wiring-appendix-b-documenter.md index fb8cd26aa..870e61ccb 100644 --- a/packages/squad-sdk/templates/workflow-wiring-appendix-b-documenter.md +++ b/packages/squad-sdk/templates/workflow-wiring-appendix-b-documenter.md @@ -1,140 +1,140 @@ -# Appendix B: Wiring a Documenter/Librarian — Complete Walkthrough - -> End-to-end example of adding a documenter role that ensures significant changes are documented. This is a FOLLOW-UP TRIGGER pattern — not a gate (which blocks), but an automatic downstream task that fires after work completes. - -## The Problem This Solves - -Your project has agents building features, fixing bugs, and writing tools. But nobody documents what was built, how to use it, or what changed. Documentation happens only when someone explicitly asks — and by then, the context is lost. - -A documenter/librarian role solves this by automatically evaluating whether completed work needs documentation and producing it if so. - -## Gate vs Follow-Up Trigger - -| Pattern | Blocks work? | When it runs | Example | -|---------|-------------|-------------|---------| -| **Gate** (Appendix A) | Yes — work cannot proceed without approval | Before merge | Code reviewer must approve PR | -| **Follow-up trigger** | No — work proceeds, documentation happens in parallel | After merge | Documenter evaluates if docs are needed | - -A documenter is typically a follow-up trigger, not a gate. You don't want documentation review to block a hotfix from merging. But you DO want documentation to happen automatically after significant changes. - -## Step-by-Step Walkthrough - -### Step 1: Create the documenter's identity - -Create `.squad/agents/{name}/charter.md`: - -```markdown -# {Name} — Documenter - -## Identity -- **Name:** {Name} -- **Role:** Documenter / Librarian -- **Expertise:** Documentation, guides, READMEs, changelogs, knowledge management -- **Style:** Clear, thorough, user-focused. Makes complex things understandable. - -## What I Own -- Evaluating whether completed work needs documentation -- Writing/updating READMEs, guides, and runbooks -- Maintaining a docs index so nothing gets lost -- Summarizing design decisions and architectural changes - -## How I Work -1. Read the PR diff or agent output -2. Assess: does this change user-facing behavior? Add a new feature? Change configuration? -3. If yes: write or update the relevant documentation -4. If no: report "no docs needed" with brief justification - -## Boundaries -**I handle:** Documentation, guides, READMEs, summaries, knowledge management -**I don't handle:** Code implementation, code review, research, operations -``` - -Create `.squad/agents/{name}/history.md` seeded with project context. - -### Step 2: Add to team.md roster - -```markdown -| 📝 {Name} | Documenter | `.squad/agents/{name}/charter.md` | ✅ Active | -``` - -### Step 3: Add routing table entry - -In `routing.md` → routing table: - -```markdown -| Documentation, reports, summaries | 📝 {Name} | `docs/` | "Write docs for X", "Summarize this", guides, READMEs | -``` - -### Step 4: Add follow-up trigger rule - -In `routing.md` → `## Rules` section, add a numbered rule: - -```markdown -N. **Documentation follow-up** — after any PR is merged that adds or modifies - user-facing features, scripts, tools, or configuration, the coordinator - spawns {Name} (background) to evaluate whether documentation is needed. - {Name} reads the merged PR diff and either writes/updates docs or reports - "no docs needed." This is a follow-up, not a gate — it does not block - the merge. -``` - -**Why a rule and not a ceremony:** Ceremonies are structured multi-participant meetings. This is a single-agent follow-up task. A routing rule is simpler and more appropriate. - -**Why background, not sync:** Documentation doesn't block other work. The documenter runs in parallel with whatever comes next. - -### Step 5: Wire into the coordinator's post-merge flow - -This is the trickiest part. The coordinator's After Agent Work flow doesn't currently have a "post-merge" hook. You wire this through the issue-lifecycle template. - -In `.squad/templates/issue-lifecycle.md`, after the merge step, add: - -```markdown -8. **Documentation follow-up.** After merge, check routing.md Rules for - documentation follow-up rule. If present, spawn the documenter (background) - with the merged PR diff to evaluate whether docs are needed. -``` - -Alternatively, you can wire this as an `after` ceremony in `ceremonies.md`: - -```yaml -- name: "Documentation Check" - when: "after" - condition: "PR merged that adds features, scripts, tools, or config changes" - facilitator: "{DocumenterName}" - participants: ["{DocumenterName}"] - output: "Docs written/updated, or 'no docs needed' with justification" -``` - -### Step 6: Worktree for doc changes - -If the documenter produces files, they need a worktree — docs are files too. The coordinator should: -1. Create a worktree for the doc update (e.g., `squad/{issue}-docs`) -2. The documenter commits and pushes -3. A PR is created for the docs -4. The docs PR goes through the normal review flow (including the code reviewer if you have one) - -This means doc changes also get reviewed. The documenter is not exempt from the review gate. - -### Step 7: Add to casting registry - -Update `.squad/casting/registry.json` with the new entry. - -### Step 8: Verify - -- [ ] After a feature PR merges, does the coordinator spawn the documenter? → Check the routing rule exists. -- [ ] Does the documenter get a worktree for their work? → Check the worktree rule covers docs. -- [ ] Do doc changes go through the review gate? → They should — docs are files, files need PRs, PRs need review. -- [ ] Is the follow-up non-blocking? → The documenter should be background, not sync. - -## What Each File Controls (Summary) - -| File | What it contributes | -|------|-------------------| -| `charter.md` | WHO the documenter is and HOW they evaluate | -| `team.md` | That the documenter EXISTS | -| `routing.md` routing table | That explicit doc requests go to this member | -| `routing.md` Rules section | That the coordinator MUST spawn docs evaluation after merges (enforcement) | -| `issue-lifecycle.md` or `ceremonies.md` | The procedural hook: when exactly the follow-up fires | -| `casting/registry.json` | Persistent name tracking | - -**The most commonly missed piece:** The Rules section entry (Step 4). Without it, the documenter only runs when someone explicitly says "write docs for X." The whole point is that it runs automatically. +# Appendix B: Wiring a Documenter/Librarian — Complete Walkthrough + +> End-to-end example of adding a documenter role that ensures significant changes are documented. This is a FOLLOW-UP TRIGGER pattern — not a gate (which blocks), but an automatic downstream task that fires after work completes. + +## The Problem This Solves + +Your project has agents building features, fixing bugs, and writing tools. But nobody documents what was built, how to use it, or what changed. Documentation happens only when someone explicitly asks — and by then, the context is lost. + +A documenter/librarian role solves this by automatically evaluating whether completed work needs documentation and producing it if so. + +## Gate vs Follow-Up Trigger + +| Pattern | Blocks work? | When it runs | Example | +|---------|-------------|-------------|---------| +| **Gate** (Appendix A) | Yes — work cannot proceed without approval | Before merge | Code reviewer must approve PR | +| **Follow-up trigger** | No — work proceeds, documentation happens in parallel | After merge | Documenter evaluates if docs are needed | + +A documenter is typically a follow-up trigger, not a gate. You don't want documentation review to block a hotfix from merging. But you DO want documentation to happen automatically after significant changes. + +## Step-by-Step Walkthrough + +### Step 1: Create the documenter's identity + +Create `.squad/agents/{name}/charter.md`: + +```markdown +# {Name} — Documenter + +## Identity +- **Name:** {Name} +- **Role:** Documenter / Librarian +- **Expertise:** Documentation, guides, READMEs, changelogs, knowledge management +- **Style:** Clear, thorough, user-focused. Makes complex things understandable. + +## What I Own +- Evaluating whether completed work needs documentation +- Writing/updating READMEs, guides, and runbooks +- Maintaining a docs index so nothing gets lost +- Summarizing design decisions and architectural changes + +## How I Work +1. Read the PR diff or agent output +2. Assess: does this change user-facing behavior? Add a new feature? Change configuration? +3. If yes: write or update the relevant documentation +4. If no: report "no docs needed" with brief justification + +## Boundaries +**I handle:** Documentation, guides, READMEs, summaries, knowledge management +**I don't handle:** Code implementation, code review, research, operations +``` + +Create `.squad/agents/{name}/history.md` seeded with project context. + +### Step 2: Add to team.md roster + +```markdown +| 📝 {Name} | Documenter | `.squad/agents/{name}/charter.md` | ✅ Active | +``` + +### Step 3: Add routing table entry + +In `routing.md` → routing table: + +```markdown +| Documentation, reports, summaries | 📝 {Name} | `docs/` | "Write docs for X", "Summarize this", guides, READMEs | +``` + +### Step 4: Add follow-up trigger rule + +In `routing.md` → `## Rules` section, add a numbered rule: + +```markdown +N. **Documentation follow-up** — after any PR is merged that adds or modifies + user-facing features, scripts, tools, or configuration, the coordinator + spawns {Name} (background) to evaluate whether documentation is needed. + {Name} reads the merged PR diff and either writes/updates docs or reports + "no docs needed." This is a follow-up, not a gate — it does not block + the merge. +``` + +**Why a rule and not a ceremony:** Ceremonies are structured multi-participant meetings. This is a single-agent follow-up task. A routing rule is simpler and more appropriate. + +**Why background, not sync:** Documentation doesn't block other work. The documenter runs in parallel with whatever comes next. + +### Step 5: Wire into the coordinator's post-merge flow + +This is the trickiest part. The coordinator's After Agent Work flow doesn't currently have a "post-merge" hook. You wire this through the issue-lifecycle template. + +In `.squad/templates/issue-lifecycle.md`, after the merge step, add: + +```markdown +8. **Documentation follow-up.** After merge, check routing.md Rules for + documentation follow-up rule. If present, spawn the documenter (background) + with the merged PR diff to evaluate whether docs are needed. +``` + +Alternatively, you can wire this as an `after` ceremony in `ceremonies.md`: + +```yaml +- name: "Documentation Check" + when: "after" + condition: "PR merged that adds features, scripts, tools, or config changes" + facilitator: "{DocumenterName}" + participants: ["{DocumenterName}"] + output: "Docs written/updated, or 'no docs needed' with justification" +``` + +### Step 6: Worktree for doc changes + +If the documenter produces files, they need a worktree — docs are files too. The coordinator should: +1. Create a worktree for the doc update (e.g., `squad/{issue}-docs`) +2. The documenter commits and pushes +3. A PR is created for the docs +4. The docs PR goes through the normal review flow (including the code reviewer if you have one) + +This means doc changes also get reviewed. The documenter is not exempt from the review gate. + +### Step 7: Add to casting registry + +Update `.squad/casting/registry.json` with the new entry. + +### Step 8: Verify + +- [ ] After a feature PR merges, does the coordinator spawn the documenter? → Check the routing rule exists. +- [ ] Does the documenter get a worktree for their work? → Check the worktree rule covers docs. +- [ ] Do doc changes go through the review gate? → They should — docs are files, files need PRs, PRs need review. +- [ ] Is the follow-up non-blocking? → The documenter should be background, not sync. + +## What Each File Controls (Summary) + +| File | What it contributes | +|------|-------------------| +| `charter.md` | WHO the documenter is and HOW they evaluate | +| `team.md` | That the documenter EXISTS | +| `routing.md` routing table | That explicit doc requests go to this member | +| `routing.md` Rules section | That the coordinator MUST spawn docs evaluation after merges (enforcement) | +| `issue-lifecycle.md` or `ceremonies.md` | The procedural hook: when exactly the follow-up fires | +| `casting/registry.json` | Persistent name tracking | + +**The most commonly missed piece:** The Rules section entry (Step 4). Without it, the documenter only runs when someone explicitly says "write docs for X." The whole point is that it runs automatically. diff --git a/packages/squad-sdk/templates/workflow-wiring-guide.md b/packages/squad-sdk/templates/workflow-wiring-guide.md index 853a13d0a..e6b775ae1 100644 --- a/packages/squad-sdk/templates/workflow-wiring-guide.md +++ b/packages/squad-sdk/templates/workflow-wiring-guide.md @@ -1,276 +1,276 @@ -# Squad Workflow Wiring Guide - -> How to wire up new team members, reviewer gates, and custom workflows so they actually get enforced by the coordinator — even in a clean session with no prior memory. - -## Why This Guide Exists - -The Squad framework (`squad.agent.md`) provides generic orchestration primitives. **It does not prescribe a specific workflow.** Your project's workflow — whether that's "all code goes through PRs and reviews" or "just commit to main" — must be wired into project-level configuration files. - -If a workflow rule exists only in someone's memory, in a chat transcript, or in `decisions.md` but NOT in a configuration file the coordinator reads at decision time — **it will not be followed in a clean session.** - -### Why Existing Patterns Aren't Enough - -The Squad framework already has concepts for routing tables, reviewer roles, and ceremonies. But having these concepts does NOT mean they work automatically: - -- **Adding a reviewer to the roster ≠ enforcing reviews.** A reviewer can be on the roster with "Reviewer" as their role and never review a single PR — because no RULE in `routing.md` tells the coordinator to route PRs to them. The roster says WHO exists. Rules say WHAT they enforce. - -- **Capturing a decision ≠ enforcing it.** `decisions.md` may contain "every change must go through a PR" and "only {ReviewerName} closes PRs." These can get buried in a large file that the coordinator reads for context but doesn't treat as enforcement rules. A decision is a historical record. A routing rule is an enforceable constraint. - -- **Describing a lifecycle ≠ wiring it.** `squad.agent.md` describes issue→branch→PR→review→merge. But if the After Agent Work section (the flow the coordinator actually follows after every agent completes) has no push/PR/review step, the lifecycle is described conceptually but never connected to the coordinator's actual decision flow. - -**The pattern that works:** A numbered rule in `routing.md` → Rules section. The coordinator reads this section, treats each rule as a constraint, and follows them. If your workflow isn't a numbered rule, it's a suggestion. - ---- - -## Configuration Surface Area - -The coordinator reads these files to decide how to behave. If your workflow isn't encoded in one of these, it doesn't exist. - -| File | What It Controls | Read When | -|------|-----------------|-----------| -| `routing.md` | WHO handles what, behavioral RULES, reviewer GATES | Every session start, before every routing decision | -| `ceremonies.md` | Auto-triggered ceremonies (before/after work batches) | Before spawning work batches, after completion | -| `templates/issue-lifecycle.md` | Git workflow: push, PR, review, merge, issue closure | When spawning agents for issue-linked work | -| Agent `charter.md` | Per-agent identity, boundaries, behavior | Inlined into every spawn prompt | -| `team.md` | Roster, member capabilities | Session start | -| `decisions.md` | Captured decisions and directives | Read by agents at spawn time | - -### How They Interact - -``` -User request arrives - → Coordinator reads routing.md (WHO handles this?) - → Coordinator checks ceremonies.md (any auto-triggered "before" ceremony?) - → Coordinator reads agent charter.md (inline into spawn prompt) - → If issue-linked: coordinator reads issue-lifecycle.md (add ISSUE CONTEXT to spawn prompt) - → Agent works - → Coordinator follows After Agent Work flow - → Coordinator checks ceremonies.md (any auto-triggered "after" ceremony?) - → Coordinator checks routing.md Rules section (any post-work rules to enforce?) -``` - -**The critical insight:** `routing.md` Rules section and `ceremonies.md` are the two enforcement mechanisms. If a rule isn't in one of these, the coordinator has no way to know about it. - ---- - -## How to Wire Up a New Team Member - -### Step 1: Create the member (files) - -``` -.squad/agents/{name}/ - charter.md ← Identity, role, boundaries, what they own - history.md ← Seeded with project context from team.md -``` - -### Step 2: Add to roster (`team.md`) - -Add a row to the `## Members` table: -``` -| {emoji} {Name} | {Role} | `.squad/agents/{name}/charter.md` | ✅ Active | -``` - -### Step 3: Add routing entry (`routing.md`) - -Add a row to the routing table: -``` -| {Work Type} | {emoji} {Name} | {Output Location} | {Examples} | -``` - -### Step 4: Add issue routing (if applicable) - -Add to the Issue Routing table in `routing.md`: -``` -| squad:{name} | {Description of work} | {emoji} {Name} | -``` - -### Step 5: Add to casting registry - -Update `.squad/casting/registry.json` with the new entry. - -### Step 6: Wire any gates (if this member is a reviewer/gate) - -**This is the step most people miss.** If the new member should review or gate other members' work, you need to wire enforcement. See "How to Wire Up a Reviewer Gate" below. - ---- - -## How to Wire Up a Reviewer Gate - -A reviewer gate means: "Agent X must review Agent Y's output before it proceeds." The framework supports this but does NOT automatically enforce it. You must wire it. - -### Option A: Routing Rule (recommended for simple gates) - -Add to `routing.md` → `## Rules` section: - -```markdown -N. **{GateName} Gate** — Every {output type} from {Author} MUST be reviewed by {ReviewerName} before {next step}. The coordinator routes {Author}'s output to {ReviewerName} (sync spawn), collects the verdict, and only proceeds if approved. On rejection, {Author} revises based on {ReviewerName}'s feedback. -``` - -**Example — reviewer for all PRs:** -```markdown -9. **{ReviewerName} PR Gate** — Every PR created by any agent MUST be reviewed by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) with the PR diff, collects APPROVE/REJECT verdict. On rejection, the original author addresses feedback. -``` - -**Example — design review gate:** -```markdown -10. **{DesignReviewer} Design Gate** — Every design doc produced by the architect MUST be reviewed by {DesignReviewer} before implementation begins. {DesignReviewer} always rejects the first draft on concept/approach. Implementation is BLOCKED until {DesignReviewer} approves. -``` - -**Why this works:** The coordinator reads the Rules section before and after every work batch. Rules are behavioral constraints the coordinator must follow. - -### Option B: Ceremony (recommended for multi-participant gates) - -Add to `ceremonies.md` using the Markdown table format the file uses: - -```markdown -## Design Review - -| Field | Value | -|-------|-------| -| **Trigger** | auto | -| **When** | before | -| **Condition** | task involves implementing a design doc | -| **Facilitator** | {DesignReviewer} | -| **Participants** | Architect, {DesignReviewer} | -| **Time budget** | focused | -| **Enabled** | ✅ yes | - -**Agenda:** -1. Read the design doc -2. Challenge the premise and approach -3. Demand alternatives and evidence -4. Verdict: APPROVE or REJECT -``` - -**Why this works:** The coordinator checks ceremonies.md for `before` ceremonies whose condition matches the current task. If matched, the ceremony runs before work begins. - -### Option A vs Option B - -| Use Case | Use Routing Rule | Use Ceremony | -|----------|-----------------|--------------| -| Simple 1-on-1 review (reviewer → author) | ✅ | Overkill | -| Multi-participant alignment (3+ agents) | Too simple | ✅ | -| Needs structured facilitation | No | ✅ | -| Must run automatically before specific work | Either works | ✅ | -| One-line behavioral constraint | ✅ | Overkill | - ---- - -## How to Wire Up an Issue Lifecycle (Git Workflow) - -This is where you define what happens after an agent completes work on a GitHub issue. The framework references `.squad/templates/issue-lifecycle.md` but does NOT create it — you must create it yourself. - -> **⚠️ This file is required if your project uses GitHub Issues Mode.** Without it, the coordinator has no post-work steps for push/PR/review and will treat agent commit as "done." - -See `.squad/templates/issue-lifecycle.md` for the full template if your project already has one. If not, create it following the pattern below. - -### Step 1: Create `templates/issue-lifecycle.md` - -Create `.squad/templates/issue-lifecycle.md` with your project's git workflow. At minimum it should include: - -- An ISSUE CONTEXT block template (for spawn prompts) -- Coordinator post-work steps (verify push → verify PR → route to reviewer → merge on approval) -- Issue closure rules (PR merge auto-close vs manual close) -- Worktree requirements (if applicable) - -### Step 2: Add enforcement rules to `routing.md` - -Add numbered rules to the `## Rules` section that reference the lifecycle: - -```markdown -N. **Issue lifecycle enforcement** — all issue-linked work follows the lifecycle - in `.squad/templates/issue-lifecycle.md`. The coordinator adds the ISSUE CONTEXT - block to spawn prompts and follows the post-work steps (verify push → verify PR - → route to reviewer → merge on approval). Read `issue-lifecycle.md` before - spawning any agent for issue work. - -N+1. **{ReviewerName} PR Gate** — every PR created by any agent MUST be reviewed - by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) - with the PR diff. On REJECT, the original author addresses feedback. On APPROVE, - the coordinator merges. No PR merges without {ReviewerName}'s approval. - -N+2. **Issue closure restriction** — issues that produced files (code, docs, scripts, - designs, tests) close ONLY via PR merge auto-close ("Closes #N" in PR body). - Never use `gh issue close` for file-producing work. Exception: tracking/strategic - issues and superseded issues may be closed with a comment. - -N+3. **Worktree for all file-producing work** — every task that creates or modifies - files (including documentation) requires a worktree. Exceptions: read-only queries, - Scribe (.squad/ state), pure analysis producing no files. -``` - -### Step 3: Verify your wiring - -After creating both files, run the verification checklist (below) to confirm a clean session coordinator would follow the lifecycle. - ---- - -## How to Wire Up a Custom Workflow Step - -If you need something that isn't a reviewer gate or issue lifecycle — for example, "always run tests before pushing" or "docs must be reviewed by the author before merge" — here's where to put it: - -### If it's a behavioral rule the coordinator should always follow: -→ Add to `routing.md` → `## Rules` section - -### If it should trigger automatically before/after specific work: -→ Add to `ceremonies.md` as a `before` or `after` ceremony - -### If it's something agents should do as part of their work: -→ Add to the agent's `charter.md` under a new section - -### If it's something that applies only to issue-linked work: -→ Add to `templates/issue-lifecycle.md` - -### If it's a team-wide constraint that should be visible to all agents: -→ Capture as a decision in `decisions.md` (via directive or decision inbox) - ---- - -## Verification Checklist - -After wiring any new member, gate, or workflow, verify: - -- [ ] **Clean session test:** Start a new session (no memory). Give a task. Does the coordinator follow the new rule? -- [ ] **File completeness:** Is the rule/gate/workflow encoded in a file the coordinator reads? (routing.md, ceremonies.md, issue-lifecycle.md, charter.md) -- [ ] **No verbal-only rules:** Is there anything the coordinator should do that's only in chat history or your memory? If yes, it will be lost on session restart. -- [ ] **Gate enforcement:** If you added a reviewer gate, does the routing.md Rules section or ceremonies.md explicitly say the coordinator must route to the reviewer? "Having a reviewer on the roster" is not the same as "enforcing that they review." -- [ ] **Issue lifecycle:** If your project uses PRs, does `templates/issue-lifecycle.md` exist? Does routing.md reference it? - ---- - -## Common Mistakes - -1. **Adding a reviewer to the roster but not wiring a gate.** Having a reviewer on the team doesn't mean they review anything. You must add a rule in routing.md that says "route PRs to {ReviewerName}." - -2. **Closing issues via `gh issue close` instead of PR merge.** If your project uses PRs, issue closure should happen via "Closes #N" in the PR body. Wire this in issue-lifecycle.md. - -3. **Writing docs/scripts directly on main.** If your project requires branches for all changes, the worktree gate must apply to ALL file-producing work — including docs. Make this explicit in routing.md Rules. - -4. **Assuming the coordinator remembers verbal instructions.** Each session starts fresh. If you told the coordinator "always use opus" in session 1, session 2 won't know unless it's in decisions.md or routing.md. - -5. **Not creating `issue-lifecycle.md`.** The framework references it but doesn't create it. If your project uses GitHub Issues Mode, create this template. - -6. **Capturing a decision but never encoding it as a rule.** `decisions.md` is a historical record. The coordinator reads it for context but doesn't treat entries as enforceable constraints. If a decision should be enforced, it must become a numbered rule in `routing.md` Rules section. - ---- - -## Decisions Audit - -Periodically scan `decisions.md` for directives that should be routing rules but aren't: - -1. Search for phrases like "always", "never", "must", "every", "required" -2. For each match, ask: "Is this enforced by a numbered rule in routing.md?" -3. If no → either add a rule, or accept that it's advisory-only -4. If yes → verify the rule text matches the decision - -This prevents `decisions.md` from becoming a graveyard of good intentions that the coordinator reads but doesn't act on. - ---- - -## Appendices - -For detailed end-to-end walkthroughs of specific wiring scenarios, see: - -- **[Appendix A: Wiring a Code Reviewer](workflow-wiring-appendix-a-code-reviewer.md)** — Full walkthrough of adding a code reviewer member and wiring their gate so it actually gets enforced. Includes every file that needs modification with exact content. - -- **[Appendix B: Wiring a Documenter/Librarian](workflow-wiring-appendix-b-documenter.md)** — Full walkthrough of adding a documenter role that ensures all significant changes are documented. Shows a follow-up trigger pattern rather than a gate pattern. +# Squad Workflow Wiring Guide + +> How to wire up new team members, reviewer gates, and custom workflows so they actually get enforced by the coordinator — even in a clean session with no prior memory. + +## Why This Guide Exists + +The Squad framework (`squad.agent.md`) provides generic orchestration primitives. **It does not prescribe a specific workflow.** Your project's workflow — whether that's "all code goes through PRs and reviews" or "just commit to main" — must be wired into project-level configuration files. + +If a workflow rule exists only in someone's memory, in a chat transcript, or in `decisions.md` but NOT in a configuration file the coordinator reads at decision time — **it will not be followed in a clean session.** + +### Why Existing Patterns Aren't Enough + +The Squad framework already has concepts for routing tables, reviewer roles, and ceremonies. But having these concepts does NOT mean they work automatically: + +- **Adding a reviewer to the roster ≠ enforcing reviews.** A reviewer can be on the roster with "Reviewer" as their role and never review a single PR — because no RULE in `routing.md` tells the coordinator to route PRs to them. The roster says WHO exists. Rules say WHAT they enforce. + +- **Capturing a decision ≠ enforcing it.** `decisions.md` may contain "every change must go through a PR" and "only {ReviewerName} closes PRs." These can get buried in a large file that the coordinator reads for context but doesn't treat as enforcement rules. A decision is a historical record. A routing rule is an enforceable constraint. + +- **Describing a lifecycle ≠ wiring it.** `squad.agent.md` describes issue→branch→PR→review→merge. But if the After Agent Work section (the flow the coordinator actually follows after every agent completes) has no push/PR/review step, the lifecycle is described conceptually but never connected to the coordinator's actual decision flow. + +**The pattern that works:** A numbered rule in `routing.md` → Rules section. The coordinator reads this section, treats each rule as a constraint, and follows them. If your workflow isn't a numbered rule, it's a suggestion. + +--- + +## Configuration Surface Area + +The coordinator reads these files to decide how to behave. If your workflow isn't encoded in one of these, it doesn't exist. + +| File | What It Controls | Read When | +|------|-----------------|-----------| +| `routing.md` | WHO handles what, behavioral RULES, reviewer GATES | Every session start, before every routing decision | +| `ceremonies.md` | Auto-triggered ceremonies (before/after work batches) | Before spawning work batches, after completion | +| `templates/issue-lifecycle.md` | Git workflow: push, PR, review, merge, issue closure | When spawning agents for issue-linked work | +| Agent `charter.md` | Per-agent identity, boundaries, behavior | Inlined into every spawn prompt | +| `team.md` | Roster, member capabilities | Session start | +| `decisions.md` | Captured decisions and directives | Read by agents at spawn time | + +### How They Interact + +``` +User request arrives + → Coordinator reads routing.md (WHO handles this?) + → Coordinator checks ceremonies.md (any auto-triggered "before" ceremony?) + → Coordinator reads agent charter.md (inline into spawn prompt) + → If issue-linked: coordinator reads issue-lifecycle.md (add ISSUE CONTEXT to spawn prompt) + → Agent works + → Coordinator follows After Agent Work flow + → Coordinator checks ceremonies.md (any auto-triggered "after" ceremony?) + → Coordinator checks routing.md Rules section (any post-work rules to enforce?) +``` + +**The critical insight:** `routing.md` Rules section and `ceremonies.md` are the two enforcement mechanisms. If a rule isn't in one of these, the coordinator has no way to know about it. + +--- + +## How to Wire Up a New Team Member + +### Step 1: Create the member (files) + +``` +.squad/agents/{name}/ + charter.md ← Identity, role, boundaries, what they own + history.md ← Seeded with project context from team.md +``` + +### Step 2: Add to roster (`team.md`) + +Add a row to the `## Members` table: +``` +| {emoji} {Name} | {Role} | `.squad/agents/{name}/charter.md` | ✅ Active | +``` + +### Step 3: Add routing entry (`routing.md`) + +Add a row to the routing table: +``` +| {Work Type} | {emoji} {Name} | {Output Location} | {Examples} | +``` + +### Step 4: Add issue routing (if applicable) + +Add to the Issue Routing table in `routing.md`: +``` +| squad:{name} | {Description of work} | {emoji} {Name} | +``` + +### Step 5: Add to casting registry + +Update `.squad/casting/registry.json` with the new entry. + +### Step 6: Wire any gates (if this member is a reviewer/gate) + +**This is the step most people miss.** If the new member should review or gate other members' work, you need to wire enforcement. See "How to Wire Up a Reviewer Gate" below. + +--- + +## How to Wire Up a Reviewer Gate + +A reviewer gate means: "Agent X must review Agent Y's output before it proceeds." The framework supports this but does NOT automatically enforce it. You must wire it. + +### Option A: Routing Rule (recommended for simple gates) + +Add to `routing.md` → `## Rules` section: + +```markdown +N. **{GateName} Gate** — Every {output type} from {Author} MUST be reviewed by {ReviewerName} before {next step}. The coordinator routes {Author}'s output to {ReviewerName} (sync spawn), collects the verdict, and only proceeds if approved. On rejection, {Author} revises based on {ReviewerName}'s feedback. +``` + +**Example — reviewer for all PRs:** +```markdown +9. **{ReviewerName} PR Gate** — Every PR created by any agent MUST be reviewed by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) with the PR diff, collects APPROVE/REJECT verdict. On rejection, the original author addresses feedback. +``` + +**Example — design review gate:** +```markdown +10. **{DesignReviewer} Design Gate** — Every design doc produced by the architect MUST be reviewed by {DesignReviewer} before implementation begins. {DesignReviewer} always rejects the first draft on concept/approach. Implementation is BLOCKED until {DesignReviewer} approves. +``` + +**Why this works:** The coordinator reads the Rules section before and after every work batch. Rules are behavioral constraints the coordinator must follow. + +### Option B: Ceremony (recommended for multi-participant gates) + +Add to `ceremonies.md` using the Markdown table format the file uses: + +```markdown +## Design Review + +| Field | Value | +|-------|-------| +| **Trigger** | auto | +| **When** | before | +| **Condition** | task involves implementing a design doc | +| **Facilitator** | {DesignReviewer} | +| **Participants** | Architect, {DesignReviewer} | +| **Time budget** | focused | +| **Enabled** | ✅ yes | + +**Agenda:** +1. Read the design doc +2. Challenge the premise and approach +3. Demand alternatives and evidence +4. Verdict: APPROVE or REJECT +``` + +**Why this works:** The coordinator checks ceremonies.md for `before` ceremonies whose condition matches the current task. If matched, the ceremony runs before work begins. + +### Option A vs Option B + +| Use Case | Use Routing Rule | Use Ceremony | +|----------|-----------------|--------------| +| Simple 1-on-1 review (reviewer → author) | ✅ | Overkill | +| Multi-participant alignment (3+ agents) | Too simple | ✅ | +| Needs structured facilitation | No | ✅ | +| Must run automatically before specific work | Either works | ✅ | +| One-line behavioral constraint | ✅ | Overkill | + +--- + +## How to Wire Up an Issue Lifecycle (Git Workflow) + +This is where you define what happens after an agent completes work on a GitHub issue. The framework references `.squad/templates/issue-lifecycle.md` but does NOT create it — you must create it yourself. + +> **⚠️ This file is required if your project uses GitHub Issues Mode.** Without it, the coordinator has no post-work steps for push/PR/review and will treat agent commit as "done." + +See `.squad/templates/issue-lifecycle.md` for the full template if your project already has one. If not, create it following the pattern below. + +### Step 1: Create `templates/issue-lifecycle.md` + +Create `.squad/templates/issue-lifecycle.md` with your project's git workflow. At minimum it should include: + +- An ISSUE CONTEXT block template (for spawn prompts) +- Coordinator post-work steps (verify push → verify PR → route to reviewer → merge on approval) +- Issue closure rules (PR merge auto-close vs manual close) +- Worktree requirements (if applicable) + +### Step 2: Add enforcement rules to `routing.md` + +Add numbered rules to the `## Rules` section that reference the lifecycle: + +```markdown +N. **Issue lifecycle enforcement** — all issue-linked work follows the lifecycle + in `.squad/templates/issue-lifecycle.md`. The coordinator adds the ISSUE CONTEXT + block to spawn prompts and follows the post-work steps (verify push → verify PR + → route to reviewer → merge on approval). Read `issue-lifecycle.md` before + spawning any agent for issue work. + +N+1. **{ReviewerName} PR Gate** — every PR created by any agent MUST be reviewed + by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) + with the PR diff. On REJECT, the original author addresses feedback. On APPROVE, + the coordinator merges. No PR merges without {ReviewerName}'s approval. + +N+2. **Issue closure restriction** — issues that produced files (code, docs, scripts, + designs, tests) close ONLY via PR merge auto-close ("Closes #N" in PR body). + Never use `gh issue close` for file-producing work. Exception: tracking/strategic + issues and superseded issues may be closed with a comment. + +N+3. **Worktree for all file-producing work** — every task that creates or modifies + files (including documentation) requires a worktree. Exceptions: read-only queries, + Scribe (.squad/ state), pure analysis producing no files. +``` + +### Step 3: Verify your wiring + +After creating both files, run the verification checklist (below) to confirm a clean session coordinator would follow the lifecycle. + +--- + +## How to Wire Up a Custom Workflow Step + +If you need something that isn't a reviewer gate or issue lifecycle — for example, "always run tests before pushing" or "docs must be reviewed by the author before merge" — here's where to put it: + +### If it's a behavioral rule the coordinator should always follow: +→ Add to `routing.md` → `## Rules` section + +### If it should trigger automatically before/after specific work: +→ Add to `ceremonies.md` as a `before` or `after` ceremony + +### If it's something agents should do as part of their work: +→ Add to the agent's `charter.md` under a new section + +### If it's something that applies only to issue-linked work: +→ Add to `templates/issue-lifecycle.md` + +### If it's a team-wide constraint that should be visible to all agents: +→ Capture as a decision in `decisions.md` (via directive or decision inbox) + +--- + +## Verification Checklist + +After wiring any new member, gate, or workflow, verify: + +- [ ] **Clean session test:** Start a new session (no memory). Give a task. Does the coordinator follow the new rule? +- [ ] **File completeness:** Is the rule/gate/workflow encoded in a file the coordinator reads? (routing.md, ceremonies.md, issue-lifecycle.md, charter.md) +- [ ] **No verbal-only rules:** Is there anything the coordinator should do that's only in chat history or your memory? If yes, it will be lost on session restart. +- [ ] **Gate enforcement:** If you added a reviewer gate, does the routing.md Rules section or ceremonies.md explicitly say the coordinator must route to the reviewer? "Having a reviewer on the roster" is not the same as "enforcing that they review." +- [ ] **Issue lifecycle:** If your project uses PRs, does `templates/issue-lifecycle.md` exist? Does routing.md reference it? + +--- + +## Common Mistakes + +1. **Adding a reviewer to the roster but not wiring a gate.** Having a reviewer on the team doesn't mean they review anything. You must add a rule in routing.md that says "route PRs to {ReviewerName}." + +2. **Closing issues via `gh issue close` instead of PR merge.** If your project uses PRs, issue closure should happen via "Closes #N" in the PR body. Wire this in issue-lifecycle.md. + +3. **Writing docs/scripts directly on main.** If your project requires branches for all changes, the worktree gate must apply to ALL file-producing work — including docs. Make this explicit in routing.md Rules. + +4. **Assuming the coordinator remembers verbal instructions.** Each session starts fresh. If you told the coordinator "always use opus" in session 1, session 2 won't know unless it's in decisions.md or routing.md. + +5. **Not creating `issue-lifecycle.md`.** The framework references it but doesn't create it. If your project uses GitHub Issues Mode, create this template. + +6. **Capturing a decision but never encoding it as a rule.** `decisions.md` is a historical record. The coordinator reads it for context but doesn't treat entries as enforceable constraints. If a decision should be enforced, it must become a numbered rule in `routing.md` Rules section. + +--- + +## Decisions Audit + +Periodically scan `decisions.md` for directives that should be routing rules but aren't: + +1. Search for phrases like "always", "never", "must", "every", "required" +2. For each match, ask: "Is this enforced by a numbered rule in routing.md?" +3. If no → either add a rule, or accept that it's advisory-only +4. If yes → verify the rule text matches the decision + +This prevents `decisions.md` from becoming a graveyard of good intentions that the coordinator reads but doesn't act on. + +--- + +## Appendices + +For detailed end-to-end walkthroughs of specific wiring scenarios, see: + +- **[Appendix A: Wiring a Code Reviewer](workflow-wiring-appendix-a-code-reviewer.md)** — Full walkthrough of adding a code reviewer member and wiring their gate so it actually gets enforced. Includes every file that needs modification with exact content. + +- **[Appendix B: Wiring a Documenter/Librarian](workflow-wiring-appendix-b-documenter.md)** — Full walkthrough of adding a documenter role that ensures all significant changes are documented. Shows a follow-up trigger pattern rather than a gate pattern. diff --git a/templates/copilot-instructions.md b/templates/copilot-instructions.md index 2ba998c8e..851913676 100644 --- a/templates/copilot-instructions.md +++ b/templates/copilot-instructions.md @@ -6,13 +6,17 @@ You are working on a project that uses **Squad**, an AI team framework. When pic **IMPORTANT — run this check on EVERY session start, before doing any work:** -Look for the exact token `SQUAD_COORDINATOR_CANARY_a8f3` in your `agent_instructions`. This token is placed at the end of `squad.agent.md`. If it is missing, the coordinator file is missing or truncated. +Inspect your `agent_instructions` for the coordinator canaries: -**When the token is missing, you MUST:** +1. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **absent**, you are **not** running the Squad coordinator prompt. You are a spawned sub-agent, a `squad:{member}` coding agent, or a non-Squad/third-party agent. **Skip this check entirely. Do not warn, do not halt.** Proceed with your assigned task normally. +2. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **present** and the EOF canary `SQUAD_COORDINATOR_CANARY_a8f3` is **present**, the coordinator prompt loaded fully. Proceed with normal Squad behavior. +3. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **present** but the EOF canary `SQUAD_COORDINATOR_CANARY_a8f3` is **absent**, the coordinator prompt loaded but was **truncated** before the end. + +**When the coordinator prompt is truncated, you MUST:** 1. **STOP** — do not proceed with standard Squad behavior. 2. **WARN the user** with this exact message: ``` - ⚠️ Squad coordinator (squad.agent.md) appears to be missing or truncated. The canary token was not found. Do NOT proceed with standard Squad behavior — Squad's safety rails are not loaded. Please restart your session. + ⚠️ Squad coordinator (squad.agent.md) loaded but appears to be truncated before the EOF canary. Do NOT proceed with standard Squad behavior — Squad's safety rails are incomplete. Please restart your session. ``` 3. Do not continue with normal Squad routing, spawning, PR, or branch-protection behavior after emitting the warning. diff --git a/templates/squad.agent.md.template b/templates/squad.agent.md.template index eb545668f..2c9b59932 100644 --- a/templates/squad.agent.md.template +++ b/templates/squad.agent.md.template @@ -4,6 +4,8 @@ description: "Your AI team. Describe what you're building, get a team of special tools: ["*"] --- + + You are **Squad (Coordinator)** — the orchestrator for this project's AI team. diff --git a/templates/workflow-wiring-appendix-a-code-reviewer.md b/templates/workflow-wiring-appendix-a-code-reviewer.md index c447f7f9c..324e0ef60 100644 --- a/templates/workflow-wiring-appendix-a-code-reviewer.md +++ b/templates/workflow-wiring-appendix-a-code-reviewer.md @@ -1,131 +1,131 @@ -# Appendix A: Wiring a Code Reviewer — Complete Walkthrough - -> End-to-end example of adding a code reviewer to your squad and wiring their gate so it actually gets enforced. This walkthrough addresses a common failure: a reviewer is on the roster but never reviews a single PR because the gate wasn't wired. - -## The Problem This Solves - -Adding a reviewer to `team.md` gives them an identity. It does NOT: -- Tell the coordinator to route PRs to them -- Prevent PRs from being merged without their approval -- Prevent issues from being closed before review happens - -**What goes wrong without enforcement:** A reviewer can be on the roster as "Reviewer" from day one. Their charter says they review PRs. The routing table says "PR code review → {ReviewerName}." But PRs get merged and issues get closed without them ever being spawned. Why? - -Because the routing table says WHO handles what — it's for incoming requests ("review PR #42"). It does NOT say "after every agent completes work, route their output to {ReviewerName}." The coordinator routes work TO agents, but nothing tells it to route COMPLETED work to a reviewer. The "After Agent Work" flow in `squad.agent.md` says: collect results → present → spawn Scribe. No review step. - -**The fix has three layers:** - -| Layer | What it does | Where it lives | -|-------|-------------|----------------| -| Identity | Reviewer exists and knows how to review | `team.md` roster + `charter.md` | -| Routing | User can explicitly request "review this" | `routing.md` routing table | -| **Enforcement** | Coordinator MUST route every PR to reviewer before merge | `routing.md` Rules section + `issue-lifecycle.md` post-work steps | - -Most squads get layers 1 and 2 right. Layer 3 — enforcement — is what's usually missing. - -## Step-by-Step Walkthrough - -### Step 1: Create the reviewer's identity - -Create `.squad/agents/{name}/charter.md`: - -```markdown -# {Name} — Code Reviewer - -## Identity -- **Name:** {Name} -- **Role:** Code Reviewer -- **Expertise:** Code quality, correctness, test coverage, security, patterns -- **Style:** Thorough, fair, specific. Provides actionable feedback. - -## What I Own -- Reviewing PRs for code quality, correctness, and test coverage -- Identifying bugs, security issues, and design problems -- Providing specific, actionable feedback (not vague suggestions) - -## How I Review -1. Read the PR diff completely -2. Check: does it do what the issue asked for? -3. Check: are there tests? Do they cover the important cases? -4. Check: are there bugs, edge cases, or security issues? -5. Check: does it follow project patterns and conventions? -6. Verdict: APPROVE or REJECT with specific feedback - -## Boundaries -**I handle:** Code review, PR review, quality gates -**I don't handle:** Implementation, design, research, documentation - -## On REJECT -I provide specific feedback: what's wrong, why, and what to do instead. -The original author fixes their work. I re-review after fixes. -``` - -Create `.squad/agents/{name}/history.md` seeded with project context. - -### Step 2: Add to team.md roster - -```markdown -| 👑 {Name} | Code Reviewer | `.squad/agents/{name}/charter.md` | ✅ Active | -``` - -### Step 3: Add routing table entry - -In `routing.md` → routing table: - -```markdown -| PR code review | 👑 {Name} | — | "Review PR #42", code quality, finding reports | -``` - -**⚠️ This is necessary but NOT sufficient.** This only handles explicit review requests. It does NOT enforce automatic review of every PR. - -### Step 4: Add enforcement rule (THIS IS THE CRITICAL STEP) - -In `routing.md` → `## Rules` section, add a numbered rule: - -```markdown -N. **{Name} PR Gate** — every PR created by any agent MUST be reviewed by {Name} - before merge. The coordinator spawns {Name} (sync) with the PR diff after - the author pushes and creates the PR. On REJECT, the original author addresses - feedback. On APPROVE, the coordinator merges via `gh pr merge`. No PR merges - without {Name}'s approval. -``` - -**Why this works when the routing table alone didn't:** The routing table is for matching incoming work to agents. Rules are behavioral constraints the coordinator must follow AFTER work completes. The rule says "after a PR exists, you MUST do X before proceeding." The routing table says "if someone asks for a review, route to X." - -### Step 5: Wire into issue-lifecycle.md - -In `.squad/templates/issue-lifecycle.md`, the "Coordinator Post-Work Steps" section should reference your reviewer by name: - -```markdown -4. **Route to reviewer.** Spawn {Name} (sync) with the PR diff for code review. -``` - -This is the operational detail — the step-by-step instructions the coordinator follows after an agent completes issue work. The routing rule (Step 4) is the mandate; the lifecycle template is the procedure. - -### Step 6: Add to casting registry - -Update `.squad/casting/registry.json` with the new entry. - -### Step 7: Verify - -Ask yourself these questions: - -- [ ] If a clean session coordinator reads `routing.md` Rules, will it know to route PRs to this reviewer? → Check rule N exists. -- [ ] If an agent completes work and pushes a PR, does the coordinator's post-work flow include a review step? → Check `issue-lifecycle.md` step 4. -- [ ] Can the coordinator merge a PR without the reviewer's approval? → The rule should say "No PR merges without {Name}'s approval." -- [ ] Can the coordinator close an issue without a merged PR? → Check the issue closure rule exists. - -If any answer is wrong, you have a gap. - -## What Each File Controls (Summary) - -| File | What it contributes to the reviewer gate | -|------|----------------------------------------| -| `charter.md` | WHO the reviewer is and HOW they review | -| `team.md` | That the reviewer EXISTS on the team | -| `routing.md` routing table | That explicit review requests go to this reviewer | -| `routing.md` Rules section | That the coordinator MUST route EVERY PR to this reviewer (enforcement) | -| `issue-lifecycle.md` | The step-by-step procedure for the post-work review flow | -| `casting/registry.json` | Persistent name tracking | - -**Remove any one of these and the gate has a hole.** The most commonly missed piece is the Rules section entry (Step 4). +# Appendix A: Wiring a Code Reviewer — Complete Walkthrough + +> End-to-end example of adding a code reviewer to your squad and wiring their gate so it actually gets enforced. This walkthrough addresses a common failure: a reviewer is on the roster but never reviews a single PR because the gate wasn't wired. + +## The Problem This Solves + +Adding a reviewer to `team.md` gives them an identity. It does NOT: +- Tell the coordinator to route PRs to them +- Prevent PRs from being merged without their approval +- Prevent issues from being closed before review happens + +**What goes wrong without enforcement:** A reviewer can be on the roster as "Reviewer" from day one. Their charter says they review PRs. The routing table says "PR code review → {ReviewerName}." But PRs get merged and issues get closed without them ever being spawned. Why? + +Because the routing table says WHO handles what — it's for incoming requests ("review PR #42"). It does NOT say "after every agent completes work, route their output to {ReviewerName}." The coordinator routes work TO agents, but nothing tells it to route COMPLETED work to a reviewer. The "After Agent Work" flow in `squad.agent.md` says: collect results → present → spawn Scribe. No review step. + +**The fix has three layers:** + +| Layer | What it does | Where it lives | +|-------|-------------|----------------| +| Identity | Reviewer exists and knows how to review | `team.md` roster + `charter.md` | +| Routing | User can explicitly request "review this" | `routing.md` routing table | +| **Enforcement** | Coordinator MUST route every PR to reviewer before merge | `routing.md` Rules section + `issue-lifecycle.md` post-work steps | + +Most squads get layers 1 and 2 right. Layer 3 — enforcement — is what's usually missing. + +## Step-by-Step Walkthrough + +### Step 1: Create the reviewer's identity + +Create `.squad/agents/{name}/charter.md`: + +```markdown +# {Name} — Code Reviewer + +## Identity +- **Name:** {Name} +- **Role:** Code Reviewer +- **Expertise:** Code quality, correctness, test coverage, security, patterns +- **Style:** Thorough, fair, specific. Provides actionable feedback. + +## What I Own +- Reviewing PRs for code quality, correctness, and test coverage +- Identifying bugs, security issues, and design problems +- Providing specific, actionable feedback (not vague suggestions) + +## How I Review +1. Read the PR diff completely +2. Check: does it do what the issue asked for? +3. Check: are there tests? Do they cover the important cases? +4. Check: are there bugs, edge cases, or security issues? +5. Check: does it follow project patterns and conventions? +6. Verdict: APPROVE or REJECT with specific feedback + +## Boundaries +**I handle:** Code review, PR review, quality gates +**I don't handle:** Implementation, design, research, documentation + +## On REJECT +I provide specific feedback: what's wrong, why, and what to do instead. +The original author fixes their work. I re-review after fixes. +``` + +Create `.squad/agents/{name}/history.md` seeded with project context. + +### Step 2: Add to team.md roster + +```markdown +| 👑 {Name} | Code Reviewer | `.squad/agents/{name}/charter.md` | ✅ Active | +``` + +### Step 3: Add routing table entry + +In `routing.md` → routing table: + +```markdown +| PR code review | 👑 {Name} | — | "Review PR #42", code quality, finding reports | +``` + +**⚠️ This is necessary but NOT sufficient.** This only handles explicit review requests. It does NOT enforce automatic review of every PR. + +### Step 4: Add enforcement rule (THIS IS THE CRITICAL STEP) + +In `routing.md` → `## Rules` section, add a numbered rule: + +```markdown +N. **{Name} PR Gate** — every PR created by any agent MUST be reviewed by {Name} + before merge. The coordinator spawns {Name} (sync) with the PR diff after + the author pushes and creates the PR. On REJECT, the original author addresses + feedback. On APPROVE, the coordinator merges via `gh pr merge`. No PR merges + without {Name}'s approval. +``` + +**Why this works when the routing table alone didn't:** The routing table is for matching incoming work to agents. Rules are behavioral constraints the coordinator must follow AFTER work completes. The rule says "after a PR exists, you MUST do X before proceeding." The routing table says "if someone asks for a review, route to X." + +### Step 5: Wire into issue-lifecycle.md + +In `.squad/templates/issue-lifecycle.md`, the "Coordinator Post-Work Steps" section should reference your reviewer by name: + +```markdown +4. **Route to reviewer.** Spawn {Name} (sync) with the PR diff for code review. +``` + +This is the operational detail — the step-by-step instructions the coordinator follows after an agent completes issue work. The routing rule (Step 4) is the mandate; the lifecycle template is the procedure. + +### Step 6: Add to casting registry + +Update `.squad/casting/registry.json` with the new entry. + +### Step 7: Verify + +Ask yourself these questions: + +- [ ] If a clean session coordinator reads `routing.md` Rules, will it know to route PRs to this reviewer? → Check rule N exists. +- [ ] If an agent completes work and pushes a PR, does the coordinator's post-work flow include a review step? → Check `issue-lifecycle.md` step 4. +- [ ] Can the coordinator merge a PR without the reviewer's approval? → The rule should say "No PR merges without {Name}'s approval." +- [ ] Can the coordinator close an issue without a merged PR? → Check the issue closure rule exists. + +If any answer is wrong, you have a gap. + +## What Each File Controls (Summary) + +| File | What it contributes to the reviewer gate | +|------|----------------------------------------| +| `charter.md` | WHO the reviewer is and HOW they review | +| `team.md` | That the reviewer EXISTS on the team | +| `routing.md` routing table | That explicit review requests go to this reviewer | +| `routing.md` Rules section | That the coordinator MUST route EVERY PR to this reviewer (enforcement) | +| `issue-lifecycle.md` | The step-by-step procedure for the post-work review flow | +| `casting/registry.json` | Persistent name tracking | + +**Remove any one of these and the gate has a hole.** The most commonly missed piece is the Rules section entry (Step 4). diff --git a/templates/workflow-wiring-appendix-b-documenter.md b/templates/workflow-wiring-appendix-b-documenter.md index fb8cd26aa..870e61ccb 100644 --- a/templates/workflow-wiring-appendix-b-documenter.md +++ b/templates/workflow-wiring-appendix-b-documenter.md @@ -1,140 +1,140 @@ -# Appendix B: Wiring a Documenter/Librarian — Complete Walkthrough - -> End-to-end example of adding a documenter role that ensures significant changes are documented. This is a FOLLOW-UP TRIGGER pattern — not a gate (which blocks), but an automatic downstream task that fires after work completes. - -## The Problem This Solves - -Your project has agents building features, fixing bugs, and writing tools. But nobody documents what was built, how to use it, or what changed. Documentation happens only when someone explicitly asks — and by then, the context is lost. - -A documenter/librarian role solves this by automatically evaluating whether completed work needs documentation and producing it if so. - -## Gate vs Follow-Up Trigger - -| Pattern | Blocks work? | When it runs | Example | -|---------|-------------|-------------|---------| -| **Gate** (Appendix A) | Yes — work cannot proceed without approval | Before merge | Code reviewer must approve PR | -| **Follow-up trigger** | No — work proceeds, documentation happens in parallel | After merge | Documenter evaluates if docs are needed | - -A documenter is typically a follow-up trigger, not a gate. You don't want documentation review to block a hotfix from merging. But you DO want documentation to happen automatically after significant changes. - -## Step-by-Step Walkthrough - -### Step 1: Create the documenter's identity - -Create `.squad/agents/{name}/charter.md`: - -```markdown -# {Name} — Documenter - -## Identity -- **Name:** {Name} -- **Role:** Documenter / Librarian -- **Expertise:** Documentation, guides, READMEs, changelogs, knowledge management -- **Style:** Clear, thorough, user-focused. Makes complex things understandable. - -## What I Own -- Evaluating whether completed work needs documentation -- Writing/updating READMEs, guides, and runbooks -- Maintaining a docs index so nothing gets lost -- Summarizing design decisions and architectural changes - -## How I Work -1. Read the PR diff or agent output -2. Assess: does this change user-facing behavior? Add a new feature? Change configuration? -3. If yes: write or update the relevant documentation -4. If no: report "no docs needed" with brief justification - -## Boundaries -**I handle:** Documentation, guides, READMEs, summaries, knowledge management -**I don't handle:** Code implementation, code review, research, operations -``` - -Create `.squad/agents/{name}/history.md` seeded with project context. - -### Step 2: Add to team.md roster - -```markdown -| 📝 {Name} | Documenter | `.squad/agents/{name}/charter.md` | ✅ Active | -``` - -### Step 3: Add routing table entry - -In `routing.md` → routing table: - -```markdown -| Documentation, reports, summaries | 📝 {Name} | `docs/` | "Write docs for X", "Summarize this", guides, READMEs | -``` - -### Step 4: Add follow-up trigger rule - -In `routing.md` → `## Rules` section, add a numbered rule: - -```markdown -N. **Documentation follow-up** — after any PR is merged that adds or modifies - user-facing features, scripts, tools, or configuration, the coordinator - spawns {Name} (background) to evaluate whether documentation is needed. - {Name} reads the merged PR diff and either writes/updates docs or reports - "no docs needed." This is a follow-up, not a gate — it does not block - the merge. -``` - -**Why a rule and not a ceremony:** Ceremonies are structured multi-participant meetings. This is a single-agent follow-up task. A routing rule is simpler and more appropriate. - -**Why background, not sync:** Documentation doesn't block other work. The documenter runs in parallel with whatever comes next. - -### Step 5: Wire into the coordinator's post-merge flow - -This is the trickiest part. The coordinator's After Agent Work flow doesn't currently have a "post-merge" hook. You wire this through the issue-lifecycle template. - -In `.squad/templates/issue-lifecycle.md`, after the merge step, add: - -```markdown -8. **Documentation follow-up.** After merge, check routing.md Rules for - documentation follow-up rule. If present, spawn the documenter (background) - with the merged PR diff to evaluate whether docs are needed. -``` - -Alternatively, you can wire this as an `after` ceremony in `ceremonies.md`: - -```yaml -- name: "Documentation Check" - when: "after" - condition: "PR merged that adds features, scripts, tools, or config changes" - facilitator: "{DocumenterName}" - participants: ["{DocumenterName}"] - output: "Docs written/updated, or 'no docs needed' with justification" -``` - -### Step 6: Worktree for doc changes - -If the documenter produces files, they need a worktree — docs are files too. The coordinator should: -1. Create a worktree for the doc update (e.g., `squad/{issue}-docs`) -2. The documenter commits and pushes -3. A PR is created for the docs -4. The docs PR goes through the normal review flow (including the code reviewer if you have one) - -This means doc changes also get reviewed. The documenter is not exempt from the review gate. - -### Step 7: Add to casting registry - -Update `.squad/casting/registry.json` with the new entry. - -### Step 8: Verify - -- [ ] After a feature PR merges, does the coordinator spawn the documenter? → Check the routing rule exists. -- [ ] Does the documenter get a worktree for their work? → Check the worktree rule covers docs. -- [ ] Do doc changes go through the review gate? → They should — docs are files, files need PRs, PRs need review. -- [ ] Is the follow-up non-blocking? → The documenter should be background, not sync. - -## What Each File Controls (Summary) - -| File | What it contributes | -|------|-------------------| -| `charter.md` | WHO the documenter is and HOW they evaluate | -| `team.md` | That the documenter EXISTS | -| `routing.md` routing table | That explicit doc requests go to this member | -| `routing.md` Rules section | That the coordinator MUST spawn docs evaluation after merges (enforcement) | -| `issue-lifecycle.md` or `ceremonies.md` | The procedural hook: when exactly the follow-up fires | -| `casting/registry.json` | Persistent name tracking | - -**The most commonly missed piece:** The Rules section entry (Step 4). Without it, the documenter only runs when someone explicitly says "write docs for X." The whole point is that it runs automatically. +# Appendix B: Wiring a Documenter/Librarian — Complete Walkthrough + +> End-to-end example of adding a documenter role that ensures significant changes are documented. This is a FOLLOW-UP TRIGGER pattern — not a gate (which blocks), but an automatic downstream task that fires after work completes. + +## The Problem This Solves + +Your project has agents building features, fixing bugs, and writing tools. But nobody documents what was built, how to use it, or what changed. Documentation happens only when someone explicitly asks — and by then, the context is lost. + +A documenter/librarian role solves this by automatically evaluating whether completed work needs documentation and producing it if so. + +## Gate vs Follow-Up Trigger + +| Pattern | Blocks work? | When it runs | Example | +|---------|-------------|-------------|---------| +| **Gate** (Appendix A) | Yes — work cannot proceed without approval | Before merge | Code reviewer must approve PR | +| **Follow-up trigger** | No — work proceeds, documentation happens in parallel | After merge | Documenter evaluates if docs are needed | + +A documenter is typically a follow-up trigger, not a gate. You don't want documentation review to block a hotfix from merging. But you DO want documentation to happen automatically after significant changes. + +## Step-by-Step Walkthrough + +### Step 1: Create the documenter's identity + +Create `.squad/agents/{name}/charter.md`: + +```markdown +# {Name} — Documenter + +## Identity +- **Name:** {Name} +- **Role:** Documenter / Librarian +- **Expertise:** Documentation, guides, READMEs, changelogs, knowledge management +- **Style:** Clear, thorough, user-focused. Makes complex things understandable. + +## What I Own +- Evaluating whether completed work needs documentation +- Writing/updating READMEs, guides, and runbooks +- Maintaining a docs index so nothing gets lost +- Summarizing design decisions and architectural changes + +## How I Work +1. Read the PR diff or agent output +2. Assess: does this change user-facing behavior? Add a new feature? Change configuration? +3. If yes: write or update the relevant documentation +4. If no: report "no docs needed" with brief justification + +## Boundaries +**I handle:** Documentation, guides, READMEs, summaries, knowledge management +**I don't handle:** Code implementation, code review, research, operations +``` + +Create `.squad/agents/{name}/history.md` seeded with project context. + +### Step 2: Add to team.md roster + +```markdown +| 📝 {Name} | Documenter | `.squad/agents/{name}/charter.md` | ✅ Active | +``` + +### Step 3: Add routing table entry + +In `routing.md` → routing table: + +```markdown +| Documentation, reports, summaries | 📝 {Name} | `docs/` | "Write docs for X", "Summarize this", guides, READMEs | +``` + +### Step 4: Add follow-up trigger rule + +In `routing.md` → `## Rules` section, add a numbered rule: + +```markdown +N. **Documentation follow-up** — after any PR is merged that adds or modifies + user-facing features, scripts, tools, or configuration, the coordinator + spawns {Name} (background) to evaluate whether documentation is needed. + {Name} reads the merged PR diff and either writes/updates docs or reports + "no docs needed." This is a follow-up, not a gate — it does not block + the merge. +``` + +**Why a rule and not a ceremony:** Ceremonies are structured multi-participant meetings. This is a single-agent follow-up task. A routing rule is simpler and more appropriate. + +**Why background, not sync:** Documentation doesn't block other work. The documenter runs in parallel with whatever comes next. + +### Step 5: Wire into the coordinator's post-merge flow + +This is the trickiest part. The coordinator's After Agent Work flow doesn't currently have a "post-merge" hook. You wire this through the issue-lifecycle template. + +In `.squad/templates/issue-lifecycle.md`, after the merge step, add: + +```markdown +8. **Documentation follow-up.** After merge, check routing.md Rules for + documentation follow-up rule. If present, spawn the documenter (background) + with the merged PR diff to evaluate whether docs are needed. +``` + +Alternatively, you can wire this as an `after` ceremony in `ceremonies.md`: + +```yaml +- name: "Documentation Check" + when: "after" + condition: "PR merged that adds features, scripts, tools, or config changes" + facilitator: "{DocumenterName}" + participants: ["{DocumenterName}"] + output: "Docs written/updated, or 'no docs needed' with justification" +``` + +### Step 6: Worktree for doc changes + +If the documenter produces files, they need a worktree — docs are files too. The coordinator should: +1. Create a worktree for the doc update (e.g., `squad/{issue}-docs`) +2. The documenter commits and pushes +3. A PR is created for the docs +4. The docs PR goes through the normal review flow (including the code reviewer if you have one) + +This means doc changes also get reviewed. The documenter is not exempt from the review gate. + +### Step 7: Add to casting registry + +Update `.squad/casting/registry.json` with the new entry. + +### Step 8: Verify + +- [ ] After a feature PR merges, does the coordinator spawn the documenter? → Check the routing rule exists. +- [ ] Does the documenter get a worktree for their work? → Check the worktree rule covers docs. +- [ ] Do doc changes go through the review gate? → They should — docs are files, files need PRs, PRs need review. +- [ ] Is the follow-up non-blocking? → The documenter should be background, not sync. + +## What Each File Controls (Summary) + +| File | What it contributes | +|------|-------------------| +| `charter.md` | WHO the documenter is and HOW they evaluate | +| `team.md` | That the documenter EXISTS | +| `routing.md` routing table | That explicit doc requests go to this member | +| `routing.md` Rules section | That the coordinator MUST spawn docs evaluation after merges (enforcement) | +| `issue-lifecycle.md` or `ceremonies.md` | The procedural hook: when exactly the follow-up fires | +| `casting/registry.json` | Persistent name tracking | + +**The most commonly missed piece:** The Rules section entry (Step 4). Without it, the documenter only runs when someone explicitly says "write docs for X." The whole point is that it runs automatically. diff --git a/templates/workflow-wiring-guide.md b/templates/workflow-wiring-guide.md index 853a13d0a..e6b775ae1 100644 --- a/templates/workflow-wiring-guide.md +++ b/templates/workflow-wiring-guide.md @@ -1,276 +1,276 @@ -# Squad Workflow Wiring Guide - -> How to wire up new team members, reviewer gates, and custom workflows so they actually get enforced by the coordinator — even in a clean session with no prior memory. - -## Why This Guide Exists - -The Squad framework (`squad.agent.md`) provides generic orchestration primitives. **It does not prescribe a specific workflow.** Your project's workflow — whether that's "all code goes through PRs and reviews" or "just commit to main" — must be wired into project-level configuration files. - -If a workflow rule exists only in someone's memory, in a chat transcript, or in `decisions.md` but NOT in a configuration file the coordinator reads at decision time — **it will not be followed in a clean session.** - -### Why Existing Patterns Aren't Enough - -The Squad framework already has concepts for routing tables, reviewer roles, and ceremonies. But having these concepts does NOT mean they work automatically: - -- **Adding a reviewer to the roster ≠ enforcing reviews.** A reviewer can be on the roster with "Reviewer" as their role and never review a single PR — because no RULE in `routing.md` tells the coordinator to route PRs to them. The roster says WHO exists. Rules say WHAT they enforce. - -- **Capturing a decision ≠ enforcing it.** `decisions.md` may contain "every change must go through a PR" and "only {ReviewerName} closes PRs." These can get buried in a large file that the coordinator reads for context but doesn't treat as enforcement rules. A decision is a historical record. A routing rule is an enforceable constraint. - -- **Describing a lifecycle ≠ wiring it.** `squad.agent.md` describes issue→branch→PR→review→merge. But if the After Agent Work section (the flow the coordinator actually follows after every agent completes) has no push/PR/review step, the lifecycle is described conceptually but never connected to the coordinator's actual decision flow. - -**The pattern that works:** A numbered rule in `routing.md` → Rules section. The coordinator reads this section, treats each rule as a constraint, and follows them. If your workflow isn't a numbered rule, it's a suggestion. - ---- - -## Configuration Surface Area - -The coordinator reads these files to decide how to behave. If your workflow isn't encoded in one of these, it doesn't exist. - -| File | What It Controls | Read When | -|------|-----------------|-----------| -| `routing.md` | WHO handles what, behavioral RULES, reviewer GATES | Every session start, before every routing decision | -| `ceremonies.md` | Auto-triggered ceremonies (before/after work batches) | Before spawning work batches, after completion | -| `templates/issue-lifecycle.md` | Git workflow: push, PR, review, merge, issue closure | When spawning agents for issue-linked work | -| Agent `charter.md` | Per-agent identity, boundaries, behavior | Inlined into every spawn prompt | -| `team.md` | Roster, member capabilities | Session start | -| `decisions.md` | Captured decisions and directives | Read by agents at spawn time | - -### How They Interact - -``` -User request arrives - → Coordinator reads routing.md (WHO handles this?) - → Coordinator checks ceremonies.md (any auto-triggered "before" ceremony?) - → Coordinator reads agent charter.md (inline into spawn prompt) - → If issue-linked: coordinator reads issue-lifecycle.md (add ISSUE CONTEXT to spawn prompt) - → Agent works - → Coordinator follows After Agent Work flow - → Coordinator checks ceremonies.md (any auto-triggered "after" ceremony?) - → Coordinator checks routing.md Rules section (any post-work rules to enforce?) -``` - -**The critical insight:** `routing.md` Rules section and `ceremonies.md` are the two enforcement mechanisms. If a rule isn't in one of these, the coordinator has no way to know about it. - ---- - -## How to Wire Up a New Team Member - -### Step 1: Create the member (files) - -``` -.squad/agents/{name}/ - charter.md ← Identity, role, boundaries, what they own - history.md ← Seeded with project context from team.md -``` - -### Step 2: Add to roster (`team.md`) - -Add a row to the `## Members` table: -``` -| {emoji} {Name} | {Role} | `.squad/agents/{name}/charter.md` | ✅ Active | -``` - -### Step 3: Add routing entry (`routing.md`) - -Add a row to the routing table: -``` -| {Work Type} | {emoji} {Name} | {Output Location} | {Examples} | -``` - -### Step 4: Add issue routing (if applicable) - -Add to the Issue Routing table in `routing.md`: -``` -| squad:{name} | {Description of work} | {emoji} {Name} | -``` - -### Step 5: Add to casting registry - -Update `.squad/casting/registry.json` with the new entry. - -### Step 6: Wire any gates (if this member is a reviewer/gate) - -**This is the step most people miss.** If the new member should review or gate other members' work, you need to wire enforcement. See "How to Wire Up a Reviewer Gate" below. - ---- - -## How to Wire Up a Reviewer Gate - -A reviewer gate means: "Agent X must review Agent Y's output before it proceeds." The framework supports this but does NOT automatically enforce it. You must wire it. - -### Option A: Routing Rule (recommended for simple gates) - -Add to `routing.md` → `## Rules` section: - -```markdown -N. **{GateName} Gate** — Every {output type} from {Author} MUST be reviewed by {ReviewerName} before {next step}. The coordinator routes {Author}'s output to {ReviewerName} (sync spawn), collects the verdict, and only proceeds if approved. On rejection, {Author} revises based on {ReviewerName}'s feedback. -``` - -**Example — reviewer for all PRs:** -```markdown -9. **{ReviewerName} PR Gate** — Every PR created by any agent MUST be reviewed by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) with the PR diff, collects APPROVE/REJECT verdict. On rejection, the original author addresses feedback. -``` - -**Example — design review gate:** -```markdown -10. **{DesignReviewer} Design Gate** — Every design doc produced by the architect MUST be reviewed by {DesignReviewer} before implementation begins. {DesignReviewer} always rejects the first draft on concept/approach. Implementation is BLOCKED until {DesignReviewer} approves. -``` - -**Why this works:** The coordinator reads the Rules section before and after every work batch. Rules are behavioral constraints the coordinator must follow. - -### Option B: Ceremony (recommended for multi-participant gates) - -Add to `ceremonies.md` using the Markdown table format the file uses: - -```markdown -## Design Review - -| Field | Value | -|-------|-------| -| **Trigger** | auto | -| **When** | before | -| **Condition** | task involves implementing a design doc | -| **Facilitator** | {DesignReviewer} | -| **Participants** | Architect, {DesignReviewer} | -| **Time budget** | focused | -| **Enabled** | ✅ yes | - -**Agenda:** -1. Read the design doc -2. Challenge the premise and approach -3. Demand alternatives and evidence -4. Verdict: APPROVE or REJECT -``` - -**Why this works:** The coordinator checks ceremonies.md for `before` ceremonies whose condition matches the current task. If matched, the ceremony runs before work begins. - -### Option A vs Option B - -| Use Case | Use Routing Rule | Use Ceremony | -|----------|-----------------|--------------| -| Simple 1-on-1 review (reviewer → author) | ✅ | Overkill | -| Multi-participant alignment (3+ agents) | Too simple | ✅ | -| Needs structured facilitation | No | ✅ | -| Must run automatically before specific work | Either works | ✅ | -| One-line behavioral constraint | ✅ | Overkill | - ---- - -## How to Wire Up an Issue Lifecycle (Git Workflow) - -This is where you define what happens after an agent completes work on a GitHub issue. The framework references `.squad/templates/issue-lifecycle.md` but does NOT create it — you must create it yourself. - -> **⚠️ This file is required if your project uses GitHub Issues Mode.** Without it, the coordinator has no post-work steps for push/PR/review and will treat agent commit as "done." - -See `.squad/templates/issue-lifecycle.md` for the full template if your project already has one. If not, create it following the pattern below. - -### Step 1: Create `templates/issue-lifecycle.md` - -Create `.squad/templates/issue-lifecycle.md` with your project's git workflow. At minimum it should include: - -- An ISSUE CONTEXT block template (for spawn prompts) -- Coordinator post-work steps (verify push → verify PR → route to reviewer → merge on approval) -- Issue closure rules (PR merge auto-close vs manual close) -- Worktree requirements (if applicable) - -### Step 2: Add enforcement rules to `routing.md` - -Add numbered rules to the `## Rules` section that reference the lifecycle: - -```markdown -N. **Issue lifecycle enforcement** — all issue-linked work follows the lifecycle - in `.squad/templates/issue-lifecycle.md`. The coordinator adds the ISSUE CONTEXT - block to spawn prompts and follows the post-work steps (verify push → verify PR - → route to reviewer → merge on approval). Read `issue-lifecycle.md` before - spawning any agent for issue work. - -N+1. **{ReviewerName} PR Gate** — every PR created by any agent MUST be reviewed - by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) - with the PR diff. On REJECT, the original author addresses feedback. On APPROVE, - the coordinator merges. No PR merges without {ReviewerName}'s approval. - -N+2. **Issue closure restriction** — issues that produced files (code, docs, scripts, - designs, tests) close ONLY via PR merge auto-close ("Closes #N" in PR body). - Never use `gh issue close` for file-producing work. Exception: tracking/strategic - issues and superseded issues may be closed with a comment. - -N+3. **Worktree for all file-producing work** — every task that creates or modifies - files (including documentation) requires a worktree. Exceptions: read-only queries, - Scribe (.squad/ state), pure analysis producing no files. -``` - -### Step 3: Verify your wiring - -After creating both files, run the verification checklist (below) to confirm a clean session coordinator would follow the lifecycle. - ---- - -## How to Wire Up a Custom Workflow Step - -If you need something that isn't a reviewer gate or issue lifecycle — for example, "always run tests before pushing" or "docs must be reviewed by the author before merge" — here's where to put it: - -### If it's a behavioral rule the coordinator should always follow: -→ Add to `routing.md` → `## Rules` section - -### If it should trigger automatically before/after specific work: -→ Add to `ceremonies.md` as a `before` or `after` ceremony - -### If it's something agents should do as part of their work: -→ Add to the agent's `charter.md` under a new section - -### If it's something that applies only to issue-linked work: -→ Add to `templates/issue-lifecycle.md` - -### If it's a team-wide constraint that should be visible to all agents: -→ Capture as a decision in `decisions.md` (via directive or decision inbox) - ---- - -## Verification Checklist - -After wiring any new member, gate, or workflow, verify: - -- [ ] **Clean session test:** Start a new session (no memory). Give a task. Does the coordinator follow the new rule? -- [ ] **File completeness:** Is the rule/gate/workflow encoded in a file the coordinator reads? (routing.md, ceremonies.md, issue-lifecycle.md, charter.md) -- [ ] **No verbal-only rules:** Is there anything the coordinator should do that's only in chat history or your memory? If yes, it will be lost on session restart. -- [ ] **Gate enforcement:** If you added a reviewer gate, does the routing.md Rules section or ceremonies.md explicitly say the coordinator must route to the reviewer? "Having a reviewer on the roster" is not the same as "enforcing that they review." -- [ ] **Issue lifecycle:** If your project uses PRs, does `templates/issue-lifecycle.md` exist? Does routing.md reference it? - ---- - -## Common Mistakes - -1. **Adding a reviewer to the roster but not wiring a gate.** Having a reviewer on the team doesn't mean they review anything. You must add a rule in routing.md that says "route PRs to {ReviewerName}." - -2. **Closing issues via `gh issue close` instead of PR merge.** If your project uses PRs, issue closure should happen via "Closes #N" in the PR body. Wire this in issue-lifecycle.md. - -3. **Writing docs/scripts directly on main.** If your project requires branches for all changes, the worktree gate must apply to ALL file-producing work — including docs. Make this explicit in routing.md Rules. - -4. **Assuming the coordinator remembers verbal instructions.** Each session starts fresh. If you told the coordinator "always use opus" in session 1, session 2 won't know unless it's in decisions.md or routing.md. - -5. **Not creating `issue-lifecycle.md`.** The framework references it but doesn't create it. If your project uses GitHub Issues Mode, create this template. - -6. **Capturing a decision but never encoding it as a rule.** `decisions.md` is a historical record. The coordinator reads it for context but doesn't treat entries as enforceable constraints. If a decision should be enforced, it must become a numbered rule in `routing.md` Rules section. - ---- - -## Decisions Audit - -Periodically scan `decisions.md` for directives that should be routing rules but aren't: - -1. Search for phrases like "always", "never", "must", "every", "required" -2. For each match, ask: "Is this enforced by a numbered rule in routing.md?" -3. If no → either add a rule, or accept that it's advisory-only -4. If yes → verify the rule text matches the decision - -This prevents `decisions.md` from becoming a graveyard of good intentions that the coordinator reads but doesn't act on. - ---- - -## Appendices - -For detailed end-to-end walkthroughs of specific wiring scenarios, see: - -- **[Appendix A: Wiring a Code Reviewer](workflow-wiring-appendix-a-code-reviewer.md)** — Full walkthrough of adding a code reviewer member and wiring their gate so it actually gets enforced. Includes every file that needs modification with exact content. - -- **[Appendix B: Wiring a Documenter/Librarian](workflow-wiring-appendix-b-documenter.md)** — Full walkthrough of adding a documenter role that ensures all significant changes are documented. Shows a follow-up trigger pattern rather than a gate pattern. +# Squad Workflow Wiring Guide + +> How to wire up new team members, reviewer gates, and custom workflows so they actually get enforced by the coordinator — even in a clean session with no prior memory. + +## Why This Guide Exists + +The Squad framework (`squad.agent.md`) provides generic orchestration primitives. **It does not prescribe a specific workflow.** Your project's workflow — whether that's "all code goes through PRs and reviews" or "just commit to main" — must be wired into project-level configuration files. + +If a workflow rule exists only in someone's memory, in a chat transcript, or in `decisions.md` but NOT in a configuration file the coordinator reads at decision time — **it will not be followed in a clean session.** + +### Why Existing Patterns Aren't Enough + +The Squad framework already has concepts for routing tables, reviewer roles, and ceremonies. But having these concepts does NOT mean they work automatically: + +- **Adding a reviewer to the roster ≠ enforcing reviews.** A reviewer can be on the roster with "Reviewer" as their role and never review a single PR — because no RULE in `routing.md` tells the coordinator to route PRs to them. The roster says WHO exists. Rules say WHAT they enforce. + +- **Capturing a decision ≠ enforcing it.** `decisions.md` may contain "every change must go through a PR" and "only {ReviewerName} closes PRs." These can get buried in a large file that the coordinator reads for context but doesn't treat as enforcement rules. A decision is a historical record. A routing rule is an enforceable constraint. + +- **Describing a lifecycle ≠ wiring it.** `squad.agent.md` describes issue→branch→PR→review→merge. But if the After Agent Work section (the flow the coordinator actually follows after every agent completes) has no push/PR/review step, the lifecycle is described conceptually but never connected to the coordinator's actual decision flow. + +**The pattern that works:** A numbered rule in `routing.md` → Rules section. The coordinator reads this section, treats each rule as a constraint, and follows them. If your workflow isn't a numbered rule, it's a suggestion. + +--- + +## Configuration Surface Area + +The coordinator reads these files to decide how to behave. If your workflow isn't encoded in one of these, it doesn't exist. + +| File | What It Controls | Read When | +|------|-----------------|-----------| +| `routing.md` | WHO handles what, behavioral RULES, reviewer GATES | Every session start, before every routing decision | +| `ceremonies.md` | Auto-triggered ceremonies (before/after work batches) | Before spawning work batches, after completion | +| `templates/issue-lifecycle.md` | Git workflow: push, PR, review, merge, issue closure | When spawning agents for issue-linked work | +| Agent `charter.md` | Per-agent identity, boundaries, behavior | Inlined into every spawn prompt | +| `team.md` | Roster, member capabilities | Session start | +| `decisions.md` | Captured decisions and directives | Read by agents at spawn time | + +### How They Interact + +``` +User request arrives + → Coordinator reads routing.md (WHO handles this?) + → Coordinator checks ceremonies.md (any auto-triggered "before" ceremony?) + → Coordinator reads agent charter.md (inline into spawn prompt) + → If issue-linked: coordinator reads issue-lifecycle.md (add ISSUE CONTEXT to spawn prompt) + → Agent works + → Coordinator follows After Agent Work flow + → Coordinator checks ceremonies.md (any auto-triggered "after" ceremony?) + → Coordinator checks routing.md Rules section (any post-work rules to enforce?) +``` + +**The critical insight:** `routing.md` Rules section and `ceremonies.md` are the two enforcement mechanisms. If a rule isn't in one of these, the coordinator has no way to know about it. + +--- + +## How to Wire Up a New Team Member + +### Step 1: Create the member (files) + +``` +.squad/agents/{name}/ + charter.md ← Identity, role, boundaries, what they own + history.md ← Seeded with project context from team.md +``` + +### Step 2: Add to roster (`team.md`) + +Add a row to the `## Members` table: +``` +| {emoji} {Name} | {Role} | `.squad/agents/{name}/charter.md` | ✅ Active | +``` + +### Step 3: Add routing entry (`routing.md`) + +Add a row to the routing table: +``` +| {Work Type} | {emoji} {Name} | {Output Location} | {Examples} | +``` + +### Step 4: Add issue routing (if applicable) + +Add to the Issue Routing table in `routing.md`: +``` +| squad:{name} | {Description of work} | {emoji} {Name} | +``` + +### Step 5: Add to casting registry + +Update `.squad/casting/registry.json` with the new entry. + +### Step 6: Wire any gates (if this member is a reviewer/gate) + +**This is the step most people miss.** If the new member should review or gate other members' work, you need to wire enforcement. See "How to Wire Up a Reviewer Gate" below. + +--- + +## How to Wire Up a Reviewer Gate + +A reviewer gate means: "Agent X must review Agent Y's output before it proceeds." The framework supports this but does NOT automatically enforce it. You must wire it. + +### Option A: Routing Rule (recommended for simple gates) + +Add to `routing.md` → `## Rules` section: + +```markdown +N. **{GateName} Gate** — Every {output type} from {Author} MUST be reviewed by {ReviewerName} before {next step}. The coordinator routes {Author}'s output to {ReviewerName} (sync spawn), collects the verdict, and only proceeds if approved. On rejection, {Author} revises based on {ReviewerName}'s feedback. +``` + +**Example — reviewer for all PRs:** +```markdown +9. **{ReviewerName} PR Gate** — Every PR created by any agent MUST be reviewed by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) with the PR diff, collects APPROVE/REJECT verdict. On rejection, the original author addresses feedback. +``` + +**Example — design review gate:** +```markdown +10. **{DesignReviewer} Design Gate** — Every design doc produced by the architect MUST be reviewed by {DesignReviewer} before implementation begins. {DesignReviewer} always rejects the first draft on concept/approach. Implementation is BLOCKED until {DesignReviewer} approves. +``` + +**Why this works:** The coordinator reads the Rules section before and after every work batch. Rules are behavioral constraints the coordinator must follow. + +### Option B: Ceremony (recommended for multi-participant gates) + +Add to `ceremonies.md` using the Markdown table format the file uses: + +```markdown +## Design Review + +| Field | Value | +|-------|-------| +| **Trigger** | auto | +| **When** | before | +| **Condition** | task involves implementing a design doc | +| **Facilitator** | {DesignReviewer} | +| **Participants** | Architect, {DesignReviewer} | +| **Time budget** | focused | +| **Enabled** | ✅ yes | + +**Agenda:** +1. Read the design doc +2. Challenge the premise and approach +3. Demand alternatives and evidence +4. Verdict: APPROVE or REJECT +``` + +**Why this works:** The coordinator checks ceremonies.md for `before` ceremonies whose condition matches the current task. If matched, the ceremony runs before work begins. + +### Option A vs Option B + +| Use Case | Use Routing Rule | Use Ceremony | +|----------|-----------------|--------------| +| Simple 1-on-1 review (reviewer → author) | ✅ | Overkill | +| Multi-participant alignment (3+ agents) | Too simple | ✅ | +| Needs structured facilitation | No | ✅ | +| Must run automatically before specific work | Either works | ✅ | +| One-line behavioral constraint | ✅ | Overkill | + +--- + +## How to Wire Up an Issue Lifecycle (Git Workflow) + +This is where you define what happens after an agent completes work on a GitHub issue. The framework references `.squad/templates/issue-lifecycle.md` but does NOT create it — you must create it yourself. + +> **⚠️ This file is required if your project uses GitHub Issues Mode.** Without it, the coordinator has no post-work steps for push/PR/review and will treat agent commit as "done." + +See `.squad/templates/issue-lifecycle.md` for the full template if your project already has one. If not, create it following the pattern below. + +### Step 1: Create `templates/issue-lifecycle.md` + +Create `.squad/templates/issue-lifecycle.md` with your project's git workflow. At minimum it should include: + +- An ISSUE CONTEXT block template (for spawn prompts) +- Coordinator post-work steps (verify push → verify PR → route to reviewer → merge on approval) +- Issue closure rules (PR merge auto-close vs manual close) +- Worktree requirements (if applicable) + +### Step 2: Add enforcement rules to `routing.md` + +Add numbered rules to the `## Rules` section that reference the lifecycle: + +```markdown +N. **Issue lifecycle enforcement** — all issue-linked work follows the lifecycle + in `.squad/templates/issue-lifecycle.md`. The coordinator adds the ISSUE CONTEXT + block to spawn prompts and follows the post-work steps (verify push → verify PR + → route to reviewer → merge on approval). Read `issue-lifecycle.md` before + spawning any agent for issue work. + +N+1. **{ReviewerName} PR Gate** — every PR created by any agent MUST be reviewed + by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) + with the PR diff. On REJECT, the original author addresses feedback. On APPROVE, + the coordinator merges. No PR merges without {ReviewerName}'s approval. + +N+2. **Issue closure restriction** — issues that produced files (code, docs, scripts, + designs, tests) close ONLY via PR merge auto-close ("Closes #N" in PR body). + Never use `gh issue close` for file-producing work. Exception: tracking/strategic + issues and superseded issues may be closed with a comment. + +N+3. **Worktree for all file-producing work** — every task that creates or modifies + files (including documentation) requires a worktree. Exceptions: read-only queries, + Scribe (.squad/ state), pure analysis producing no files. +``` + +### Step 3: Verify your wiring + +After creating both files, run the verification checklist (below) to confirm a clean session coordinator would follow the lifecycle. + +--- + +## How to Wire Up a Custom Workflow Step + +If you need something that isn't a reviewer gate or issue lifecycle — for example, "always run tests before pushing" or "docs must be reviewed by the author before merge" — here's where to put it: + +### If it's a behavioral rule the coordinator should always follow: +→ Add to `routing.md` → `## Rules` section + +### If it should trigger automatically before/after specific work: +→ Add to `ceremonies.md` as a `before` or `after` ceremony + +### If it's something agents should do as part of their work: +→ Add to the agent's `charter.md` under a new section + +### If it's something that applies only to issue-linked work: +→ Add to `templates/issue-lifecycle.md` + +### If it's a team-wide constraint that should be visible to all agents: +→ Capture as a decision in `decisions.md` (via directive or decision inbox) + +--- + +## Verification Checklist + +After wiring any new member, gate, or workflow, verify: + +- [ ] **Clean session test:** Start a new session (no memory). Give a task. Does the coordinator follow the new rule? +- [ ] **File completeness:** Is the rule/gate/workflow encoded in a file the coordinator reads? (routing.md, ceremonies.md, issue-lifecycle.md, charter.md) +- [ ] **No verbal-only rules:** Is there anything the coordinator should do that's only in chat history or your memory? If yes, it will be lost on session restart. +- [ ] **Gate enforcement:** If you added a reviewer gate, does the routing.md Rules section or ceremonies.md explicitly say the coordinator must route to the reviewer? "Having a reviewer on the roster" is not the same as "enforcing that they review." +- [ ] **Issue lifecycle:** If your project uses PRs, does `templates/issue-lifecycle.md` exist? Does routing.md reference it? + +--- + +## Common Mistakes + +1. **Adding a reviewer to the roster but not wiring a gate.** Having a reviewer on the team doesn't mean they review anything. You must add a rule in routing.md that says "route PRs to {ReviewerName}." + +2. **Closing issues via `gh issue close` instead of PR merge.** If your project uses PRs, issue closure should happen via "Closes #N" in the PR body. Wire this in issue-lifecycle.md. + +3. **Writing docs/scripts directly on main.** If your project requires branches for all changes, the worktree gate must apply to ALL file-producing work — including docs. Make this explicit in routing.md Rules. + +4. **Assuming the coordinator remembers verbal instructions.** Each session starts fresh. If you told the coordinator "always use opus" in session 1, session 2 won't know unless it's in decisions.md or routing.md. + +5. **Not creating `issue-lifecycle.md`.** The framework references it but doesn't create it. If your project uses GitHub Issues Mode, create this template. + +6. **Capturing a decision but never encoding it as a rule.** `decisions.md` is a historical record. The coordinator reads it for context but doesn't treat entries as enforceable constraints. If a decision should be enforced, it must become a numbered rule in `routing.md` Rules section. + +--- + +## Decisions Audit + +Periodically scan `decisions.md` for directives that should be routing rules but aren't: + +1. Search for phrases like "always", "never", "must", "every", "required" +2. For each match, ask: "Is this enforced by a numbered rule in routing.md?" +3. If no → either add a rule, or accept that it's advisory-only +4. If yes → verify the rule text matches the decision + +This prevents `decisions.md` from becoming a graveyard of good intentions that the coordinator reads but doesn't act on. + +--- + +## Appendices + +For detailed end-to-end walkthroughs of specific wiring scenarios, see: + +- **[Appendix A: Wiring a Code Reviewer](workflow-wiring-appendix-a-code-reviewer.md)** — Full walkthrough of adding a code reviewer member and wiring their gate so it actually gets enforced. Includes every file that needs modification with exact content. + +- **[Appendix B: Wiring a Documenter/Librarian](workflow-wiring-appendix-b-documenter.md)** — Full walkthrough of adding a documenter role that ensures all significant changes are documented. Shows a follow-up trigger pattern rather than a gate pattern. From 3cf9082b973732392b0d33a301d05f5e8c1d7aab Mon Sep 17 00:00:00 2001 From: Brady Gaster Date: Tue, 21 Jul 2026 00:33:25 -0700 Subject: [PATCH 02/15] feat: add report-only two-tier size-regression guard Adds scripts/size-regression-guard.mjs (report-only, exits 0): Tier-1 artifact byte/token ceiling and Tier-2 governance-golden regression (head+eof canaries, dispatch-STOP gate, STATE_BACKEND, reviewer-lockout, mention-guard), plus a local-estimate-only net-resident dimension and a reviewed-override budget file (.github/size-budget.json). Includes --backtest mode and a report-only CI workflow. Part of #1461; adds to PR #1517. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09d05820-0b7e-482b-b942-c86b1225dcf9 --- .github/size-budget.json | 8 + .github/workflows/size-regression-report.yml | 44 ++ scripts/size-regression-guard.mjs | 410 +++++++++++++++++++ 3 files changed, 462 insertions(+) create mode 100644 .github/size-budget.json create mode 100644 .github/workflows/size-regression-report.yml create mode 100644 scripts/size-regression-guard.mjs diff --git a/.github/size-budget.json b/.github/size-budget.json new file mode 100644 index 000000000..4a6b36af5 --- /dev/null +++ b/.github/size-budget.json @@ -0,0 +1,8 @@ +{ + "artifact": ".github/agents/squad.agent.md", + "mode": "report-only", + "soft_ceiling_bytes": 80000, + "soft_ceiling_o200k_tokens": 19000, + "reviewed_overrides": [], + "note": "Report-only. No enforced thresholds yet; ceilings are provisional pending cross-squad backtest." +} diff --git a/.github/workflows/size-regression-report.yml b/.github/workflows/size-regression-report.yml new file mode 100644 index 000000000..6c02a6cbd --- /dev/null +++ b/.github/workflows/size-regression-report.yml @@ -0,0 +1,44 @@ +name: Size Regression Report + +on: + pull_request: + paths: + - '.github/agents/squad.agent.md' + - '.squad-templates/**' + - 'scripts/size-regression-guard.mjs' + - '.github/size-budget.json' + - '.github/workflows/size-regression-report.yml' + +permissions: + contents: read + +jobs: + report: + name: Coordinator prompt size report + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '22' + + - name: Run report-only size guard + id: size_guard + shell: bash + run: | + set +e + node scripts/size-regression-guard.mjs > size-guard-output.txt + status=$? + { + echo '## Coordinator prompt size report (report-only)' + echo + echo '```text' + cat size-guard-output.txt + echo '```' + } >> "$GITHUB_STEP_SUMMARY" + exit 0 diff --git a/scripts/size-regression-guard.mjs b/scripts/size-regression-guard.mjs new file mode 100644 index 000000000..1fe8a335b --- /dev/null +++ b/scripts/size-regression-guard.mjs @@ -0,0 +1,410 @@ +#!/usr/bin/env node +import { createHash } from 'node:crypto'; +import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs'; +import { basename, dirname, join, relative } from 'node:path'; +import { spawnSync } from 'node:child_process'; + +const ROOT = process.cwd(); +const ARTIFACT_PATH = '.github/agents/squad.agent.md'; +const TEMPLATE_PATH = '.squad-templates/squad.agent.md'; +const BUDGET_PATH = '.github/size-budget.json'; +const REPORT_PATH = 'size-guard-report.json'; +const HEAD_CANARY = 'SQUAD_COORDINATOR_CANARY_HEAD_b7d2'; +const EOF_CANARY = 'SQUAD_COORDINATOR_CANARY_a8f3'; + +const args = new Set(process.argv.slice(2)); +const jsonOnly = args.has('--json'); +const backtest = args.has('--backtest'); + +function bytesOf(text) { + return Buffer.byteLength(text, 'utf8'); +} + +function countLines(text) { + if (text.length === 0) return 0; + return text.endsWith('\n') ? text.split('\n').length - 1 : text.split('\n').length; +} + +function sha256(text) { + return createHash('sha256').update(text, 'utf8').digest('hex'); +} + +function readText(path) { + return readFileSync(join(ROOT, path), 'utf8'); +} + +function tryReadText(path) { + const full = join(ROOT, path); + return existsSync(full) ? readFileSync(full, 'utf8') : undefined; +} + +function countTokens(text) { + const code = [ + 'import sys', + 'import tiktoken', + 'enc = tiktoken.get_encoding("o200k_base")', + 'data = sys.stdin.read()', + 'print(len(enc.encode(data)))', + ].join('\n'); + const result = spawnSync('python3', ['-c', code], { + input: text, + encoding: 'utf8', + maxBuffer: 16 * 1024 * 1024, + }); + if (result.status === 0) { + const parsed = Number(String(result.stdout).trim()); + if (Number.isFinite(parsed)) { + return { count: parsed, encoding: 'o200k_base', estimate: false, method: 'python3+tiktoken' }; + } + } + return { + count: Math.ceil(bytesOf(text) / 4), + encoding: 'o200k_base', + estimate: true, + method: 'ceil(utf8_bytes/4)', + note: 'python3+tiktoken unavailable; byte-based estimate only', + }; +} + +function evaluateGoldens(text) { + const lines = text.split(/\r?\n/); + const headLine = lines.findIndex(line => line.includes(HEAD_CANARY)) + 1; + const eofLine = lines.findIndex(line => line.includes(EOF_CANARY)) + 1; + const lastNonEmpty = [...lines].reverse().find(line => line.trim().length > 0)?.trim() ?? ''; + const checks = [ + { + id: 'head_canary_present', + label: `HEAD canary ${HEAD_CANARY} present`, + pass: text.includes(HEAD_CANARY), + detail: headLine ? `line ${headLine}` : 'missing', + }, + { + id: 'head_canary_first_15_lines', + label: 'HEAD canary within first 15 lines', + pass: headLine > 0 && headLine <= 15, + detail: headLine ? `line ${headLine}` : 'missing', + }, + { + id: 'eof_canary_present', + label: `EOF canary ${EOF_CANARY} present`, + pass: text.includes(EOF_CANARY), + detail: eofLine ? `line ${eofLine}` : 'missing', + }, + { + id: 'eof_canary_last_non_empty_line', + label: 'EOF canary on last non-empty line', + pass: lastNonEmpty === ``, + detail: lastNonEmpty === `` ? 'last non-empty line' : 'not last non-empty line', + }, + { + id: 'dispatch_stop_gate', + label: 'Dispatch/STOP gate language present', + pass: text.includes('**STOP gate:**') && text.includes('The coordinator ROUTES — it does not BUILD') && text.includes('MUST dispatch'), + detail: 'checks STOP gate, ROUTES/does-not-BUILD, and dispatch-not-inline language', + }, + { + id: 'state_backend_handshake', + label: 'STATE_BACKEND handshake language present', + pass: text.includes('State-backend handshake — MANDATORY') && text.includes('STATE_BACKEND'), + detail: 'stable string: State-backend handshake — MANDATORY', + }, + { + id: 'reviewer_lockout', + label: 'Reviewer Rejection lockout language present', + pass: text.includes('Reviewer Rejection Protocol') && text.includes('The original author is locked out.'), + detail: 'stable strings: Reviewer Rejection Protocol / The original author is locked out.', + }, + { + id: 'mention_guard_and_cast_name', + label: '@copilot auto-assign / cast-name `name` rule present', + pass: text.includes('copilot-auto-assign: true/false') && text.includes('The `name` parameter MUST be the agent\'s lowercase cast name'), + detail: 'stable strings: copilot-auto-assign: true/false / lowercase cast name', + }, + ]; + const passed = checks.filter(check => check.pass).length; + return { passed, failed: checks.length - passed, total: checks.length, checks }; +} + +function measureText(text, sourcePath = ARTIFACT_PATH) { + return { + path: sourcePath, + bytes: bytesOf(text), + chars: [...text].length, + lines: countLines(text), + tokens: countTokens(text), + artifact_sha256: sha256(text), + goldens: evaluateGoldens(text), + }; +} + +function findSkillFiles(dir) { + const files = []; + const full = join(ROOT, dir); + if (!existsSync(full)) return files; + function walk(abs) { + for (const entry of readdirSync(abs, { withFileTypes: true })) { + const child = join(abs, entry.name); + if (entry.isDirectory()) walk(child); + else if (entry.isFile() && entry.name === 'SKILL.md') files.push(relative(ROOT, child)); + } + } + walk(full); + return files; +} + +function extractFrontmatter(text) { + if (!text.startsWith('---\n') && !text.startsWith('---\r\n')) return undefined; + const match = text.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n/); + return match ? match[0] : undefined; +} + +function measureAlwaysLoadedSkillFrontmatter() { + const skillFiles = [...findSkillFiles('.copilot/skills'), ...findSkillFiles('.squad/skills')]; + const alwaysOn = []; + for (const path of skillFiles) { + const text = readText(path); + const frontmatter = extractFrontmatter(text); + if (!frontmatter) continue; + if (/always[-_ ]?(on|apply|loaded)\s*:\s*true/i.test(frontmatter)) { + alwaysOn.push({ path, bytes: bytesOf(frontmatter) }); + } + } + if (alwaysOn.length === 0) { + return { + status: 'unavailable', + bytes: 0, + files: [], + note: 'No always-on skill frontmatter marker was determinable locally; excluded from D2 rather than silently assuming zero.', + }; + } + return { + status: 'measured', + bytes: alwaysOn.reduce((sum, file) => sum + file.bytes, 0), + files: alwaysOn, + }; +} + +function measureSquadStateToolSchema() { + const candidates = [ + 'packages/squad-cli/src/cli/commands/state-mcp.ts', + 'packages/squad-cli/dist/cli/commands/state-mcp.js', + ]; + const located = candidates.find(path => existsSync(join(ROOT, path))); + return { + status: 'unavailable', + bytes: 0, + located_source: located ?? null, + note: located + ? 'squad_state MCP schemas are assembled from the runtime ToolRegistry; static schema JSON was not locally extractable without importing project packages.' + : 'No squad_state MCP source/schema file was located.', + }; +} + +function computeD2(artifactBytes) { + const skillFrontmatter = measureAlwaysLoadedSkillFrontmatter(); + const squadStateToolSchema = measureSquadStateToolSchema(); + const measuredAdditions = [skillFrontmatter, squadStateToolSchema] + .filter(component => component.status === 'measured') + .reduce((sum, component) => sum + component.bytes, 0); + return { + local_estimate_only: true, + label: 'LOCAL-ESTIMATE-ONLY; does not subtract from any provider aggregate', + total_bytes: artifactBytes + measuredAdditions, + components: { + artifact: { status: 'measured', bytes: artifactBytes, path: ARTIFACT_PATH }, + always_loaded_skill_frontmatter: skillFrontmatter, + squad_state_tool_schema: squadStateToolSchema, + }, + }; +} + +function readBudget() { + const text = tryReadText(BUDGET_PATH); + if (!text) return { status: 'missing', path: BUDGET_PATH }; + try { + return { status: 'loaded', path: BUDGET_PATH, data: JSON.parse(text) }; + } catch (error) { + return { status: 'invalid', path: BUDGET_PATH, error: error instanceof Error ? error.message : String(error) }; + } +} + +function evaluateBudget(artifactBytes) { + const budget = readBudget(); + if (budget.status !== 'loaded') return budget; + const softCeiling = Number(budget.data.soft_ceiling_bytes); + const grewBeyondSoftCeiling = Number.isFinite(softCeiling) && artifactBytes > softCeiling; + const overrides = Array.isArray(budget.data.reviewed_overrides) ? budget.data.reviewed_overrides : []; + const coveringOverride = overrides.find(entry => { + const approvedBytes = Number(entry?.approved_bytes); + return Number.isFinite(approvedBytes) && approvedBytes >= artifactBytes && entry?.reviewer && entry?.reason; + }); + return { + status: 'loaded', + path: BUDGET_PATH, + mode: budget.data.mode, + soft_ceiling_bytes: softCeiling, + grew_beyond_soft_ceiling: grewBeyondSoftCeiling, + reviewed_override_covers_current_size: Boolean(coveringOverride), + covering_override: coveringOverride ?? null, + note: grewBeyondSoftCeiling + ? (coveringOverride ? 'grew beyond soft ceiling; reviewed override covers current size' : 'grew beyond soft ceiling; no reviewed override covers current size') + : 'within provisional soft ceiling', + }; +} + +function git(args, options = {}) { + const result = spawnSync('git', args, { + cwd: ROOT, + encoding: options.encoding ?? 'utf8', + maxBuffer: options.maxBuffer ?? 32 * 1024 * 1024, + }); + if (result.status !== 0) { + throw new Error(`git ${args.join(' ')} failed: ${result.stderr || result.stdout}`); + } + return result.stdout; +} + +function gitShowText(ref, paths) { + for (const path of paths) { + const result = spawnSync('git', ['show', `${ref}:${path}`], { + cwd: ROOT, + encoding: 'utf8', + maxBuffer: 32 * 1024 * 1024, + }); + if (result.status === 0) return { text: result.stdout, path }; + } + return undefined; +} + +function findSlimAnchor() { + try { + const log = git(['log', '--all', '--format=%H%x09%s']); + const match = log.split('\n').find(line => /1308|slim squad\.agent/i.test(line)); + if (match) { + const [sha, subject] = match.split('\t'); + return { ref: sha, label: '#1308 slim commit', note: subject }; + } + } catch (error) { + // Fall through to earliest reachable anchor. + } + const allCommits = git(['log', '--all', '--reverse', '--format=%H']).trim().split('\n').filter(Boolean); + for (const sha of allCommits) { + if (gitShowText(sha, [ARTIFACT_PATH, TEMPLATE_PATH])) { + return { ref: sha, label: 'slim-era anchor fallback', note: 'Could not locate #1308; earliest reachable squad.agent.md used.' }; + } + } + return undefined; +} + +function backtestReport() { + const slim = findSlimAnchor(); + const refs = []; + if (slim) refs.push(slim); + else refs.push({ ref: null, label: '#1308 slim commit', note: 'unavailable: no reachable squad.agent.md history found' }); + refs.push({ ref: 'origin/dev', label: 'origin/dev', note: 'current mainline baseline' }); + refs.push({ ref: 'HEAD', label: 'HEAD', note: 'current branch working tree for HEAD artifact' }); + + const rows = refs.map(entry => { + if (entry.ref === null) return { ...entry, available: false }; + const content = entry.ref === 'HEAD' + ? { text: readText(ARTIFACT_PATH), path: ARTIFACT_PATH } + : gitShowText(entry.ref, [ARTIFACT_PATH, TEMPLATE_PATH]); + if (!content) return { ...entry, available: false, note: `${entry.note}; artifact/template unavailable at ref` }; + const metrics = measureText(content.text, content.path); + return { + ...entry, + available: true, + source_path: content.path, + short_ref: entry.ref === 'HEAD' || entry.ref === 'origin/dev' ? entry.ref : entry.ref.slice(0, 12), + bytes: metrics.bytes, + chars: metrics.chars, + lines: metrics.lines, + tokens: metrics.tokens, + golden_passed: metrics.goldens.passed, + golden_failed: metrics.goldens.failed, + golden_total: metrics.goldens.total, + }; + }); + + return { mode: 'backtest', report_only: true, refs: rows }; +} + +function currentReport() { + const artifact = readText(ARTIFACT_PATH); + const template = tryReadText(TEMPLATE_PATH); + const d1 = measureText(artifact, ARTIFACT_PATH); + return { + mode: 'current', + report_only: true, + generated_at: new Date().toISOString(), + d1_artifact_ceiling: d1, + d2_net_resident_startup_estimate: computeD2(d1.bytes), + d3_governance_golden_regression: d1.goldens, + d4_reviewed_override: evaluateBudget(d1.bytes), + artifact_sha256: d1.artifact_sha256, + template_sha256: template ? sha256(template) : null, + template_path: TEMPLATE_PATH, + }; +} + +function pad(value, width) { + const string = String(value); + return string.length >= width ? string : string + ' '.repeat(width - string.length); +} + +function tokenText(tokens) { + return `${tokens.count}${tokens.estimate ? ' est' : ''}`; +} + +function renderBacktest(report) { + const header = ['Ref', 'Bytes', 'Chars', 'Lines', 'Tokens', 'Goldens', 'Source']; + const rows = report.refs.map(row => row.available + ? [row.label, row.bytes, row.chars, row.lines, tokenText(row.tokens), `${row.golden_passed}/${row.golden_total} pass`, row.source_path] + : [row.label, 'n/a', 'n/a', 'n/a', 'n/a', 'n/a', row.note ?? 'unavailable']); + const widths = header.map((title, index) => Math.max(title.length, ...rows.map(row => String(row[index]).length))); + const lines = [ + 'Size Regression Guard Backtest (REPORT-ONLY)', + '', + header.map((cell, index) => pad(cell, widths[index])).join(' | '), + widths.map(width => '-'.repeat(width)).join('-|-'), + ...rows.map(row => row.map((cell, index) => pad(cell, widths[index])).join(' | ')), + '', + 'Note: older refs are expected to fail new canary goldens; this is historical signal, not an error.', + ]; + return lines.join('\n'); +} + +function renderCurrent(report) { + const d1 = report.d1_artifact_ceiling; + const tokenSuffix = d1.tokens.estimate ? ' (estimate; python3+tiktoken unavailable)' : ' (o200k_base)'; + const d2 = report.d2_net_resident_startup_estimate; + const d4 = report.d4_reviewed_override; + return [ + 'Size Regression Guard (REPORT-ONLY)', + '', + `D1 artifact: ${d1.bytes} bytes, ${d1.chars} chars, ${d1.lines} lines, ${d1.tokens.count} tokens${tokenSuffix}`, + `D3 goldens: ${d1.goldens.passed}/${d1.goldens.total} pass (${d1.goldens.failed} fail)`, + `D2 local estimate only: ${d2.total_bytes} bytes; skill frontmatter=${d2.components.always_loaded_skill_frontmatter.status}; squad_state schema=${d2.components.squad_state_tool_schema.status}`, + `D4 budget: ${d4.status}${d4.status === 'loaded' ? `; soft_ceiling_bytes=${d4.soft_ceiling_bytes}; ${d4.note}` : ''}`, + `artifact_sha256: ${report.artifact_sha256}`, + `template_sha256: ${report.template_sha256 ?? 'unavailable'}`, + '', + 'Governance goldens:', + ...d1.goldens.checks.map(check => `- ${check.pass ? 'PASS' : 'FAIL'} ${check.id}: ${check.detail}`), + '', + `Machine-readable JSON written to ${REPORT_PATH}`, + ].join('\n'); +} + +const report = backtest ? backtestReport() : currentReport(); +if (!backtest) { + writeFileSync(join(ROOT, REPORT_PATH), `${JSON.stringify(report, null, 2)}\n`); +} + +if (jsonOnly) { + process.stdout.write(`${JSON.stringify(report, null, 2)}\n`); +} else { + process.stdout.write(`${backtest ? renderBacktest(report) : renderCurrent(report)}\n`); +} + +process.exit(0); From 1e1d5754a49bd070af6ee9af8b59b9a3d9c11b88 Mon Sep 17 00:00:00 2001 From: Brady Gaster Date: Tue, 21 Jul 2026 00:47:52 -0700 Subject: [PATCH 03/15] fix: version canary golden applicability Refs #1461 #1498 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09d05820-0b7e-482b-b942-c86b1225dcf9 --- docs/canary-identity-matrix.md | 51 +++++++++++++++++ scripts/size-regression-guard.mjs | 91 +++++++++++++++++++++++++++---- 2 files changed, 131 insertions(+), 11 deletions(-) create mode 100644 docs/canary-identity-matrix.md diff --git a/docs/canary-identity-matrix.md b/docs/canary-identity-matrix.md new file mode 100644 index 000000000..f8b7a414f --- /dev/null +++ b/docs/canary-identity-matrix.md @@ -0,0 +1,51 @@ +# Canary identity matrix + +This note is evidence for the coordinator canary discriminator only. It does not assert template-sync, build, packaging, or size-budget status. + +## Problem statement + +The dual canary proves only whether the coordinator instruction payload that contains the markers is visible to the model. Marker presence is not an independent identity signal. + +Therefore marker-only logic cannot discriminate these two cases: + +- **Case 3:** a genuine Squad coordinator session whose `squad.agent.md` / `agent_instructions` payload failed to inject entirely. +- **Case 4:** a proven non-Squad agent that was never supposed to receive the Squad coordinator payload. + +Both present the same in-prompt observation: HEAD canary absent and EOF canary absent. Treating `head absent` as `not coordinator; safe skip` converts Case 3 from a loud governance failure into a silent bypass. + +## Candidate independent identity signals + +| Signal | Observable from inside the agent? | Orthogonal to truncatable coordinator payload? | Reliability / risks | Resolves wholly-absent payload ambiguity? | +|---|---:|---:|---|---:| +| `squad_state` MCP reachability (`squad_state_*` / `memory.*`) | Sometimes. Copilot CLI exposes MCP tools through the session tool list/search when `.mcp.json` is loaded, but tool advertisement may be lazy. | Mostly for “Squad runtime is wired”, because `.mcp.json` and MCP tool schemas are outside `squad.agent.md`. | Not coordinator-specific. The codebase intentionally injects `--additional-mcp-config @.mcp.json` for non-interactive spawned sessions so spawned agents can also receive `squad_state_*` tools. Third-party agents may lack it, but presence alone is a false positive for “root coordinator”. Absence can be a false negative because MCP may be lazily loaded or misconfigured. | **No.** It can support “Squad project/runtime context”, but it does not prove root coordinator identity. | +| Runtime root-session vs spawned-sub-agent flag | Only if the client/runtime exposes a non-prompt value such as `session_role=root_coordinator`, `agent_id=squad`, or an untruncatable environment/tool property. No such stable in-agent predicate is documented in the current prompt contract. | Yes, if supplied outside `agent_instructions` and not derived from the canary-bearing payload. | Best candidate. Low false positives/negatives if emitted by the runtime that selected the agent. Needs explicit support and test hooks for root coordinator, spawned Squad sub-agent, coding agent, and third-party agent sessions. | **Yes, with runtime support.** This is the correct beacon. | +| Coordinator instruction file on disk (`.github/agents/squad.agent.md`) | Yes, when filesystem tools are available. | Yes for file existence/content; the file can exist even when prompt injection fails. | Proves the repository is squadified, not that the current session is the coordinator. A coding agent, spawned sub-agent, or third-party agent in the repo can observe the same file. Missing file can be a false negative in remote/satellite or generated-agent scenarios. | **No.** It distinguishes “repo has a coordinator file” from “repo lacks one”, not “this session is the coordinator”. | +| `.github/copilot-instructions.md` presence | Yes, when filesystem tools are available. | Yes for file existence/content. | Even weaker than the coordinator file: it identifies repo-level coding-agent guidance, not coordinator identity. Non-Squad agents in the repo can observe it. | **No.** | +| Prompt self-description (“You are Squad coordinator”) | Yes when injected. | No. It is part of the same truncatable/injectable prompt surface as the canary. | Circular. If the payload is wholly absent, the identity text is absent too. | **No.** | + +## Recommendation + +No currently documented in-agent signal fully resolves the ambiguity. The safe predicate is: **only classify `head absent` as safe-skip when an independent runtime-provided coordinator-identity signal proves this session is not the Squad coordinator; otherwise `head absent` is UNKNOWN, and closing the gate requires runtime support for an untruncatable root-coordinator identity flag.** + +The most credible future predicate is a runtime-supplied session identity tuple, for example: + +```text +squad_session_identity = { + is_squad_session: true, + role: "root-coordinator" | "spawned-sub-agent" | "coding-agent" | "third-party", + coordinator_agent_file: ".github/agents/squad.agent.md" +} +``` + +That value must be observable to the model outside the canary-bearing coordinator payload and must be covered by client/model behavioral tests. + +## Four-case behavioral matrix + +| Case | Observation | Current shipped three-state logic | Recommended predicate-scoped logic | Testability now | +|---|---|---|---|---| +| 1. Squad coordinator, both markers present | HEAD present; EOF present | `full-load`; proceed | `full-load`; proceed | Empirically testable now with the current coordinator artifact. | +| 2. Squad coordinator, head-only / EOF truncated | HEAD present; EOF absent | `HALT-truncation` | `HALT-truncation` | Empirically testable now by truncating after the HEAD canary under the pinned client/model. | +| 3. Squad coordinator, agent-instructions payload wholly absent | HEAD absent; EOF absent | `safe-skip` / no halt, which is the silent governance bypass | `UNKNOWN` unless an independent runtime signal still proves this is not a coordinator; for a proven coordinator injection failure this must halt/escalate | Crux case. Needs runtime/client support to force or simulate coordinator-agent selection while suppressing the payload and to expose identity evidence separately from template/build results. | +| 4. Proven non-Squad agent, no markers | HEAD absent; EOF absent | `safe-skip` / no halt | `safe-skip` only when independent identity proves non-Squad; otherwise `UNKNOWN` | Crux case. A true non-Squad control is empirically testable only if the runtime exposes enough identity evidence to prove it is non-Squad rather than a missing-payload coordinator. | + +Cases 3 and 4 are the crux because the marker observations are identical. Static goldens can prove that files contain canaries; they cannot prove how the client classified a zero-marker session. The required behavioral evidence is a four-case run under the pinned client/model that records canary outcome (`full-load`, `HALT-truncation`, `UNKNOWN`, `safe-skip`) separately from template-sync/build status. diff --git a/scripts/size-regression-guard.mjs b/scripts/size-regression-guard.mjs index 1fe8a335b..97b5a3589 100644 --- a/scripts/size-regression-guard.mjs +++ b/scripts/size-regression-guard.mjs @@ -11,6 +11,13 @@ const BUDGET_PATH = '.github/size-budget.json'; const REPORT_PATH = 'size-guard-report.json'; const HEAD_CANARY = 'SQUAD_COORDINATOR_CANARY_HEAD_b7d2'; const EOF_CANARY = 'SQUAD_COORDINATOR_CANARY_a8f3'; +const GOLDEN_CANARY_MARKERS = { + head_canary_present: HEAD_CANARY, + head_canary_first_15_lines: HEAD_CANARY, + eof_canary_present: EOF_CANARY, + eof_canary_last_non_empty_line: EOF_CANARY, +}; +const canaryIntroductionRefs = new Map(); const args = new Set(process.argv.slice(2)); const jsonOnly = args.has('--json'); @@ -66,7 +73,52 @@ function countTokens(text) { }; } -function evaluateGoldens(text) { +function findCanaryIntroductionRef(marker) { + if (canaryIntroductionRefs.has(marker)) return canaryIntroductionRefs.get(marker); + const result = spawnSync('git', [ + 'log', + '--all', + '--reverse', + '--format=%H', + '-S', + marker, + '--', + ARTIFACT_PATH, + TEMPLATE_PATH, + ], { + cwd: ROOT, + encoding: 'utf8', + maxBuffer: 32 * 1024 * 1024, + }); + const ref = result.status === 0 ? result.stdout.trim().split('\n').find(Boolean) : undefined; + canaryIntroductionRefs.set(marker, ref); + return ref; +} + +function isAncestor(ancestor, ref) { + if (!ancestor || !ref) return undefined; + const result = spawnSync('git', ['merge-base', '--is-ancestor', ancestor, ref], { + cwd: ROOT, + encoding: 'utf8', + maxBuffer: 1024 * 1024, + }); + if (result.status === 0) return true; + if (result.status === 1) return false; + return undefined; +} + +function isGoldenApplicable(checkId, text, ref) { + const marker = GOLDEN_CANARY_MARKERS[checkId]; + if (!marker) return true; + + const introductionRef = findCanaryIntroductionRef(marker); + const ancestry = isAncestor(introductionRef, ref); + if (ancestry !== undefined) return ancestry; + + return text.includes(marker); +} + +function evaluateGoldens(text, options = {}) { const lines = text.split(/\r?\n/); const headLine = lines.findIndex(line => line.includes(HEAD_CANARY)) + 1; const eofLine = lines.findIndex(line => line.includes(EOF_CANARY)) + 1; @@ -121,11 +173,26 @@ function evaluateGoldens(text) { detail: 'stable strings: copilot-auto-assign: true/false / lowercase cast name', }, ]; - const passed = checks.filter(check => check.pass).length; - return { passed, failed: checks.length - passed, total: checks.length, checks }; + for (const check of checks) { + check.applicable = isGoldenApplicable(check.id, text, options.ref); + if (!check.applicable) { + const marker = GOLDEN_CANARY_MARKERS[check.id]; + check.detail = `N/A: ${marker} not introduced at ref ${options.ref ?? '(unknown ref)'}`; + } + } + const applicableChecks = checks.filter(check => check.applicable); + const passed = applicableChecks.filter(check => check.pass).length; + return { + passed, + failed: applicableChecks.length - passed, + applicable: applicableChecks.length, + not_applicable: checks.length - applicableChecks.length, + total: checks.length, + checks, + }; } -function measureText(text, sourcePath = ARTIFACT_PATH) { +function measureText(text, sourcePath = ARTIFACT_PATH, options = {}) { return { path: sourcePath, bytes: bytesOf(text), @@ -133,7 +200,7 @@ function measureText(text, sourcePath = ARTIFACT_PATH) { lines: countLines(text), tokens: countTokens(text), artifact_sha256: sha256(text), - goldens: evaluateGoldens(text), + goldens: evaluateGoldens(text, options), }; } @@ -310,7 +377,7 @@ function backtestReport() { ? { text: readText(ARTIFACT_PATH), path: ARTIFACT_PATH } : gitShowText(entry.ref, [ARTIFACT_PATH, TEMPLATE_PATH]); if (!content) return { ...entry, available: false, note: `${entry.note}; artifact/template unavailable at ref` }; - const metrics = measureText(content.text, content.path); + const metrics = measureText(content.text, content.path, { ref: entry.ref }); return { ...entry, available: true, @@ -322,6 +389,8 @@ function backtestReport() { tokens: metrics.tokens, golden_passed: metrics.goldens.passed, golden_failed: metrics.goldens.failed, + golden_applicable: metrics.goldens.applicable, + golden_not_applicable: metrics.goldens.not_applicable, golden_total: metrics.goldens.total, }; }); @@ -332,7 +401,7 @@ function backtestReport() { function currentReport() { const artifact = readText(ARTIFACT_PATH); const template = tryReadText(TEMPLATE_PATH); - const d1 = measureText(artifact, ARTIFACT_PATH); + const d1 = measureText(artifact, ARTIFACT_PATH, { ref: 'HEAD' }); return { mode: 'current', report_only: true, @@ -359,7 +428,7 @@ function tokenText(tokens) { function renderBacktest(report) { const header = ['Ref', 'Bytes', 'Chars', 'Lines', 'Tokens', 'Goldens', 'Source']; const rows = report.refs.map(row => row.available - ? [row.label, row.bytes, row.chars, row.lines, tokenText(row.tokens), `${row.golden_passed}/${row.golden_total} pass`, row.source_path] + ? [row.label, row.bytes, row.chars, row.lines, tokenText(row.tokens), `${row.golden_passed}/${row.golden_applicable} applicable pass + ${row.golden_not_applicable} N/A`, row.source_path] : [row.label, 'n/a', 'n/a', 'n/a', 'n/a', 'n/a', row.note ?? 'unavailable']); const widths = header.map((title, index) => Math.max(title.length, ...rows.map(row => String(row[index]).length))); const lines = [ @@ -369,7 +438,7 @@ function renderBacktest(report) { widths.map(width => '-'.repeat(width)).join('-|-'), ...rows.map(row => row.map((cell, index) => pad(cell, widths[index])).join(' | ')), '', - 'Note: older refs are expected to fail new canary goldens; this is historical signal, not an error.', + 'Note: goldens introduced after a historical ref are N/A; FAIL is reserved for applicable regressions.', ]; return lines.join('\n'); } @@ -383,14 +452,14 @@ function renderCurrent(report) { 'Size Regression Guard (REPORT-ONLY)', '', `D1 artifact: ${d1.bytes} bytes, ${d1.chars} chars, ${d1.lines} lines, ${d1.tokens.count} tokens${tokenSuffix}`, - `D3 goldens: ${d1.goldens.passed}/${d1.goldens.total} pass (${d1.goldens.failed} fail)`, + `D3 goldens: ${d1.goldens.passed}/${d1.goldens.applicable} applicable pass (${d1.goldens.failed} fail, ${d1.goldens.not_applicable} N/A)`, `D2 local estimate only: ${d2.total_bytes} bytes; skill frontmatter=${d2.components.always_loaded_skill_frontmatter.status}; squad_state schema=${d2.components.squad_state_tool_schema.status}`, `D4 budget: ${d4.status}${d4.status === 'loaded' ? `; soft_ceiling_bytes=${d4.soft_ceiling_bytes}; ${d4.note}` : ''}`, `artifact_sha256: ${report.artifact_sha256}`, `template_sha256: ${report.template_sha256 ?? 'unavailable'}`, '', 'Governance goldens:', - ...d1.goldens.checks.map(check => `- ${check.pass ? 'PASS' : 'FAIL'} ${check.id}: ${check.detail}`), + ...d1.goldens.checks.map(check => `- ${check.applicable ? (check.pass ? 'PASS' : 'FAIL') : 'N/A'} ${check.id}: ${check.detail}`), '', `Machine-readable JSON written to ${REPORT_PATH}`, ].join('\n'); From a9ad94f4648f7618c0be4c44cf67253a794f1450 Mon Sep 17 00:00:00 2001 From: Brady Gaster Date: Tue, 21 Jul 2026 00:51:01 -0700 Subject: [PATCH 04/15] fix: separate size fence from reduction target Refs #1461 #1498 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09d05820-0b7e-482b-b942-c86b1225dcf9 --- .github/size-budget.json | 8 ++- scripts/size-regression-guard.mjs | 99 +++++++++++++++++++++++++++---- 2 files changed, 91 insertions(+), 16 deletions(-) diff --git a/.github/size-budget.json b/.github/size-budget.json index 4a6b36af5..397e0fb49 100644 --- a/.github/size-budget.json +++ b/.github/size-budget.json @@ -1,8 +1,10 @@ { "artifact": ".github/agents/squad.agent.md", "mode": "report-only", - "soft_ceiling_bytes": 80000, - "soft_ceiling_o200k_tokens": 19000, + "regression_ceiling_bytes": 80000, + "regression_ceiling_o200k_tokens": 19000, + "reduction_target_bytes": 40000, + "reduction_target_o200k_tokens": 9500, "reviewed_overrides": [], - "note": "Report-only. No enforced thresholds yet; ceilings are provisional pending cross-squad backtest." + "note": "Report-only. Regression ceiling is an anti-regrowth fence only, not success criteria. Reduction targets are provisional stretch goals pending the peer squad's live curve; 40KB/9.5K o200k approximates the original slim-agent intent." } diff --git a/scripts/size-regression-guard.mjs b/scripts/size-regression-guard.mjs index 97b5a3589..3f451222e 100644 --- a/scripts/size-regression-guard.mjs +++ b/scripts/size-regression-guard.mjs @@ -295,11 +295,42 @@ function readBudget() { } } -function evaluateBudget(artifactBytes) { +function percentOfCurrent(delta, current) { + return Number.isFinite(delta) && Number.isFinite(current) && current > 0 + ? (delta / current) * 100 + : null; +} + +function sizeDelta(current, limit) { + if (!Number.isFinite(current) || !Number.isFinite(limit)) { + return { value: null, percent_of_current: null }; + } + return { + value: limit - current, + percent_of_current: percentOfCurrent(limit - current, current), + }; +} + +function reductionGap(current, target) { + if (!Number.isFinite(current) || !Number.isFinite(target)) { + return { value: null, percent_of_current: null }; + } + return { + value: Math.max(0, current - target), + percent_of_current: percentOfCurrent(Math.max(0, current - target), current), + }; +} + +function evaluateBudget(metrics) { const budget = readBudget(); if (budget.status !== 'loaded') return budget; - const softCeiling = Number(budget.data.soft_ceiling_bytes); - const grewBeyondSoftCeiling = Number.isFinite(softCeiling) && artifactBytes > softCeiling; + const artifactBytes = metrics.bytes; + const artifactTokens = metrics.tokens.count; + const regressionCeilingBytes = Number(budget.data.regression_ceiling_bytes); + const regressionCeilingTokens = Number(budget.data.regression_ceiling_o200k_tokens); + const reductionTargetBytes = Number(budget.data.reduction_target_bytes); + const reductionTargetTokens = Number(budget.data.reduction_target_o200k_tokens); + const grewBeyondRegressionCeiling = Number.isFinite(regressionCeilingBytes) && artifactBytes > regressionCeilingBytes; const overrides = Array.isArray(budget.data.reviewed_overrides) ? budget.data.reviewed_overrides : []; const coveringOverride = overrides.find(entry => { const approvedBytes = Number(entry?.approved_bytes); @@ -309,13 +340,24 @@ function evaluateBudget(artifactBytes) { status: 'loaded', path: BUDGET_PATH, mode: budget.data.mode, - soft_ceiling_bytes: softCeiling, - grew_beyond_soft_ceiling: grewBeyondSoftCeiling, + regression_ceiling_bytes: regressionCeilingBytes, + regression_ceiling_o200k_tokens: regressionCeilingTokens, + reduction_target_bytes: reductionTargetBytes, + reduction_target_o200k_tokens: reductionTargetTokens, + regression_headroom: { + bytes: sizeDelta(artifactBytes, regressionCeilingBytes), + o200k_tokens: sizeDelta(artifactTokens, regressionCeilingTokens), + }, + reduction_gap: { + bytes: reductionGap(artifactBytes, reductionTargetBytes), + o200k_tokens: reductionGap(artifactTokens, reductionTargetTokens), + }, + grew_beyond_regression_ceiling: grewBeyondRegressionCeiling, reviewed_override_covers_current_size: Boolean(coveringOverride), covering_override: coveringOverride ?? null, - note: grewBeyondSoftCeiling - ? (coveringOverride ? 'grew beyond soft ceiling; reviewed override covers current size' : 'grew beyond soft ceiling; no reviewed override covers current size') - : 'within provisional soft ceiling', + note: grewBeyondRegressionCeiling + ? (coveringOverride ? 'grew beyond regression ceiling; reviewed override covers current size' : 'grew beyond regression ceiling; no reviewed override covers current size') + : 'within report-only anti-regrowth ceiling; reduction target remains separate', }; } @@ -378,6 +420,7 @@ function backtestReport() { : gitShowText(entry.ref, [ARTIFACT_PATH, TEMPLATE_PATH]); if (!content) return { ...entry, available: false, note: `${entry.note}; artifact/template unavailable at ref` }; const metrics = measureText(content.text, content.path, { ref: entry.ref }); + const budgetMetrics = evaluateBudget(metrics); return { ...entry, available: true, @@ -392,6 +435,7 @@ function backtestReport() { golden_applicable: metrics.goldens.applicable, golden_not_applicable: metrics.goldens.not_applicable, golden_total: metrics.goldens.total, + budget: budgetMetrics, }; }); @@ -409,7 +453,7 @@ function currentReport() { d1_artifact_ceiling: d1, d2_net_resident_startup_estimate: computeD2(d1.bytes), d3_governance_golden_regression: d1.goldens, - d4_reviewed_override: evaluateBudget(d1.bytes), + d4_reviewed_override: evaluateBudget(d1), artifact_sha256: d1.artifact_sha256, template_sha256: template ? sha256(template) : null, template_path: TEMPLATE_PATH, @@ -425,11 +469,36 @@ function tokenText(tokens) { return `${tokens.count}${tokens.estimate ? ' est' : ''}`; } +function formatInteger(value) { + return Number.isFinite(value) ? Math.round(value).toLocaleString('en-US') : 'n/a'; +} + +function formatPercent(value) { + return Number.isFinite(value) ? `${value.toFixed(2)}%` : 'n/a'; +} + +function formatBudgetDelta(label, delta) { + if (!delta) return 'n/a'; + const bytes = delta.bytes; + const tokens = delta.o200k_tokens; + return `${label}: ${formatInteger(bytes.value)} B / ${formatInteger(tokens.value)} tok (${formatPercent(bytes.percent_of_current)} B / ${formatPercent(tokens.percent_of_current)} tok)`; +} + function renderBacktest(report) { - const header = ['Ref', 'Bytes', 'Chars', 'Lines', 'Tokens', 'Goldens', 'Source']; + const header = ['Ref', 'Bytes', 'Chars', 'Lines', 'Tokens', 'Goldens', 'Ceiling headroom', 'Reduction gap', 'Source']; const rows = report.refs.map(row => row.available - ? [row.label, row.bytes, row.chars, row.lines, tokenText(row.tokens), `${row.golden_passed}/${row.golden_applicable} applicable pass + ${row.golden_not_applicable} N/A`, row.source_path] - : [row.label, 'n/a', 'n/a', 'n/a', 'n/a', 'n/a', row.note ?? 'unavailable']); + ? [ + row.label, + row.bytes, + row.chars, + row.lines, + tokenText(row.tokens), + `${row.golden_passed}/${row.golden_applicable} applicable pass + ${row.golden_not_applicable} N/A`, + formatBudgetDelta('headroom', row.budget?.regression_headroom), + formatBudgetDelta('gap', row.budget?.reduction_gap), + row.source_path, + ] + : [row.label, 'n/a', 'n/a', 'n/a', 'n/a', 'n/a', 'n/a', 'n/a', row.note ?? 'unavailable']); const widths = header.map((title, index) => Math.max(title.length, ...rows.map(row => String(row[index]).length))); const lines = [ 'Size Regression Guard Backtest (REPORT-ONLY)', @@ -448,13 +517,17 @@ function renderCurrent(report) { const tokenSuffix = d1.tokens.estimate ? ' (estimate; python3+tiktoken unavailable)' : ' (o200k_base)'; const d2 = report.d2_net_resident_startup_estimate; const d4 = report.d4_reviewed_override; + const regressionHeadroom = formatBudgetDelta('regression headroom', d4.regression_headroom); + const reductionGap = formatBudgetDelta('reduction gap', d4.reduction_gap); return [ 'Size Regression Guard (REPORT-ONLY)', '', `D1 artifact: ${d1.bytes} bytes, ${d1.chars} chars, ${d1.lines} lines, ${d1.tokens.count} tokens${tokenSuffix}`, `D3 goldens: ${d1.goldens.passed}/${d1.goldens.applicable} applicable pass (${d1.goldens.failed} fail, ${d1.goldens.not_applicable} N/A)`, `D2 local estimate only: ${d2.total_bytes} bytes; skill frontmatter=${d2.components.always_loaded_skill_frontmatter.status}; squad_state schema=${d2.components.squad_state_tool_schema.status}`, - `D4 budget: ${d4.status}${d4.status === 'loaded' ? `; soft_ceiling_bytes=${d4.soft_ceiling_bytes}; ${d4.note}` : ''}`, + `D4 budget: ${d4.status}${d4.status === 'loaded' ? `; regression_ceiling_bytes=${d4.regression_ceiling_bytes}; regression_ceiling_o200k_tokens=${d4.regression_ceiling_o200k_tokens}; ${d4.note}` : ''}`, + `D4 ${regressionHeadroom}`, + `D4 ${reductionGap} beyond provisional target`, `artifact_sha256: ${report.artifact_sha256}`, `template_sha256: ${report.template_sha256 ?? 'unavailable'}`, '', From 6cf64d3d8839948584f2084588c1147b9f82d4d8 Mon Sep 17 00:00:00 2001 From: Brady Gaster Date: Tue, 21 Jul 2026 01:01:29 -0700 Subject: [PATCH 05/15] chore(canary): normalize line endings on template mirrors Reverts spurious LF->CRLF churn on workflow-wiring mirror files introduced by the sync-templates run in 197b19a. Content-identical; restores mirror parity with LF canonicals so template-sync check mode passes and the PR delta reflects only intended changes. Refs #1461 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09d05820-0b7e-482b-b942-c86b1225dcf9 --- ...orkflow-wiring-appendix-a-code-reviewer.md | 262 ++++----- .../workflow-wiring-appendix-b-documenter.md | 280 ++++----- .../templates/workflow-wiring-guide.md | 552 +++++++++--------- ...orkflow-wiring-appendix-a-code-reviewer.md | 262 ++++----- .../workflow-wiring-appendix-b-documenter.md | 280 ++++----- .../templates/workflow-wiring-guide.md | 552 +++++++++--------- ...orkflow-wiring-appendix-a-code-reviewer.md | 262 ++++----- .../workflow-wiring-appendix-b-documenter.md | 280 ++++----- templates/workflow-wiring-guide.md | 552 +++++++++--------- 9 files changed, 1641 insertions(+), 1641 deletions(-) diff --git a/packages/squad-cli/templates/workflow-wiring-appendix-a-code-reviewer.md b/packages/squad-cli/templates/workflow-wiring-appendix-a-code-reviewer.md index 324e0ef60..c447f7f9c 100644 --- a/packages/squad-cli/templates/workflow-wiring-appendix-a-code-reviewer.md +++ b/packages/squad-cli/templates/workflow-wiring-appendix-a-code-reviewer.md @@ -1,131 +1,131 @@ -# Appendix A: Wiring a Code Reviewer — Complete Walkthrough - -> End-to-end example of adding a code reviewer to your squad and wiring their gate so it actually gets enforced. This walkthrough addresses a common failure: a reviewer is on the roster but never reviews a single PR because the gate wasn't wired. - -## The Problem This Solves - -Adding a reviewer to `team.md` gives them an identity. It does NOT: -- Tell the coordinator to route PRs to them -- Prevent PRs from being merged without their approval -- Prevent issues from being closed before review happens - -**What goes wrong without enforcement:** A reviewer can be on the roster as "Reviewer" from day one. Their charter says they review PRs. The routing table says "PR code review → {ReviewerName}." But PRs get merged and issues get closed without them ever being spawned. Why? - -Because the routing table says WHO handles what — it's for incoming requests ("review PR #42"). It does NOT say "after every agent completes work, route their output to {ReviewerName}." The coordinator routes work TO agents, but nothing tells it to route COMPLETED work to a reviewer. The "After Agent Work" flow in `squad.agent.md` says: collect results → present → spawn Scribe. No review step. - -**The fix has three layers:** - -| Layer | What it does | Where it lives | -|-------|-------------|----------------| -| Identity | Reviewer exists and knows how to review | `team.md` roster + `charter.md` | -| Routing | User can explicitly request "review this" | `routing.md` routing table | -| **Enforcement** | Coordinator MUST route every PR to reviewer before merge | `routing.md` Rules section + `issue-lifecycle.md` post-work steps | - -Most squads get layers 1 and 2 right. Layer 3 — enforcement — is what's usually missing. - -## Step-by-Step Walkthrough - -### Step 1: Create the reviewer's identity - -Create `.squad/agents/{name}/charter.md`: - -```markdown -# {Name} — Code Reviewer - -## Identity -- **Name:** {Name} -- **Role:** Code Reviewer -- **Expertise:** Code quality, correctness, test coverage, security, patterns -- **Style:** Thorough, fair, specific. Provides actionable feedback. - -## What I Own -- Reviewing PRs for code quality, correctness, and test coverage -- Identifying bugs, security issues, and design problems -- Providing specific, actionable feedback (not vague suggestions) - -## How I Review -1. Read the PR diff completely -2. Check: does it do what the issue asked for? -3. Check: are there tests? Do they cover the important cases? -4. Check: are there bugs, edge cases, or security issues? -5. Check: does it follow project patterns and conventions? -6. Verdict: APPROVE or REJECT with specific feedback - -## Boundaries -**I handle:** Code review, PR review, quality gates -**I don't handle:** Implementation, design, research, documentation - -## On REJECT -I provide specific feedback: what's wrong, why, and what to do instead. -The original author fixes their work. I re-review after fixes. -``` - -Create `.squad/agents/{name}/history.md` seeded with project context. - -### Step 2: Add to team.md roster - -```markdown -| 👑 {Name} | Code Reviewer | `.squad/agents/{name}/charter.md` | ✅ Active | -``` - -### Step 3: Add routing table entry - -In `routing.md` → routing table: - -```markdown -| PR code review | 👑 {Name} | — | "Review PR #42", code quality, finding reports | -``` - -**⚠️ This is necessary but NOT sufficient.** This only handles explicit review requests. It does NOT enforce automatic review of every PR. - -### Step 4: Add enforcement rule (THIS IS THE CRITICAL STEP) - -In `routing.md` → `## Rules` section, add a numbered rule: - -```markdown -N. **{Name} PR Gate** — every PR created by any agent MUST be reviewed by {Name} - before merge. The coordinator spawns {Name} (sync) with the PR diff after - the author pushes and creates the PR. On REJECT, the original author addresses - feedback. On APPROVE, the coordinator merges via `gh pr merge`. No PR merges - without {Name}'s approval. -``` - -**Why this works when the routing table alone didn't:** The routing table is for matching incoming work to agents. Rules are behavioral constraints the coordinator must follow AFTER work completes. The rule says "after a PR exists, you MUST do X before proceeding." The routing table says "if someone asks for a review, route to X." - -### Step 5: Wire into issue-lifecycle.md - -In `.squad/templates/issue-lifecycle.md`, the "Coordinator Post-Work Steps" section should reference your reviewer by name: - -```markdown -4. **Route to reviewer.** Spawn {Name} (sync) with the PR diff for code review. -``` - -This is the operational detail — the step-by-step instructions the coordinator follows after an agent completes issue work. The routing rule (Step 4) is the mandate; the lifecycle template is the procedure. - -### Step 6: Add to casting registry - -Update `.squad/casting/registry.json` with the new entry. - -### Step 7: Verify - -Ask yourself these questions: - -- [ ] If a clean session coordinator reads `routing.md` Rules, will it know to route PRs to this reviewer? → Check rule N exists. -- [ ] If an agent completes work and pushes a PR, does the coordinator's post-work flow include a review step? → Check `issue-lifecycle.md` step 4. -- [ ] Can the coordinator merge a PR without the reviewer's approval? → The rule should say "No PR merges without {Name}'s approval." -- [ ] Can the coordinator close an issue without a merged PR? → Check the issue closure rule exists. - -If any answer is wrong, you have a gap. - -## What Each File Controls (Summary) - -| File | What it contributes to the reviewer gate | -|------|----------------------------------------| -| `charter.md` | WHO the reviewer is and HOW they review | -| `team.md` | That the reviewer EXISTS on the team | -| `routing.md` routing table | That explicit review requests go to this reviewer | -| `routing.md` Rules section | That the coordinator MUST route EVERY PR to this reviewer (enforcement) | -| `issue-lifecycle.md` | The step-by-step procedure for the post-work review flow | -| `casting/registry.json` | Persistent name tracking | - -**Remove any one of these and the gate has a hole.** The most commonly missed piece is the Rules section entry (Step 4). +# Appendix A: Wiring a Code Reviewer — Complete Walkthrough + +> End-to-end example of adding a code reviewer to your squad and wiring their gate so it actually gets enforced. This walkthrough addresses a common failure: a reviewer is on the roster but never reviews a single PR because the gate wasn't wired. + +## The Problem This Solves + +Adding a reviewer to `team.md` gives them an identity. It does NOT: +- Tell the coordinator to route PRs to them +- Prevent PRs from being merged without their approval +- Prevent issues from being closed before review happens + +**What goes wrong without enforcement:** A reviewer can be on the roster as "Reviewer" from day one. Their charter says they review PRs. The routing table says "PR code review → {ReviewerName}." But PRs get merged and issues get closed without them ever being spawned. Why? + +Because the routing table says WHO handles what — it's for incoming requests ("review PR #42"). It does NOT say "after every agent completes work, route their output to {ReviewerName}." The coordinator routes work TO agents, but nothing tells it to route COMPLETED work to a reviewer. The "After Agent Work" flow in `squad.agent.md` says: collect results → present → spawn Scribe. No review step. + +**The fix has three layers:** + +| Layer | What it does | Where it lives | +|-------|-------------|----------------| +| Identity | Reviewer exists and knows how to review | `team.md` roster + `charter.md` | +| Routing | User can explicitly request "review this" | `routing.md` routing table | +| **Enforcement** | Coordinator MUST route every PR to reviewer before merge | `routing.md` Rules section + `issue-lifecycle.md` post-work steps | + +Most squads get layers 1 and 2 right. Layer 3 — enforcement — is what's usually missing. + +## Step-by-Step Walkthrough + +### Step 1: Create the reviewer's identity + +Create `.squad/agents/{name}/charter.md`: + +```markdown +# {Name} — Code Reviewer + +## Identity +- **Name:** {Name} +- **Role:** Code Reviewer +- **Expertise:** Code quality, correctness, test coverage, security, patterns +- **Style:** Thorough, fair, specific. Provides actionable feedback. + +## What I Own +- Reviewing PRs for code quality, correctness, and test coverage +- Identifying bugs, security issues, and design problems +- Providing specific, actionable feedback (not vague suggestions) + +## How I Review +1. Read the PR diff completely +2. Check: does it do what the issue asked for? +3. Check: are there tests? Do they cover the important cases? +4. Check: are there bugs, edge cases, or security issues? +5. Check: does it follow project patterns and conventions? +6. Verdict: APPROVE or REJECT with specific feedback + +## Boundaries +**I handle:** Code review, PR review, quality gates +**I don't handle:** Implementation, design, research, documentation + +## On REJECT +I provide specific feedback: what's wrong, why, and what to do instead. +The original author fixes their work. I re-review after fixes. +``` + +Create `.squad/agents/{name}/history.md` seeded with project context. + +### Step 2: Add to team.md roster + +```markdown +| 👑 {Name} | Code Reviewer | `.squad/agents/{name}/charter.md` | ✅ Active | +``` + +### Step 3: Add routing table entry + +In `routing.md` → routing table: + +```markdown +| PR code review | 👑 {Name} | — | "Review PR #42", code quality, finding reports | +``` + +**⚠️ This is necessary but NOT sufficient.** This only handles explicit review requests. It does NOT enforce automatic review of every PR. + +### Step 4: Add enforcement rule (THIS IS THE CRITICAL STEP) + +In `routing.md` → `## Rules` section, add a numbered rule: + +```markdown +N. **{Name} PR Gate** — every PR created by any agent MUST be reviewed by {Name} + before merge. The coordinator spawns {Name} (sync) with the PR diff after + the author pushes and creates the PR. On REJECT, the original author addresses + feedback. On APPROVE, the coordinator merges via `gh pr merge`. No PR merges + without {Name}'s approval. +``` + +**Why this works when the routing table alone didn't:** The routing table is for matching incoming work to agents. Rules are behavioral constraints the coordinator must follow AFTER work completes. The rule says "after a PR exists, you MUST do X before proceeding." The routing table says "if someone asks for a review, route to X." + +### Step 5: Wire into issue-lifecycle.md + +In `.squad/templates/issue-lifecycle.md`, the "Coordinator Post-Work Steps" section should reference your reviewer by name: + +```markdown +4. **Route to reviewer.** Spawn {Name} (sync) with the PR diff for code review. +``` + +This is the operational detail — the step-by-step instructions the coordinator follows after an agent completes issue work. The routing rule (Step 4) is the mandate; the lifecycle template is the procedure. + +### Step 6: Add to casting registry + +Update `.squad/casting/registry.json` with the new entry. + +### Step 7: Verify + +Ask yourself these questions: + +- [ ] If a clean session coordinator reads `routing.md` Rules, will it know to route PRs to this reviewer? → Check rule N exists. +- [ ] If an agent completes work and pushes a PR, does the coordinator's post-work flow include a review step? → Check `issue-lifecycle.md` step 4. +- [ ] Can the coordinator merge a PR without the reviewer's approval? → The rule should say "No PR merges without {Name}'s approval." +- [ ] Can the coordinator close an issue without a merged PR? → Check the issue closure rule exists. + +If any answer is wrong, you have a gap. + +## What Each File Controls (Summary) + +| File | What it contributes to the reviewer gate | +|------|----------------------------------------| +| `charter.md` | WHO the reviewer is and HOW they review | +| `team.md` | That the reviewer EXISTS on the team | +| `routing.md` routing table | That explicit review requests go to this reviewer | +| `routing.md` Rules section | That the coordinator MUST route EVERY PR to this reviewer (enforcement) | +| `issue-lifecycle.md` | The step-by-step procedure for the post-work review flow | +| `casting/registry.json` | Persistent name tracking | + +**Remove any one of these and the gate has a hole.** The most commonly missed piece is the Rules section entry (Step 4). diff --git a/packages/squad-cli/templates/workflow-wiring-appendix-b-documenter.md b/packages/squad-cli/templates/workflow-wiring-appendix-b-documenter.md index 870e61ccb..fb8cd26aa 100644 --- a/packages/squad-cli/templates/workflow-wiring-appendix-b-documenter.md +++ b/packages/squad-cli/templates/workflow-wiring-appendix-b-documenter.md @@ -1,140 +1,140 @@ -# Appendix B: Wiring a Documenter/Librarian — Complete Walkthrough - -> End-to-end example of adding a documenter role that ensures significant changes are documented. This is a FOLLOW-UP TRIGGER pattern — not a gate (which blocks), but an automatic downstream task that fires after work completes. - -## The Problem This Solves - -Your project has agents building features, fixing bugs, and writing tools. But nobody documents what was built, how to use it, or what changed. Documentation happens only when someone explicitly asks — and by then, the context is lost. - -A documenter/librarian role solves this by automatically evaluating whether completed work needs documentation and producing it if so. - -## Gate vs Follow-Up Trigger - -| Pattern | Blocks work? | When it runs | Example | -|---------|-------------|-------------|---------| -| **Gate** (Appendix A) | Yes — work cannot proceed without approval | Before merge | Code reviewer must approve PR | -| **Follow-up trigger** | No — work proceeds, documentation happens in parallel | After merge | Documenter evaluates if docs are needed | - -A documenter is typically a follow-up trigger, not a gate. You don't want documentation review to block a hotfix from merging. But you DO want documentation to happen automatically after significant changes. - -## Step-by-Step Walkthrough - -### Step 1: Create the documenter's identity - -Create `.squad/agents/{name}/charter.md`: - -```markdown -# {Name} — Documenter - -## Identity -- **Name:** {Name} -- **Role:** Documenter / Librarian -- **Expertise:** Documentation, guides, READMEs, changelogs, knowledge management -- **Style:** Clear, thorough, user-focused. Makes complex things understandable. - -## What I Own -- Evaluating whether completed work needs documentation -- Writing/updating READMEs, guides, and runbooks -- Maintaining a docs index so nothing gets lost -- Summarizing design decisions and architectural changes - -## How I Work -1. Read the PR diff or agent output -2. Assess: does this change user-facing behavior? Add a new feature? Change configuration? -3. If yes: write or update the relevant documentation -4. If no: report "no docs needed" with brief justification - -## Boundaries -**I handle:** Documentation, guides, READMEs, summaries, knowledge management -**I don't handle:** Code implementation, code review, research, operations -``` - -Create `.squad/agents/{name}/history.md` seeded with project context. - -### Step 2: Add to team.md roster - -```markdown -| 📝 {Name} | Documenter | `.squad/agents/{name}/charter.md` | ✅ Active | -``` - -### Step 3: Add routing table entry - -In `routing.md` → routing table: - -```markdown -| Documentation, reports, summaries | 📝 {Name} | `docs/` | "Write docs for X", "Summarize this", guides, READMEs | -``` - -### Step 4: Add follow-up trigger rule - -In `routing.md` → `## Rules` section, add a numbered rule: - -```markdown -N. **Documentation follow-up** — after any PR is merged that adds or modifies - user-facing features, scripts, tools, or configuration, the coordinator - spawns {Name} (background) to evaluate whether documentation is needed. - {Name} reads the merged PR diff and either writes/updates docs or reports - "no docs needed." This is a follow-up, not a gate — it does not block - the merge. -``` - -**Why a rule and not a ceremony:** Ceremonies are structured multi-participant meetings. This is a single-agent follow-up task. A routing rule is simpler and more appropriate. - -**Why background, not sync:** Documentation doesn't block other work. The documenter runs in parallel with whatever comes next. - -### Step 5: Wire into the coordinator's post-merge flow - -This is the trickiest part. The coordinator's After Agent Work flow doesn't currently have a "post-merge" hook. You wire this through the issue-lifecycle template. - -In `.squad/templates/issue-lifecycle.md`, after the merge step, add: - -```markdown -8. **Documentation follow-up.** After merge, check routing.md Rules for - documentation follow-up rule. If present, spawn the documenter (background) - with the merged PR diff to evaluate whether docs are needed. -``` - -Alternatively, you can wire this as an `after` ceremony in `ceremonies.md`: - -```yaml -- name: "Documentation Check" - when: "after" - condition: "PR merged that adds features, scripts, tools, or config changes" - facilitator: "{DocumenterName}" - participants: ["{DocumenterName}"] - output: "Docs written/updated, or 'no docs needed' with justification" -``` - -### Step 6: Worktree for doc changes - -If the documenter produces files, they need a worktree — docs are files too. The coordinator should: -1. Create a worktree for the doc update (e.g., `squad/{issue}-docs`) -2. The documenter commits and pushes -3. A PR is created for the docs -4. The docs PR goes through the normal review flow (including the code reviewer if you have one) - -This means doc changes also get reviewed. The documenter is not exempt from the review gate. - -### Step 7: Add to casting registry - -Update `.squad/casting/registry.json` with the new entry. - -### Step 8: Verify - -- [ ] After a feature PR merges, does the coordinator spawn the documenter? → Check the routing rule exists. -- [ ] Does the documenter get a worktree for their work? → Check the worktree rule covers docs. -- [ ] Do doc changes go through the review gate? → They should — docs are files, files need PRs, PRs need review. -- [ ] Is the follow-up non-blocking? → The documenter should be background, not sync. - -## What Each File Controls (Summary) - -| File | What it contributes | -|------|-------------------| -| `charter.md` | WHO the documenter is and HOW they evaluate | -| `team.md` | That the documenter EXISTS | -| `routing.md` routing table | That explicit doc requests go to this member | -| `routing.md` Rules section | That the coordinator MUST spawn docs evaluation after merges (enforcement) | -| `issue-lifecycle.md` or `ceremonies.md` | The procedural hook: when exactly the follow-up fires | -| `casting/registry.json` | Persistent name tracking | - -**The most commonly missed piece:** The Rules section entry (Step 4). Without it, the documenter only runs when someone explicitly says "write docs for X." The whole point is that it runs automatically. +# Appendix B: Wiring a Documenter/Librarian — Complete Walkthrough + +> End-to-end example of adding a documenter role that ensures significant changes are documented. This is a FOLLOW-UP TRIGGER pattern — not a gate (which blocks), but an automatic downstream task that fires after work completes. + +## The Problem This Solves + +Your project has agents building features, fixing bugs, and writing tools. But nobody documents what was built, how to use it, or what changed. Documentation happens only when someone explicitly asks — and by then, the context is lost. + +A documenter/librarian role solves this by automatically evaluating whether completed work needs documentation and producing it if so. + +## Gate vs Follow-Up Trigger + +| Pattern | Blocks work? | When it runs | Example | +|---------|-------------|-------------|---------| +| **Gate** (Appendix A) | Yes — work cannot proceed without approval | Before merge | Code reviewer must approve PR | +| **Follow-up trigger** | No — work proceeds, documentation happens in parallel | After merge | Documenter evaluates if docs are needed | + +A documenter is typically a follow-up trigger, not a gate. You don't want documentation review to block a hotfix from merging. But you DO want documentation to happen automatically after significant changes. + +## Step-by-Step Walkthrough + +### Step 1: Create the documenter's identity + +Create `.squad/agents/{name}/charter.md`: + +```markdown +# {Name} — Documenter + +## Identity +- **Name:** {Name} +- **Role:** Documenter / Librarian +- **Expertise:** Documentation, guides, READMEs, changelogs, knowledge management +- **Style:** Clear, thorough, user-focused. Makes complex things understandable. + +## What I Own +- Evaluating whether completed work needs documentation +- Writing/updating READMEs, guides, and runbooks +- Maintaining a docs index so nothing gets lost +- Summarizing design decisions and architectural changes + +## How I Work +1. Read the PR diff or agent output +2. Assess: does this change user-facing behavior? Add a new feature? Change configuration? +3. If yes: write or update the relevant documentation +4. If no: report "no docs needed" with brief justification + +## Boundaries +**I handle:** Documentation, guides, READMEs, summaries, knowledge management +**I don't handle:** Code implementation, code review, research, operations +``` + +Create `.squad/agents/{name}/history.md` seeded with project context. + +### Step 2: Add to team.md roster + +```markdown +| 📝 {Name} | Documenter | `.squad/agents/{name}/charter.md` | ✅ Active | +``` + +### Step 3: Add routing table entry + +In `routing.md` → routing table: + +```markdown +| Documentation, reports, summaries | 📝 {Name} | `docs/` | "Write docs for X", "Summarize this", guides, READMEs | +``` + +### Step 4: Add follow-up trigger rule + +In `routing.md` → `## Rules` section, add a numbered rule: + +```markdown +N. **Documentation follow-up** — after any PR is merged that adds or modifies + user-facing features, scripts, tools, or configuration, the coordinator + spawns {Name} (background) to evaluate whether documentation is needed. + {Name} reads the merged PR diff and either writes/updates docs or reports + "no docs needed." This is a follow-up, not a gate — it does not block + the merge. +``` + +**Why a rule and not a ceremony:** Ceremonies are structured multi-participant meetings. This is a single-agent follow-up task. A routing rule is simpler and more appropriate. + +**Why background, not sync:** Documentation doesn't block other work. The documenter runs in parallel with whatever comes next. + +### Step 5: Wire into the coordinator's post-merge flow + +This is the trickiest part. The coordinator's After Agent Work flow doesn't currently have a "post-merge" hook. You wire this through the issue-lifecycle template. + +In `.squad/templates/issue-lifecycle.md`, after the merge step, add: + +```markdown +8. **Documentation follow-up.** After merge, check routing.md Rules for + documentation follow-up rule. If present, spawn the documenter (background) + with the merged PR diff to evaluate whether docs are needed. +``` + +Alternatively, you can wire this as an `after` ceremony in `ceremonies.md`: + +```yaml +- name: "Documentation Check" + when: "after" + condition: "PR merged that adds features, scripts, tools, or config changes" + facilitator: "{DocumenterName}" + participants: ["{DocumenterName}"] + output: "Docs written/updated, or 'no docs needed' with justification" +``` + +### Step 6: Worktree for doc changes + +If the documenter produces files, they need a worktree — docs are files too. The coordinator should: +1. Create a worktree for the doc update (e.g., `squad/{issue}-docs`) +2. The documenter commits and pushes +3. A PR is created for the docs +4. The docs PR goes through the normal review flow (including the code reviewer if you have one) + +This means doc changes also get reviewed. The documenter is not exempt from the review gate. + +### Step 7: Add to casting registry + +Update `.squad/casting/registry.json` with the new entry. + +### Step 8: Verify + +- [ ] After a feature PR merges, does the coordinator spawn the documenter? → Check the routing rule exists. +- [ ] Does the documenter get a worktree for their work? → Check the worktree rule covers docs. +- [ ] Do doc changes go through the review gate? → They should — docs are files, files need PRs, PRs need review. +- [ ] Is the follow-up non-blocking? → The documenter should be background, not sync. + +## What Each File Controls (Summary) + +| File | What it contributes | +|------|-------------------| +| `charter.md` | WHO the documenter is and HOW they evaluate | +| `team.md` | That the documenter EXISTS | +| `routing.md` routing table | That explicit doc requests go to this member | +| `routing.md` Rules section | That the coordinator MUST spawn docs evaluation after merges (enforcement) | +| `issue-lifecycle.md` or `ceremonies.md` | The procedural hook: when exactly the follow-up fires | +| `casting/registry.json` | Persistent name tracking | + +**The most commonly missed piece:** The Rules section entry (Step 4). Without it, the documenter only runs when someone explicitly says "write docs for X." The whole point is that it runs automatically. diff --git a/packages/squad-cli/templates/workflow-wiring-guide.md b/packages/squad-cli/templates/workflow-wiring-guide.md index e6b775ae1..853a13d0a 100644 --- a/packages/squad-cli/templates/workflow-wiring-guide.md +++ b/packages/squad-cli/templates/workflow-wiring-guide.md @@ -1,276 +1,276 @@ -# Squad Workflow Wiring Guide - -> How to wire up new team members, reviewer gates, and custom workflows so they actually get enforced by the coordinator — even in a clean session with no prior memory. - -## Why This Guide Exists - -The Squad framework (`squad.agent.md`) provides generic orchestration primitives. **It does not prescribe a specific workflow.** Your project's workflow — whether that's "all code goes through PRs and reviews" or "just commit to main" — must be wired into project-level configuration files. - -If a workflow rule exists only in someone's memory, in a chat transcript, or in `decisions.md` but NOT in a configuration file the coordinator reads at decision time — **it will not be followed in a clean session.** - -### Why Existing Patterns Aren't Enough - -The Squad framework already has concepts for routing tables, reviewer roles, and ceremonies. But having these concepts does NOT mean they work automatically: - -- **Adding a reviewer to the roster ≠ enforcing reviews.** A reviewer can be on the roster with "Reviewer" as their role and never review a single PR — because no RULE in `routing.md` tells the coordinator to route PRs to them. The roster says WHO exists. Rules say WHAT they enforce. - -- **Capturing a decision ≠ enforcing it.** `decisions.md` may contain "every change must go through a PR" and "only {ReviewerName} closes PRs." These can get buried in a large file that the coordinator reads for context but doesn't treat as enforcement rules. A decision is a historical record. A routing rule is an enforceable constraint. - -- **Describing a lifecycle ≠ wiring it.** `squad.agent.md` describes issue→branch→PR→review→merge. But if the After Agent Work section (the flow the coordinator actually follows after every agent completes) has no push/PR/review step, the lifecycle is described conceptually but never connected to the coordinator's actual decision flow. - -**The pattern that works:** A numbered rule in `routing.md` → Rules section. The coordinator reads this section, treats each rule as a constraint, and follows them. If your workflow isn't a numbered rule, it's a suggestion. - ---- - -## Configuration Surface Area - -The coordinator reads these files to decide how to behave. If your workflow isn't encoded in one of these, it doesn't exist. - -| File | What It Controls | Read When | -|------|-----------------|-----------| -| `routing.md` | WHO handles what, behavioral RULES, reviewer GATES | Every session start, before every routing decision | -| `ceremonies.md` | Auto-triggered ceremonies (before/after work batches) | Before spawning work batches, after completion | -| `templates/issue-lifecycle.md` | Git workflow: push, PR, review, merge, issue closure | When spawning agents for issue-linked work | -| Agent `charter.md` | Per-agent identity, boundaries, behavior | Inlined into every spawn prompt | -| `team.md` | Roster, member capabilities | Session start | -| `decisions.md` | Captured decisions and directives | Read by agents at spawn time | - -### How They Interact - -``` -User request arrives - → Coordinator reads routing.md (WHO handles this?) - → Coordinator checks ceremonies.md (any auto-triggered "before" ceremony?) - → Coordinator reads agent charter.md (inline into spawn prompt) - → If issue-linked: coordinator reads issue-lifecycle.md (add ISSUE CONTEXT to spawn prompt) - → Agent works - → Coordinator follows After Agent Work flow - → Coordinator checks ceremonies.md (any auto-triggered "after" ceremony?) - → Coordinator checks routing.md Rules section (any post-work rules to enforce?) -``` - -**The critical insight:** `routing.md` Rules section and `ceremonies.md` are the two enforcement mechanisms. If a rule isn't in one of these, the coordinator has no way to know about it. - ---- - -## How to Wire Up a New Team Member - -### Step 1: Create the member (files) - -``` -.squad/agents/{name}/ - charter.md ← Identity, role, boundaries, what they own - history.md ← Seeded with project context from team.md -``` - -### Step 2: Add to roster (`team.md`) - -Add a row to the `## Members` table: -``` -| {emoji} {Name} | {Role} | `.squad/agents/{name}/charter.md` | ✅ Active | -``` - -### Step 3: Add routing entry (`routing.md`) - -Add a row to the routing table: -``` -| {Work Type} | {emoji} {Name} | {Output Location} | {Examples} | -``` - -### Step 4: Add issue routing (if applicable) - -Add to the Issue Routing table in `routing.md`: -``` -| squad:{name} | {Description of work} | {emoji} {Name} | -``` - -### Step 5: Add to casting registry - -Update `.squad/casting/registry.json` with the new entry. - -### Step 6: Wire any gates (if this member is a reviewer/gate) - -**This is the step most people miss.** If the new member should review or gate other members' work, you need to wire enforcement. See "How to Wire Up a Reviewer Gate" below. - ---- - -## How to Wire Up a Reviewer Gate - -A reviewer gate means: "Agent X must review Agent Y's output before it proceeds." The framework supports this but does NOT automatically enforce it. You must wire it. - -### Option A: Routing Rule (recommended for simple gates) - -Add to `routing.md` → `## Rules` section: - -```markdown -N. **{GateName} Gate** — Every {output type} from {Author} MUST be reviewed by {ReviewerName} before {next step}. The coordinator routes {Author}'s output to {ReviewerName} (sync spawn), collects the verdict, and only proceeds if approved. On rejection, {Author} revises based on {ReviewerName}'s feedback. -``` - -**Example — reviewer for all PRs:** -```markdown -9. **{ReviewerName} PR Gate** — Every PR created by any agent MUST be reviewed by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) with the PR diff, collects APPROVE/REJECT verdict. On rejection, the original author addresses feedback. -``` - -**Example — design review gate:** -```markdown -10. **{DesignReviewer} Design Gate** — Every design doc produced by the architect MUST be reviewed by {DesignReviewer} before implementation begins. {DesignReviewer} always rejects the first draft on concept/approach. Implementation is BLOCKED until {DesignReviewer} approves. -``` - -**Why this works:** The coordinator reads the Rules section before and after every work batch. Rules are behavioral constraints the coordinator must follow. - -### Option B: Ceremony (recommended for multi-participant gates) - -Add to `ceremonies.md` using the Markdown table format the file uses: - -```markdown -## Design Review - -| Field | Value | -|-------|-------| -| **Trigger** | auto | -| **When** | before | -| **Condition** | task involves implementing a design doc | -| **Facilitator** | {DesignReviewer} | -| **Participants** | Architect, {DesignReviewer} | -| **Time budget** | focused | -| **Enabled** | ✅ yes | - -**Agenda:** -1. Read the design doc -2. Challenge the premise and approach -3. Demand alternatives and evidence -4. Verdict: APPROVE or REJECT -``` - -**Why this works:** The coordinator checks ceremonies.md for `before` ceremonies whose condition matches the current task. If matched, the ceremony runs before work begins. - -### Option A vs Option B - -| Use Case | Use Routing Rule | Use Ceremony | -|----------|-----------------|--------------| -| Simple 1-on-1 review (reviewer → author) | ✅ | Overkill | -| Multi-participant alignment (3+ agents) | Too simple | ✅ | -| Needs structured facilitation | No | ✅ | -| Must run automatically before specific work | Either works | ✅ | -| One-line behavioral constraint | ✅ | Overkill | - ---- - -## How to Wire Up an Issue Lifecycle (Git Workflow) - -This is where you define what happens after an agent completes work on a GitHub issue. The framework references `.squad/templates/issue-lifecycle.md` but does NOT create it — you must create it yourself. - -> **⚠️ This file is required if your project uses GitHub Issues Mode.** Without it, the coordinator has no post-work steps for push/PR/review and will treat agent commit as "done." - -See `.squad/templates/issue-lifecycle.md` for the full template if your project already has one. If not, create it following the pattern below. - -### Step 1: Create `templates/issue-lifecycle.md` - -Create `.squad/templates/issue-lifecycle.md` with your project's git workflow. At minimum it should include: - -- An ISSUE CONTEXT block template (for spawn prompts) -- Coordinator post-work steps (verify push → verify PR → route to reviewer → merge on approval) -- Issue closure rules (PR merge auto-close vs manual close) -- Worktree requirements (if applicable) - -### Step 2: Add enforcement rules to `routing.md` - -Add numbered rules to the `## Rules` section that reference the lifecycle: - -```markdown -N. **Issue lifecycle enforcement** — all issue-linked work follows the lifecycle - in `.squad/templates/issue-lifecycle.md`. The coordinator adds the ISSUE CONTEXT - block to spawn prompts and follows the post-work steps (verify push → verify PR - → route to reviewer → merge on approval). Read `issue-lifecycle.md` before - spawning any agent for issue work. - -N+1. **{ReviewerName} PR Gate** — every PR created by any agent MUST be reviewed - by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) - with the PR diff. On REJECT, the original author addresses feedback. On APPROVE, - the coordinator merges. No PR merges without {ReviewerName}'s approval. - -N+2. **Issue closure restriction** — issues that produced files (code, docs, scripts, - designs, tests) close ONLY via PR merge auto-close ("Closes #N" in PR body). - Never use `gh issue close` for file-producing work. Exception: tracking/strategic - issues and superseded issues may be closed with a comment. - -N+3. **Worktree for all file-producing work** — every task that creates or modifies - files (including documentation) requires a worktree. Exceptions: read-only queries, - Scribe (.squad/ state), pure analysis producing no files. -``` - -### Step 3: Verify your wiring - -After creating both files, run the verification checklist (below) to confirm a clean session coordinator would follow the lifecycle. - ---- - -## How to Wire Up a Custom Workflow Step - -If you need something that isn't a reviewer gate or issue lifecycle — for example, "always run tests before pushing" or "docs must be reviewed by the author before merge" — here's where to put it: - -### If it's a behavioral rule the coordinator should always follow: -→ Add to `routing.md` → `## Rules` section - -### If it should trigger automatically before/after specific work: -→ Add to `ceremonies.md` as a `before` or `after` ceremony - -### If it's something agents should do as part of their work: -→ Add to the agent's `charter.md` under a new section - -### If it's something that applies only to issue-linked work: -→ Add to `templates/issue-lifecycle.md` - -### If it's a team-wide constraint that should be visible to all agents: -→ Capture as a decision in `decisions.md` (via directive or decision inbox) - ---- - -## Verification Checklist - -After wiring any new member, gate, or workflow, verify: - -- [ ] **Clean session test:** Start a new session (no memory). Give a task. Does the coordinator follow the new rule? -- [ ] **File completeness:** Is the rule/gate/workflow encoded in a file the coordinator reads? (routing.md, ceremonies.md, issue-lifecycle.md, charter.md) -- [ ] **No verbal-only rules:** Is there anything the coordinator should do that's only in chat history or your memory? If yes, it will be lost on session restart. -- [ ] **Gate enforcement:** If you added a reviewer gate, does the routing.md Rules section or ceremonies.md explicitly say the coordinator must route to the reviewer? "Having a reviewer on the roster" is not the same as "enforcing that they review." -- [ ] **Issue lifecycle:** If your project uses PRs, does `templates/issue-lifecycle.md` exist? Does routing.md reference it? - ---- - -## Common Mistakes - -1. **Adding a reviewer to the roster but not wiring a gate.** Having a reviewer on the team doesn't mean they review anything. You must add a rule in routing.md that says "route PRs to {ReviewerName}." - -2. **Closing issues via `gh issue close` instead of PR merge.** If your project uses PRs, issue closure should happen via "Closes #N" in the PR body. Wire this in issue-lifecycle.md. - -3. **Writing docs/scripts directly on main.** If your project requires branches for all changes, the worktree gate must apply to ALL file-producing work — including docs. Make this explicit in routing.md Rules. - -4. **Assuming the coordinator remembers verbal instructions.** Each session starts fresh. If you told the coordinator "always use opus" in session 1, session 2 won't know unless it's in decisions.md or routing.md. - -5. **Not creating `issue-lifecycle.md`.** The framework references it but doesn't create it. If your project uses GitHub Issues Mode, create this template. - -6. **Capturing a decision but never encoding it as a rule.** `decisions.md` is a historical record. The coordinator reads it for context but doesn't treat entries as enforceable constraints. If a decision should be enforced, it must become a numbered rule in `routing.md` Rules section. - ---- - -## Decisions Audit - -Periodically scan `decisions.md` for directives that should be routing rules but aren't: - -1. Search for phrases like "always", "never", "must", "every", "required" -2. For each match, ask: "Is this enforced by a numbered rule in routing.md?" -3. If no → either add a rule, or accept that it's advisory-only -4. If yes → verify the rule text matches the decision - -This prevents `decisions.md` from becoming a graveyard of good intentions that the coordinator reads but doesn't act on. - ---- - -## Appendices - -For detailed end-to-end walkthroughs of specific wiring scenarios, see: - -- **[Appendix A: Wiring a Code Reviewer](workflow-wiring-appendix-a-code-reviewer.md)** — Full walkthrough of adding a code reviewer member and wiring their gate so it actually gets enforced. Includes every file that needs modification with exact content. - -- **[Appendix B: Wiring a Documenter/Librarian](workflow-wiring-appendix-b-documenter.md)** — Full walkthrough of adding a documenter role that ensures all significant changes are documented. Shows a follow-up trigger pattern rather than a gate pattern. +# Squad Workflow Wiring Guide + +> How to wire up new team members, reviewer gates, and custom workflows so they actually get enforced by the coordinator — even in a clean session with no prior memory. + +## Why This Guide Exists + +The Squad framework (`squad.agent.md`) provides generic orchestration primitives. **It does not prescribe a specific workflow.** Your project's workflow — whether that's "all code goes through PRs and reviews" or "just commit to main" — must be wired into project-level configuration files. + +If a workflow rule exists only in someone's memory, in a chat transcript, or in `decisions.md` but NOT in a configuration file the coordinator reads at decision time — **it will not be followed in a clean session.** + +### Why Existing Patterns Aren't Enough + +The Squad framework already has concepts for routing tables, reviewer roles, and ceremonies. But having these concepts does NOT mean they work automatically: + +- **Adding a reviewer to the roster ≠ enforcing reviews.** A reviewer can be on the roster with "Reviewer" as their role and never review a single PR — because no RULE in `routing.md` tells the coordinator to route PRs to them. The roster says WHO exists. Rules say WHAT they enforce. + +- **Capturing a decision ≠ enforcing it.** `decisions.md` may contain "every change must go through a PR" and "only {ReviewerName} closes PRs." These can get buried in a large file that the coordinator reads for context but doesn't treat as enforcement rules. A decision is a historical record. A routing rule is an enforceable constraint. + +- **Describing a lifecycle ≠ wiring it.** `squad.agent.md` describes issue→branch→PR→review→merge. But if the After Agent Work section (the flow the coordinator actually follows after every agent completes) has no push/PR/review step, the lifecycle is described conceptually but never connected to the coordinator's actual decision flow. + +**The pattern that works:** A numbered rule in `routing.md` → Rules section. The coordinator reads this section, treats each rule as a constraint, and follows them. If your workflow isn't a numbered rule, it's a suggestion. + +--- + +## Configuration Surface Area + +The coordinator reads these files to decide how to behave. If your workflow isn't encoded in one of these, it doesn't exist. + +| File | What It Controls | Read When | +|------|-----------------|-----------| +| `routing.md` | WHO handles what, behavioral RULES, reviewer GATES | Every session start, before every routing decision | +| `ceremonies.md` | Auto-triggered ceremonies (before/after work batches) | Before spawning work batches, after completion | +| `templates/issue-lifecycle.md` | Git workflow: push, PR, review, merge, issue closure | When spawning agents for issue-linked work | +| Agent `charter.md` | Per-agent identity, boundaries, behavior | Inlined into every spawn prompt | +| `team.md` | Roster, member capabilities | Session start | +| `decisions.md` | Captured decisions and directives | Read by agents at spawn time | + +### How They Interact + +``` +User request arrives + → Coordinator reads routing.md (WHO handles this?) + → Coordinator checks ceremonies.md (any auto-triggered "before" ceremony?) + → Coordinator reads agent charter.md (inline into spawn prompt) + → If issue-linked: coordinator reads issue-lifecycle.md (add ISSUE CONTEXT to spawn prompt) + → Agent works + → Coordinator follows After Agent Work flow + → Coordinator checks ceremonies.md (any auto-triggered "after" ceremony?) + → Coordinator checks routing.md Rules section (any post-work rules to enforce?) +``` + +**The critical insight:** `routing.md` Rules section and `ceremonies.md` are the two enforcement mechanisms. If a rule isn't in one of these, the coordinator has no way to know about it. + +--- + +## How to Wire Up a New Team Member + +### Step 1: Create the member (files) + +``` +.squad/agents/{name}/ + charter.md ← Identity, role, boundaries, what they own + history.md ← Seeded with project context from team.md +``` + +### Step 2: Add to roster (`team.md`) + +Add a row to the `## Members` table: +``` +| {emoji} {Name} | {Role} | `.squad/agents/{name}/charter.md` | ✅ Active | +``` + +### Step 3: Add routing entry (`routing.md`) + +Add a row to the routing table: +``` +| {Work Type} | {emoji} {Name} | {Output Location} | {Examples} | +``` + +### Step 4: Add issue routing (if applicable) + +Add to the Issue Routing table in `routing.md`: +``` +| squad:{name} | {Description of work} | {emoji} {Name} | +``` + +### Step 5: Add to casting registry + +Update `.squad/casting/registry.json` with the new entry. + +### Step 6: Wire any gates (if this member is a reviewer/gate) + +**This is the step most people miss.** If the new member should review or gate other members' work, you need to wire enforcement. See "How to Wire Up a Reviewer Gate" below. + +--- + +## How to Wire Up a Reviewer Gate + +A reviewer gate means: "Agent X must review Agent Y's output before it proceeds." The framework supports this but does NOT automatically enforce it. You must wire it. + +### Option A: Routing Rule (recommended for simple gates) + +Add to `routing.md` → `## Rules` section: + +```markdown +N. **{GateName} Gate** — Every {output type} from {Author} MUST be reviewed by {ReviewerName} before {next step}. The coordinator routes {Author}'s output to {ReviewerName} (sync spawn), collects the verdict, and only proceeds if approved. On rejection, {Author} revises based on {ReviewerName}'s feedback. +``` + +**Example — reviewer for all PRs:** +```markdown +9. **{ReviewerName} PR Gate** — Every PR created by any agent MUST be reviewed by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) with the PR diff, collects APPROVE/REJECT verdict. On rejection, the original author addresses feedback. +``` + +**Example — design review gate:** +```markdown +10. **{DesignReviewer} Design Gate** — Every design doc produced by the architect MUST be reviewed by {DesignReviewer} before implementation begins. {DesignReviewer} always rejects the first draft on concept/approach. Implementation is BLOCKED until {DesignReviewer} approves. +``` + +**Why this works:** The coordinator reads the Rules section before and after every work batch. Rules are behavioral constraints the coordinator must follow. + +### Option B: Ceremony (recommended for multi-participant gates) + +Add to `ceremonies.md` using the Markdown table format the file uses: + +```markdown +## Design Review + +| Field | Value | +|-------|-------| +| **Trigger** | auto | +| **When** | before | +| **Condition** | task involves implementing a design doc | +| **Facilitator** | {DesignReviewer} | +| **Participants** | Architect, {DesignReviewer} | +| **Time budget** | focused | +| **Enabled** | ✅ yes | + +**Agenda:** +1. Read the design doc +2. Challenge the premise and approach +3. Demand alternatives and evidence +4. Verdict: APPROVE or REJECT +``` + +**Why this works:** The coordinator checks ceremonies.md for `before` ceremonies whose condition matches the current task. If matched, the ceremony runs before work begins. + +### Option A vs Option B + +| Use Case | Use Routing Rule | Use Ceremony | +|----------|-----------------|--------------| +| Simple 1-on-1 review (reviewer → author) | ✅ | Overkill | +| Multi-participant alignment (3+ agents) | Too simple | ✅ | +| Needs structured facilitation | No | ✅ | +| Must run automatically before specific work | Either works | ✅ | +| One-line behavioral constraint | ✅ | Overkill | + +--- + +## How to Wire Up an Issue Lifecycle (Git Workflow) + +This is where you define what happens after an agent completes work on a GitHub issue. The framework references `.squad/templates/issue-lifecycle.md` but does NOT create it — you must create it yourself. + +> **⚠️ This file is required if your project uses GitHub Issues Mode.** Without it, the coordinator has no post-work steps for push/PR/review and will treat agent commit as "done." + +See `.squad/templates/issue-lifecycle.md` for the full template if your project already has one. If not, create it following the pattern below. + +### Step 1: Create `templates/issue-lifecycle.md` + +Create `.squad/templates/issue-lifecycle.md` with your project's git workflow. At minimum it should include: + +- An ISSUE CONTEXT block template (for spawn prompts) +- Coordinator post-work steps (verify push → verify PR → route to reviewer → merge on approval) +- Issue closure rules (PR merge auto-close vs manual close) +- Worktree requirements (if applicable) + +### Step 2: Add enforcement rules to `routing.md` + +Add numbered rules to the `## Rules` section that reference the lifecycle: + +```markdown +N. **Issue lifecycle enforcement** — all issue-linked work follows the lifecycle + in `.squad/templates/issue-lifecycle.md`. The coordinator adds the ISSUE CONTEXT + block to spawn prompts and follows the post-work steps (verify push → verify PR + → route to reviewer → merge on approval). Read `issue-lifecycle.md` before + spawning any agent for issue work. + +N+1. **{ReviewerName} PR Gate** — every PR created by any agent MUST be reviewed + by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) + with the PR diff. On REJECT, the original author addresses feedback. On APPROVE, + the coordinator merges. No PR merges without {ReviewerName}'s approval. + +N+2. **Issue closure restriction** — issues that produced files (code, docs, scripts, + designs, tests) close ONLY via PR merge auto-close ("Closes #N" in PR body). + Never use `gh issue close` for file-producing work. Exception: tracking/strategic + issues and superseded issues may be closed with a comment. + +N+3. **Worktree for all file-producing work** — every task that creates or modifies + files (including documentation) requires a worktree. Exceptions: read-only queries, + Scribe (.squad/ state), pure analysis producing no files. +``` + +### Step 3: Verify your wiring + +After creating both files, run the verification checklist (below) to confirm a clean session coordinator would follow the lifecycle. + +--- + +## How to Wire Up a Custom Workflow Step + +If you need something that isn't a reviewer gate or issue lifecycle — for example, "always run tests before pushing" or "docs must be reviewed by the author before merge" — here's where to put it: + +### If it's a behavioral rule the coordinator should always follow: +→ Add to `routing.md` → `## Rules` section + +### If it should trigger automatically before/after specific work: +→ Add to `ceremonies.md` as a `before` or `after` ceremony + +### If it's something agents should do as part of their work: +→ Add to the agent's `charter.md` under a new section + +### If it's something that applies only to issue-linked work: +→ Add to `templates/issue-lifecycle.md` + +### If it's a team-wide constraint that should be visible to all agents: +→ Capture as a decision in `decisions.md` (via directive or decision inbox) + +--- + +## Verification Checklist + +After wiring any new member, gate, or workflow, verify: + +- [ ] **Clean session test:** Start a new session (no memory). Give a task. Does the coordinator follow the new rule? +- [ ] **File completeness:** Is the rule/gate/workflow encoded in a file the coordinator reads? (routing.md, ceremonies.md, issue-lifecycle.md, charter.md) +- [ ] **No verbal-only rules:** Is there anything the coordinator should do that's only in chat history or your memory? If yes, it will be lost on session restart. +- [ ] **Gate enforcement:** If you added a reviewer gate, does the routing.md Rules section or ceremonies.md explicitly say the coordinator must route to the reviewer? "Having a reviewer on the roster" is not the same as "enforcing that they review." +- [ ] **Issue lifecycle:** If your project uses PRs, does `templates/issue-lifecycle.md` exist? Does routing.md reference it? + +--- + +## Common Mistakes + +1. **Adding a reviewer to the roster but not wiring a gate.** Having a reviewer on the team doesn't mean they review anything. You must add a rule in routing.md that says "route PRs to {ReviewerName}." + +2. **Closing issues via `gh issue close` instead of PR merge.** If your project uses PRs, issue closure should happen via "Closes #N" in the PR body. Wire this in issue-lifecycle.md. + +3. **Writing docs/scripts directly on main.** If your project requires branches for all changes, the worktree gate must apply to ALL file-producing work — including docs. Make this explicit in routing.md Rules. + +4. **Assuming the coordinator remembers verbal instructions.** Each session starts fresh. If you told the coordinator "always use opus" in session 1, session 2 won't know unless it's in decisions.md or routing.md. + +5. **Not creating `issue-lifecycle.md`.** The framework references it but doesn't create it. If your project uses GitHub Issues Mode, create this template. + +6. **Capturing a decision but never encoding it as a rule.** `decisions.md` is a historical record. The coordinator reads it for context but doesn't treat entries as enforceable constraints. If a decision should be enforced, it must become a numbered rule in `routing.md` Rules section. + +--- + +## Decisions Audit + +Periodically scan `decisions.md` for directives that should be routing rules but aren't: + +1. Search for phrases like "always", "never", "must", "every", "required" +2. For each match, ask: "Is this enforced by a numbered rule in routing.md?" +3. If no → either add a rule, or accept that it's advisory-only +4. If yes → verify the rule text matches the decision + +This prevents `decisions.md` from becoming a graveyard of good intentions that the coordinator reads but doesn't act on. + +--- + +## Appendices + +For detailed end-to-end walkthroughs of specific wiring scenarios, see: + +- **[Appendix A: Wiring a Code Reviewer](workflow-wiring-appendix-a-code-reviewer.md)** — Full walkthrough of adding a code reviewer member and wiring their gate so it actually gets enforced. Includes every file that needs modification with exact content. + +- **[Appendix B: Wiring a Documenter/Librarian](workflow-wiring-appendix-b-documenter.md)** — Full walkthrough of adding a documenter role that ensures all significant changes are documented. Shows a follow-up trigger pattern rather than a gate pattern. diff --git a/packages/squad-sdk/templates/workflow-wiring-appendix-a-code-reviewer.md b/packages/squad-sdk/templates/workflow-wiring-appendix-a-code-reviewer.md index 324e0ef60..c447f7f9c 100644 --- a/packages/squad-sdk/templates/workflow-wiring-appendix-a-code-reviewer.md +++ b/packages/squad-sdk/templates/workflow-wiring-appendix-a-code-reviewer.md @@ -1,131 +1,131 @@ -# Appendix A: Wiring a Code Reviewer — Complete Walkthrough - -> End-to-end example of adding a code reviewer to your squad and wiring their gate so it actually gets enforced. This walkthrough addresses a common failure: a reviewer is on the roster but never reviews a single PR because the gate wasn't wired. - -## The Problem This Solves - -Adding a reviewer to `team.md` gives them an identity. It does NOT: -- Tell the coordinator to route PRs to them -- Prevent PRs from being merged without their approval -- Prevent issues from being closed before review happens - -**What goes wrong without enforcement:** A reviewer can be on the roster as "Reviewer" from day one. Their charter says they review PRs. The routing table says "PR code review → {ReviewerName}." But PRs get merged and issues get closed without them ever being spawned. Why? - -Because the routing table says WHO handles what — it's for incoming requests ("review PR #42"). It does NOT say "after every agent completes work, route their output to {ReviewerName}." The coordinator routes work TO agents, but nothing tells it to route COMPLETED work to a reviewer. The "After Agent Work" flow in `squad.agent.md` says: collect results → present → spawn Scribe. No review step. - -**The fix has three layers:** - -| Layer | What it does | Where it lives | -|-------|-------------|----------------| -| Identity | Reviewer exists and knows how to review | `team.md` roster + `charter.md` | -| Routing | User can explicitly request "review this" | `routing.md` routing table | -| **Enforcement** | Coordinator MUST route every PR to reviewer before merge | `routing.md` Rules section + `issue-lifecycle.md` post-work steps | - -Most squads get layers 1 and 2 right. Layer 3 — enforcement — is what's usually missing. - -## Step-by-Step Walkthrough - -### Step 1: Create the reviewer's identity - -Create `.squad/agents/{name}/charter.md`: - -```markdown -# {Name} — Code Reviewer - -## Identity -- **Name:** {Name} -- **Role:** Code Reviewer -- **Expertise:** Code quality, correctness, test coverage, security, patterns -- **Style:** Thorough, fair, specific. Provides actionable feedback. - -## What I Own -- Reviewing PRs for code quality, correctness, and test coverage -- Identifying bugs, security issues, and design problems -- Providing specific, actionable feedback (not vague suggestions) - -## How I Review -1. Read the PR diff completely -2. Check: does it do what the issue asked for? -3. Check: are there tests? Do they cover the important cases? -4. Check: are there bugs, edge cases, or security issues? -5. Check: does it follow project patterns and conventions? -6. Verdict: APPROVE or REJECT with specific feedback - -## Boundaries -**I handle:** Code review, PR review, quality gates -**I don't handle:** Implementation, design, research, documentation - -## On REJECT -I provide specific feedback: what's wrong, why, and what to do instead. -The original author fixes their work. I re-review after fixes. -``` - -Create `.squad/agents/{name}/history.md` seeded with project context. - -### Step 2: Add to team.md roster - -```markdown -| 👑 {Name} | Code Reviewer | `.squad/agents/{name}/charter.md` | ✅ Active | -``` - -### Step 3: Add routing table entry - -In `routing.md` → routing table: - -```markdown -| PR code review | 👑 {Name} | — | "Review PR #42", code quality, finding reports | -``` - -**⚠️ This is necessary but NOT sufficient.** This only handles explicit review requests. It does NOT enforce automatic review of every PR. - -### Step 4: Add enforcement rule (THIS IS THE CRITICAL STEP) - -In `routing.md` → `## Rules` section, add a numbered rule: - -```markdown -N. **{Name} PR Gate** — every PR created by any agent MUST be reviewed by {Name} - before merge. The coordinator spawns {Name} (sync) with the PR diff after - the author pushes and creates the PR. On REJECT, the original author addresses - feedback. On APPROVE, the coordinator merges via `gh pr merge`. No PR merges - without {Name}'s approval. -``` - -**Why this works when the routing table alone didn't:** The routing table is for matching incoming work to agents. Rules are behavioral constraints the coordinator must follow AFTER work completes. The rule says "after a PR exists, you MUST do X before proceeding." The routing table says "if someone asks for a review, route to X." - -### Step 5: Wire into issue-lifecycle.md - -In `.squad/templates/issue-lifecycle.md`, the "Coordinator Post-Work Steps" section should reference your reviewer by name: - -```markdown -4. **Route to reviewer.** Spawn {Name} (sync) with the PR diff for code review. -``` - -This is the operational detail — the step-by-step instructions the coordinator follows after an agent completes issue work. The routing rule (Step 4) is the mandate; the lifecycle template is the procedure. - -### Step 6: Add to casting registry - -Update `.squad/casting/registry.json` with the new entry. - -### Step 7: Verify - -Ask yourself these questions: - -- [ ] If a clean session coordinator reads `routing.md` Rules, will it know to route PRs to this reviewer? → Check rule N exists. -- [ ] If an agent completes work and pushes a PR, does the coordinator's post-work flow include a review step? → Check `issue-lifecycle.md` step 4. -- [ ] Can the coordinator merge a PR without the reviewer's approval? → The rule should say "No PR merges without {Name}'s approval." -- [ ] Can the coordinator close an issue without a merged PR? → Check the issue closure rule exists. - -If any answer is wrong, you have a gap. - -## What Each File Controls (Summary) - -| File | What it contributes to the reviewer gate | -|------|----------------------------------------| -| `charter.md` | WHO the reviewer is and HOW they review | -| `team.md` | That the reviewer EXISTS on the team | -| `routing.md` routing table | That explicit review requests go to this reviewer | -| `routing.md` Rules section | That the coordinator MUST route EVERY PR to this reviewer (enforcement) | -| `issue-lifecycle.md` | The step-by-step procedure for the post-work review flow | -| `casting/registry.json` | Persistent name tracking | - -**Remove any one of these and the gate has a hole.** The most commonly missed piece is the Rules section entry (Step 4). +# Appendix A: Wiring a Code Reviewer — Complete Walkthrough + +> End-to-end example of adding a code reviewer to your squad and wiring their gate so it actually gets enforced. This walkthrough addresses a common failure: a reviewer is on the roster but never reviews a single PR because the gate wasn't wired. + +## The Problem This Solves + +Adding a reviewer to `team.md` gives them an identity. It does NOT: +- Tell the coordinator to route PRs to them +- Prevent PRs from being merged without their approval +- Prevent issues from being closed before review happens + +**What goes wrong without enforcement:** A reviewer can be on the roster as "Reviewer" from day one. Their charter says they review PRs. The routing table says "PR code review → {ReviewerName}." But PRs get merged and issues get closed without them ever being spawned. Why? + +Because the routing table says WHO handles what — it's for incoming requests ("review PR #42"). It does NOT say "after every agent completes work, route their output to {ReviewerName}." The coordinator routes work TO agents, but nothing tells it to route COMPLETED work to a reviewer. The "After Agent Work" flow in `squad.agent.md` says: collect results → present → spawn Scribe. No review step. + +**The fix has three layers:** + +| Layer | What it does | Where it lives | +|-------|-------------|----------------| +| Identity | Reviewer exists and knows how to review | `team.md` roster + `charter.md` | +| Routing | User can explicitly request "review this" | `routing.md` routing table | +| **Enforcement** | Coordinator MUST route every PR to reviewer before merge | `routing.md` Rules section + `issue-lifecycle.md` post-work steps | + +Most squads get layers 1 and 2 right. Layer 3 — enforcement — is what's usually missing. + +## Step-by-Step Walkthrough + +### Step 1: Create the reviewer's identity + +Create `.squad/agents/{name}/charter.md`: + +```markdown +# {Name} — Code Reviewer + +## Identity +- **Name:** {Name} +- **Role:** Code Reviewer +- **Expertise:** Code quality, correctness, test coverage, security, patterns +- **Style:** Thorough, fair, specific. Provides actionable feedback. + +## What I Own +- Reviewing PRs for code quality, correctness, and test coverage +- Identifying bugs, security issues, and design problems +- Providing specific, actionable feedback (not vague suggestions) + +## How I Review +1. Read the PR diff completely +2. Check: does it do what the issue asked for? +3. Check: are there tests? Do they cover the important cases? +4. Check: are there bugs, edge cases, or security issues? +5. Check: does it follow project patterns and conventions? +6. Verdict: APPROVE or REJECT with specific feedback + +## Boundaries +**I handle:** Code review, PR review, quality gates +**I don't handle:** Implementation, design, research, documentation + +## On REJECT +I provide specific feedback: what's wrong, why, and what to do instead. +The original author fixes their work. I re-review after fixes. +``` + +Create `.squad/agents/{name}/history.md` seeded with project context. + +### Step 2: Add to team.md roster + +```markdown +| 👑 {Name} | Code Reviewer | `.squad/agents/{name}/charter.md` | ✅ Active | +``` + +### Step 3: Add routing table entry + +In `routing.md` → routing table: + +```markdown +| PR code review | 👑 {Name} | — | "Review PR #42", code quality, finding reports | +``` + +**⚠️ This is necessary but NOT sufficient.** This only handles explicit review requests. It does NOT enforce automatic review of every PR. + +### Step 4: Add enforcement rule (THIS IS THE CRITICAL STEP) + +In `routing.md` → `## Rules` section, add a numbered rule: + +```markdown +N. **{Name} PR Gate** — every PR created by any agent MUST be reviewed by {Name} + before merge. The coordinator spawns {Name} (sync) with the PR diff after + the author pushes and creates the PR. On REJECT, the original author addresses + feedback. On APPROVE, the coordinator merges via `gh pr merge`. No PR merges + without {Name}'s approval. +``` + +**Why this works when the routing table alone didn't:** The routing table is for matching incoming work to agents. Rules are behavioral constraints the coordinator must follow AFTER work completes. The rule says "after a PR exists, you MUST do X before proceeding." The routing table says "if someone asks for a review, route to X." + +### Step 5: Wire into issue-lifecycle.md + +In `.squad/templates/issue-lifecycle.md`, the "Coordinator Post-Work Steps" section should reference your reviewer by name: + +```markdown +4. **Route to reviewer.** Spawn {Name} (sync) with the PR diff for code review. +``` + +This is the operational detail — the step-by-step instructions the coordinator follows after an agent completes issue work. The routing rule (Step 4) is the mandate; the lifecycle template is the procedure. + +### Step 6: Add to casting registry + +Update `.squad/casting/registry.json` with the new entry. + +### Step 7: Verify + +Ask yourself these questions: + +- [ ] If a clean session coordinator reads `routing.md` Rules, will it know to route PRs to this reviewer? → Check rule N exists. +- [ ] If an agent completes work and pushes a PR, does the coordinator's post-work flow include a review step? → Check `issue-lifecycle.md` step 4. +- [ ] Can the coordinator merge a PR without the reviewer's approval? → The rule should say "No PR merges without {Name}'s approval." +- [ ] Can the coordinator close an issue without a merged PR? → Check the issue closure rule exists. + +If any answer is wrong, you have a gap. + +## What Each File Controls (Summary) + +| File | What it contributes to the reviewer gate | +|------|----------------------------------------| +| `charter.md` | WHO the reviewer is and HOW they review | +| `team.md` | That the reviewer EXISTS on the team | +| `routing.md` routing table | That explicit review requests go to this reviewer | +| `routing.md` Rules section | That the coordinator MUST route EVERY PR to this reviewer (enforcement) | +| `issue-lifecycle.md` | The step-by-step procedure for the post-work review flow | +| `casting/registry.json` | Persistent name tracking | + +**Remove any one of these and the gate has a hole.** The most commonly missed piece is the Rules section entry (Step 4). diff --git a/packages/squad-sdk/templates/workflow-wiring-appendix-b-documenter.md b/packages/squad-sdk/templates/workflow-wiring-appendix-b-documenter.md index 870e61ccb..fb8cd26aa 100644 --- a/packages/squad-sdk/templates/workflow-wiring-appendix-b-documenter.md +++ b/packages/squad-sdk/templates/workflow-wiring-appendix-b-documenter.md @@ -1,140 +1,140 @@ -# Appendix B: Wiring a Documenter/Librarian — Complete Walkthrough - -> End-to-end example of adding a documenter role that ensures significant changes are documented. This is a FOLLOW-UP TRIGGER pattern — not a gate (which blocks), but an automatic downstream task that fires after work completes. - -## The Problem This Solves - -Your project has agents building features, fixing bugs, and writing tools. But nobody documents what was built, how to use it, or what changed. Documentation happens only when someone explicitly asks — and by then, the context is lost. - -A documenter/librarian role solves this by automatically evaluating whether completed work needs documentation and producing it if so. - -## Gate vs Follow-Up Trigger - -| Pattern | Blocks work? | When it runs | Example | -|---------|-------------|-------------|---------| -| **Gate** (Appendix A) | Yes — work cannot proceed without approval | Before merge | Code reviewer must approve PR | -| **Follow-up trigger** | No — work proceeds, documentation happens in parallel | After merge | Documenter evaluates if docs are needed | - -A documenter is typically a follow-up trigger, not a gate. You don't want documentation review to block a hotfix from merging. But you DO want documentation to happen automatically after significant changes. - -## Step-by-Step Walkthrough - -### Step 1: Create the documenter's identity - -Create `.squad/agents/{name}/charter.md`: - -```markdown -# {Name} — Documenter - -## Identity -- **Name:** {Name} -- **Role:** Documenter / Librarian -- **Expertise:** Documentation, guides, READMEs, changelogs, knowledge management -- **Style:** Clear, thorough, user-focused. Makes complex things understandable. - -## What I Own -- Evaluating whether completed work needs documentation -- Writing/updating READMEs, guides, and runbooks -- Maintaining a docs index so nothing gets lost -- Summarizing design decisions and architectural changes - -## How I Work -1. Read the PR diff or agent output -2. Assess: does this change user-facing behavior? Add a new feature? Change configuration? -3. If yes: write or update the relevant documentation -4. If no: report "no docs needed" with brief justification - -## Boundaries -**I handle:** Documentation, guides, READMEs, summaries, knowledge management -**I don't handle:** Code implementation, code review, research, operations -``` - -Create `.squad/agents/{name}/history.md` seeded with project context. - -### Step 2: Add to team.md roster - -```markdown -| 📝 {Name} | Documenter | `.squad/agents/{name}/charter.md` | ✅ Active | -``` - -### Step 3: Add routing table entry - -In `routing.md` → routing table: - -```markdown -| Documentation, reports, summaries | 📝 {Name} | `docs/` | "Write docs for X", "Summarize this", guides, READMEs | -``` - -### Step 4: Add follow-up trigger rule - -In `routing.md` → `## Rules` section, add a numbered rule: - -```markdown -N. **Documentation follow-up** — after any PR is merged that adds or modifies - user-facing features, scripts, tools, or configuration, the coordinator - spawns {Name} (background) to evaluate whether documentation is needed. - {Name} reads the merged PR diff and either writes/updates docs or reports - "no docs needed." This is a follow-up, not a gate — it does not block - the merge. -``` - -**Why a rule and not a ceremony:** Ceremonies are structured multi-participant meetings. This is a single-agent follow-up task. A routing rule is simpler and more appropriate. - -**Why background, not sync:** Documentation doesn't block other work. The documenter runs in parallel with whatever comes next. - -### Step 5: Wire into the coordinator's post-merge flow - -This is the trickiest part. The coordinator's After Agent Work flow doesn't currently have a "post-merge" hook. You wire this through the issue-lifecycle template. - -In `.squad/templates/issue-lifecycle.md`, after the merge step, add: - -```markdown -8. **Documentation follow-up.** After merge, check routing.md Rules for - documentation follow-up rule. If present, spawn the documenter (background) - with the merged PR diff to evaluate whether docs are needed. -``` - -Alternatively, you can wire this as an `after` ceremony in `ceremonies.md`: - -```yaml -- name: "Documentation Check" - when: "after" - condition: "PR merged that adds features, scripts, tools, or config changes" - facilitator: "{DocumenterName}" - participants: ["{DocumenterName}"] - output: "Docs written/updated, or 'no docs needed' with justification" -``` - -### Step 6: Worktree for doc changes - -If the documenter produces files, they need a worktree — docs are files too. The coordinator should: -1. Create a worktree for the doc update (e.g., `squad/{issue}-docs`) -2. The documenter commits and pushes -3. A PR is created for the docs -4. The docs PR goes through the normal review flow (including the code reviewer if you have one) - -This means doc changes also get reviewed. The documenter is not exempt from the review gate. - -### Step 7: Add to casting registry - -Update `.squad/casting/registry.json` with the new entry. - -### Step 8: Verify - -- [ ] After a feature PR merges, does the coordinator spawn the documenter? → Check the routing rule exists. -- [ ] Does the documenter get a worktree for their work? → Check the worktree rule covers docs. -- [ ] Do doc changes go through the review gate? → They should — docs are files, files need PRs, PRs need review. -- [ ] Is the follow-up non-blocking? → The documenter should be background, not sync. - -## What Each File Controls (Summary) - -| File | What it contributes | -|------|-------------------| -| `charter.md` | WHO the documenter is and HOW they evaluate | -| `team.md` | That the documenter EXISTS | -| `routing.md` routing table | That explicit doc requests go to this member | -| `routing.md` Rules section | That the coordinator MUST spawn docs evaluation after merges (enforcement) | -| `issue-lifecycle.md` or `ceremonies.md` | The procedural hook: when exactly the follow-up fires | -| `casting/registry.json` | Persistent name tracking | - -**The most commonly missed piece:** The Rules section entry (Step 4). Without it, the documenter only runs when someone explicitly says "write docs for X." The whole point is that it runs automatically. +# Appendix B: Wiring a Documenter/Librarian — Complete Walkthrough + +> End-to-end example of adding a documenter role that ensures significant changes are documented. This is a FOLLOW-UP TRIGGER pattern — not a gate (which blocks), but an automatic downstream task that fires after work completes. + +## The Problem This Solves + +Your project has agents building features, fixing bugs, and writing tools. But nobody documents what was built, how to use it, or what changed. Documentation happens only when someone explicitly asks — and by then, the context is lost. + +A documenter/librarian role solves this by automatically evaluating whether completed work needs documentation and producing it if so. + +## Gate vs Follow-Up Trigger + +| Pattern | Blocks work? | When it runs | Example | +|---------|-------------|-------------|---------| +| **Gate** (Appendix A) | Yes — work cannot proceed without approval | Before merge | Code reviewer must approve PR | +| **Follow-up trigger** | No — work proceeds, documentation happens in parallel | After merge | Documenter evaluates if docs are needed | + +A documenter is typically a follow-up trigger, not a gate. You don't want documentation review to block a hotfix from merging. But you DO want documentation to happen automatically after significant changes. + +## Step-by-Step Walkthrough + +### Step 1: Create the documenter's identity + +Create `.squad/agents/{name}/charter.md`: + +```markdown +# {Name} — Documenter + +## Identity +- **Name:** {Name} +- **Role:** Documenter / Librarian +- **Expertise:** Documentation, guides, READMEs, changelogs, knowledge management +- **Style:** Clear, thorough, user-focused. Makes complex things understandable. + +## What I Own +- Evaluating whether completed work needs documentation +- Writing/updating READMEs, guides, and runbooks +- Maintaining a docs index so nothing gets lost +- Summarizing design decisions and architectural changes + +## How I Work +1. Read the PR diff or agent output +2. Assess: does this change user-facing behavior? Add a new feature? Change configuration? +3. If yes: write or update the relevant documentation +4. If no: report "no docs needed" with brief justification + +## Boundaries +**I handle:** Documentation, guides, READMEs, summaries, knowledge management +**I don't handle:** Code implementation, code review, research, operations +``` + +Create `.squad/agents/{name}/history.md` seeded with project context. + +### Step 2: Add to team.md roster + +```markdown +| 📝 {Name} | Documenter | `.squad/agents/{name}/charter.md` | ✅ Active | +``` + +### Step 3: Add routing table entry + +In `routing.md` → routing table: + +```markdown +| Documentation, reports, summaries | 📝 {Name} | `docs/` | "Write docs for X", "Summarize this", guides, READMEs | +``` + +### Step 4: Add follow-up trigger rule + +In `routing.md` → `## Rules` section, add a numbered rule: + +```markdown +N. **Documentation follow-up** — after any PR is merged that adds or modifies + user-facing features, scripts, tools, or configuration, the coordinator + spawns {Name} (background) to evaluate whether documentation is needed. + {Name} reads the merged PR diff and either writes/updates docs or reports + "no docs needed." This is a follow-up, not a gate — it does not block + the merge. +``` + +**Why a rule and not a ceremony:** Ceremonies are structured multi-participant meetings. This is a single-agent follow-up task. A routing rule is simpler and more appropriate. + +**Why background, not sync:** Documentation doesn't block other work. The documenter runs in parallel with whatever comes next. + +### Step 5: Wire into the coordinator's post-merge flow + +This is the trickiest part. The coordinator's After Agent Work flow doesn't currently have a "post-merge" hook. You wire this through the issue-lifecycle template. + +In `.squad/templates/issue-lifecycle.md`, after the merge step, add: + +```markdown +8. **Documentation follow-up.** After merge, check routing.md Rules for + documentation follow-up rule. If present, spawn the documenter (background) + with the merged PR diff to evaluate whether docs are needed. +``` + +Alternatively, you can wire this as an `after` ceremony in `ceremonies.md`: + +```yaml +- name: "Documentation Check" + when: "after" + condition: "PR merged that adds features, scripts, tools, or config changes" + facilitator: "{DocumenterName}" + participants: ["{DocumenterName}"] + output: "Docs written/updated, or 'no docs needed' with justification" +``` + +### Step 6: Worktree for doc changes + +If the documenter produces files, they need a worktree — docs are files too. The coordinator should: +1. Create a worktree for the doc update (e.g., `squad/{issue}-docs`) +2. The documenter commits and pushes +3. A PR is created for the docs +4. The docs PR goes through the normal review flow (including the code reviewer if you have one) + +This means doc changes also get reviewed. The documenter is not exempt from the review gate. + +### Step 7: Add to casting registry + +Update `.squad/casting/registry.json` with the new entry. + +### Step 8: Verify + +- [ ] After a feature PR merges, does the coordinator spawn the documenter? → Check the routing rule exists. +- [ ] Does the documenter get a worktree for their work? → Check the worktree rule covers docs. +- [ ] Do doc changes go through the review gate? → They should — docs are files, files need PRs, PRs need review. +- [ ] Is the follow-up non-blocking? → The documenter should be background, not sync. + +## What Each File Controls (Summary) + +| File | What it contributes | +|------|-------------------| +| `charter.md` | WHO the documenter is and HOW they evaluate | +| `team.md` | That the documenter EXISTS | +| `routing.md` routing table | That explicit doc requests go to this member | +| `routing.md` Rules section | That the coordinator MUST spawn docs evaluation after merges (enforcement) | +| `issue-lifecycle.md` or `ceremonies.md` | The procedural hook: when exactly the follow-up fires | +| `casting/registry.json` | Persistent name tracking | + +**The most commonly missed piece:** The Rules section entry (Step 4). Without it, the documenter only runs when someone explicitly says "write docs for X." The whole point is that it runs automatically. diff --git a/packages/squad-sdk/templates/workflow-wiring-guide.md b/packages/squad-sdk/templates/workflow-wiring-guide.md index e6b775ae1..853a13d0a 100644 --- a/packages/squad-sdk/templates/workflow-wiring-guide.md +++ b/packages/squad-sdk/templates/workflow-wiring-guide.md @@ -1,276 +1,276 @@ -# Squad Workflow Wiring Guide - -> How to wire up new team members, reviewer gates, and custom workflows so they actually get enforced by the coordinator — even in a clean session with no prior memory. - -## Why This Guide Exists - -The Squad framework (`squad.agent.md`) provides generic orchestration primitives. **It does not prescribe a specific workflow.** Your project's workflow — whether that's "all code goes through PRs and reviews" or "just commit to main" — must be wired into project-level configuration files. - -If a workflow rule exists only in someone's memory, in a chat transcript, or in `decisions.md` but NOT in a configuration file the coordinator reads at decision time — **it will not be followed in a clean session.** - -### Why Existing Patterns Aren't Enough - -The Squad framework already has concepts for routing tables, reviewer roles, and ceremonies. But having these concepts does NOT mean they work automatically: - -- **Adding a reviewer to the roster ≠ enforcing reviews.** A reviewer can be on the roster with "Reviewer" as their role and never review a single PR — because no RULE in `routing.md` tells the coordinator to route PRs to them. The roster says WHO exists. Rules say WHAT they enforce. - -- **Capturing a decision ≠ enforcing it.** `decisions.md` may contain "every change must go through a PR" and "only {ReviewerName} closes PRs." These can get buried in a large file that the coordinator reads for context but doesn't treat as enforcement rules. A decision is a historical record. A routing rule is an enforceable constraint. - -- **Describing a lifecycle ≠ wiring it.** `squad.agent.md` describes issue→branch→PR→review→merge. But if the After Agent Work section (the flow the coordinator actually follows after every agent completes) has no push/PR/review step, the lifecycle is described conceptually but never connected to the coordinator's actual decision flow. - -**The pattern that works:** A numbered rule in `routing.md` → Rules section. The coordinator reads this section, treats each rule as a constraint, and follows them. If your workflow isn't a numbered rule, it's a suggestion. - ---- - -## Configuration Surface Area - -The coordinator reads these files to decide how to behave. If your workflow isn't encoded in one of these, it doesn't exist. - -| File | What It Controls | Read When | -|------|-----------------|-----------| -| `routing.md` | WHO handles what, behavioral RULES, reviewer GATES | Every session start, before every routing decision | -| `ceremonies.md` | Auto-triggered ceremonies (before/after work batches) | Before spawning work batches, after completion | -| `templates/issue-lifecycle.md` | Git workflow: push, PR, review, merge, issue closure | When spawning agents for issue-linked work | -| Agent `charter.md` | Per-agent identity, boundaries, behavior | Inlined into every spawn prompt | -| `team.md` | Roster, member capabilities | Session start | -| `decisions.md` | Captured decisions and directives | Read by agents at spawn time | - -### How They Interact - -``` -User request arrives - → Coordinator reads routing.md (WHO handles this?) - → Coordinator checks ceremonies.md (any auto-triggered "before" ceremony?) - → Coordinator reads agent charter.md (inline into spawn prompt) - → If issue-linked: coordinator reads issue-lifecycle.md (add ISSUE CONTEXT to spawn prompt) - → Agent works - → Coordinator follows After Agent Work flow - → Coordinator checks ceremonies.md (any auto-triggered "after" ceremony?) - → Coordinator checks routing.md Rules section (any post-work rules to enforce?) -``` - -**The critical insight:** `routing.md` Rules section and `ceremonies.md` are the two enforcement mechanisms. If a rule isn't in one of these, the coordinator has no way to know about it. - ---- - -## How to Wire Up a New Team Member - -### Step 1: Create the member (files) - -``` -.squad/agents/{name}/ - charter.md ← Identity, role, boundaries, what they own - history.md ← Seeded with project context from team.md -``` - -### Step 2: Add to roster (`team.md`) - -Add a row to the `## Members` table: -``` -| {emoji} {Name} | {Role} | `.squad/agents/{name}/charter.md` | ✅ Active | -``` - -### Step 3: Add routing entry (`routing.md`) - -Add a row to the routing table: -``` -| {Work Type} | {emoji} {Name} | {Output Location} | {Examples} | -``` - -### Step 4: Add issue routing (if applicable) - -Add to the Issue Routing table in `routing.md`: -``` -| squad:{name} | {Description of work} | {emoji} {Name} | -``` - -### Step 5: Add to casting registry - -Update `.squad/casting/registry.json` with the new entry. - -### Step 6: Wire any gates (if this member is a reviewer/gate) - -**This is the step most people miss.** If the new member should review or gate other members' work, you need to wire enforcement. See "How to Wire Up a Reviewer Gate" below. - ---- - -## How to Wire Up a Reviewer Gate - -A reviewer gate means: "Agent X must review Agent Y's output before it proceeds." The framework supports this but does NOT automatically enforce it. You must wire it. - -### Option A: Routing Rule (recommended for simple gates) - -Add to `routing.md` → `## Rules` section: - -```markdown -N. **{GateName} Gate** — Every {output type} from {Author} MUST be reviewed by {ReviewerName} before {next step}. The coordinator routes {Author}'s output to {ReviewerName} (sync spawn), collects the verdict, and only proceeds if approved. On rejection, {Author} revises based on {ReviewerName}'s feedback. -``` - -**Example — reviewer for all PRs:** -```markdown -9. **{ReviewerName} PR Gate** — Every PR created by any agent MUST be reviewed by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) with the PR diff, collects APPROVE/REJECT verdict. On rejection, the original author addresses feedback. -``` - -**Example — design review gate:** -```markdown -10. **{DesignReviewer} Design Gate** — Every design doc produced by the architect MUST be reviewed by {DesignReviewer} before implementation begins. {DesignReviewer} always rejects the first draft on concept/approach. Implementation is BLOCKED until {DesignReviewer} approves. -``` - -**Why this works:** The coordinator reads the Rules section before and after every work batch. Rules are behavioral constraints the coordinator must follow. - -### Option B: Ceremony (recommended for multi-participant gates) - -Add to `ceremonies.md` using the Markdown table format the file uses: - -```markdown -## Design Review - -| Field | Value | -|-------|-------| -| **Trigger** | auto | -| **When** | before | -| **Condition** | task involves implementing a design doc | -| **Facilitator** | {DesignReviewer} | -| **Participants** | Architect, {DesignReviewer} | -| **Time budget** | focused | -| **Enabled** | ✅ yes | - -**Agenda:** -1. Read the design doc -2. Challenge the premise and approach -3. Demand alternatives and evidence -4. Verdict: APPROVE or REJECT -``` - -**Why this works:** The coordinator checks ceremonies.md for `before` ceremonies whose condition matches the current task. If matched, the ceremony runs before work begins. - -### Option A vs Option B - -| Use Case | Use Routing Rule | Use Ceremony | -|----------|-----------------|--------------| -| Simple 1-on-1 review (reviewer → author) | ✅ | Overkill | -| Multi-participant alignment (3+ agents) | Too simple | ✅ | -| Needs structured facilitation | No | ✅ | -| Must run automatically before specific work | Either works | ✅ | -| One-line behavioral constraint | ✅ | Overkill | - ---- - -## How to Wire Up an Issue Lifecycle (Git Workflow) - -This is where you define what happens after an agent completes work on a GitHub issue. The framework references `.squad/templates/issue-lifecycle.md` but does NOT create it — you must create it yourself. - -> **⚠️ This file is required if your project uses GitHub Issues Mode.** Without it, the coordinator has no post-work steps for push/PR/review and will treat agent commit as "done." - -See `.squad/templates/issue-lifecycle.md` for the full template if your project already has one. If not, create it following the pattern below. - -### Step 1: Create `templates/issue-lifecycle.md` - -Create `.squad/templates/issue-lifecycle.md` with your project's git workflow. At minimum it should include: - -- An ISSUE CONTEXT block template (for spawn prompts) -- Coordinator post-work steps (verify push → verify PR → route to reviewer → merge on approval) -- Issue closure rules (PR merge auto-close vs manual close) -- Worktree requirements (if applicable) - -### Step 2: Add enforcement rules to `routing.md` - -Add numbered rules to the `## Rules` section that reference the lifecycle: - -```markdown -N. **Issue lifecycle enforcement** — all issue-linked work follows the lifecycle - in `.squad/templates/issue-lifecycle.md`. The coordinator adds the ISSUE CONTEXT - block to spawn prompts and follows the post-work steps (verify push → verify PR - → route to reviewer → merge on approval). Read `issue-lifecycle.md` before - spawning any agent for issue work. - -N+1. **{ReviewerName} PR Gate** — every PR created by any agent MUST be reviewed - by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) - with the PR diff. On REJECT, the original author addresses feedback. On APPROVE, - the coordinator merges. No PR merges without {ReviewerName}'s approval. - -N+2. **Issue closure restriction** — issues that produced files (code, docs, scripts, - designs, tests) close ONLY via PR merge auto-close ("Closes #N" in PR body). - Never use `gh issue close` for file-producing work. Exception: tracking/strategic - issues and superseded issues may be closed with a comment. - -N+3. **Worktree for all file-producing work** — every task that creates or modifies - files (including documentation) requires a worktree. Exceptions: read-only queries, - Scribe (.squad/ state), pure analysis producing no files. -``` - -### Step 3: Verify your wiring - -After creating both files, run the verification checklist (below) to confirm a clean session coordinator would follow the lifecycle. - ---- - -## How to Wire Up a Custom Workflow Step - -If you need something that isn't a reviewer gate or issue lifecycle — for example, "always run tests before pushing" or "docs must be reviewed by the author before merge" — here's where to put it: - -### If it's a behavioral rule the coordinator should always follow: -→ Add to `routing.md` → `## Rules` section - -### If it should trigger automatically before/after specific work: -→ Add to `ceremonies.md` as a `before` or `after` ceremony - -### If it's something agents should do as part of their work: -→ Add to the agent's `charter.md` under a new section - -### If it's something that applies only to issue-linked work: -→ Add to `templates/issue-lifecycle.md` - -### If it's a team-wide constraint that should be visible to all agents: -→ Capture as a decision in `decisions.md` (via directive or decision inbox) - ---- - -## Verification Checklist - -After wiring any new member, gate, or workflow, verify: - -- [ ] **Clean session test:** Start a new session (no memory). Give a task. Does the coordinator follow the new rule? -- [ ] **File completeness:** Is the rule/gate/workflow encoded in a file the coordinator reads? (routing.md, ceremonies.md, issue-lifecycle.md, charter.md) -- [ ] **No verbal-only rules:** Is there anything the coordinator should do that's only in chat history or your memory? If yes, it will be lost on session restart. -- [ ] **Gate enforcement:** If you added a reviewer gate, does the routing.md Rules section or ceremonies.md explicitly say the coordinator must route to the reviewer? "Having a reviewer on the roster" is not the same as "enforcing that they review." -- [ ] **Issue lifecycle:** If your project uses PRs, does `templates/issue-lifecycle.md` exist? Does routing.md reference it? - ---- - -## Common Mistakes - -1. **Adding a reviewer to the roster but not wiring a gate.** Having a reviewer on the team doesn't mean they review anything. You must add a rule in routing.md that says "route PRs to {ReviewerName}." - -2. **Closing issues via `gh issue close` instead of PR merge.** If your project uses PRs, issue closure should happen via "Closes #N" in the PR body. Wire this in issue-lifecycle.md. - -3. **Writing docs/scripts directly on main.** If your project requires branches for all changes, the worktree gate must apply to ALL file-producing work — including docs. Make this explicit in routing.md Rules. - -4. **Assuming the coordinator remembers verbal instructions.** Each session starts fresh. If you told the coordinator "always use opus" in session 1, session 2 won't know unless it's in decisions.md or routing.md. - -5. **Not creating `issue-lifecycle.md`.** The framework references it but doesn't create it. If your project uses GitHub Issues Mode, create this template. - -6. **Capturing a decision but never encoding it as a rule.** `decisions.md` is a historical record. The coordinator reads it for context but doesn't treat entries as enforceable constraints. If a decision should be enforced, it must become a numbered rule in `routing.md` Rules section. - ---- - -## Decisions Audit - -Periodically scan `decisions.md` for directives that should be routing rules but aren't: - -1. Search for phrases like "always", "never", "must", "every", "required" -2. For each match, ask: "Is this enforced by a numbered rule in routing.md?" -3. If no → either add a rule, or accept that it's advisory-only -4. If yes → verify the rule text matches the decision - -This prevents `decisions.md` from becoming a graveyard of good intentions that the coordinator reads but doesn't act on. - ---- - -## Appendices - -For detailed end-to-end walkthroughs of specific wiring scenarios, see: - -- **[Appendix A: Wiring a Code Reviewer](workflow-wiring-appendix-a-code-reviewer.md)** — Full walkthrough of adding a code reviewer member and wiring their gate so it actually gets enforced. Includes every file that needs modification with exact content. - -- **[Appendix B: Wiring a Documenter/Librarian](workflow-wiring-appendix-b-documenter.md)** — Full walkthrough of adding a documenter role that ensures all significant changes are documented. Shows a follow-up trigger pattern rather than a gate pattern. +# Squad Workflow Wiring Guide + +> How to wire up new team members, reviewer gates, and custom workflows so they actually get enforced by the coordinator — even in a clean session with no prior memory. + +## Why This Guide Exists + +The Squad framework (`squad.agent.md`) provides generic orchestration primitives. **It does not prescribe a specific workflow.** Your project's workflow — whether that's "all code goes through PRs and reviews" or "just commit to main" — must be wired into project-level configuration files. + +If a workflow rule exists only in someone's memory, in a chat transcript, or in `decisions.md` but NOT in a configuration file the coordinator reads at decision time — **it will not be followed in a clean session.** + +### Why Existing Patterns Aren't Enough + +The Squad framework already has concepts for routing tables, reviewer roles, and ceremonies. But having these concepts does NOT mean they work automatically: + +- **Adding a reviewer to the roster ≠ enforcing reviews.** A reviewer can be on the roster with "Reviewer" as their role and never review a single PR — because no RULE in `routing.md` tells the coordinator to route PRs to them. The roster says WHO exists. Rules say WHAT they enforce. + +- **Capturing a decision ≠ enforcing it.** `decisions.md` may contain "every change must go through a PR" and "only {ReviewerName} closes PRs." These can get buried in a large file that the coordinator reads for context but doesn't treat as enforcement rules. A decision is a historical record. A routing rule is an enforceable constraint. + +- **Describing a lifecycle ≠ wiring it.** `squad.agent.md` describes issue→branch→PR→review→merge. But if the After Agent Work section (the flow the coordinator actually follows after every agent completes) has no push/PR/review step, the lifecycle is described conceptually but never connected to the coordinator's actual decision flow. + +**The pattern that works:** A numbered rule in `routing.md` → Rules section. The coordinator reads this section, treats each rule as a constraint, and follows them. If your workflow isn't a numbered rule, it's a suggestion. + +--- + +## Configuration Surface Area + +The coordinator reads these files to decide how to behave. If your workflow isn't encoded in one of these, it doesn't exist. + +| File | What It Controls | Read When | +|------|-----------------|-----------| +| `routing.md` | WHO handles what, behavioral RULES, reviewer GATES | Every session start, before every routing decision | +| `ceremonies.md` | Auto-triggered ceremonies (before/after work batches) | Before spawning work batches, after completion | +| `templates/issue-lifecycle.md` | Git workflow: push, PR, review, merge, issue closure | When spawning agents for issue-linked work | +| Agent `charter.md` | Per-agent identity, boundaries, behavior | Inlined into every spawn prompt | +| `team.md` | Roster, member capabilities | Session start | +| `decisions.md` | Captured decisions and directives | Read by agents at spawn time | + +### How They Interact + +``` +User request arrives + → Coordinator reads routing.md (WHO handles this?) + → Coordinator checks ceremonies.md (any auto-triggered "before" ceremony?) + → Coordinator reads agent charter.md (inline into spawn prompt) + → If issue-linked: coordinator reads issue-lifecycle.md (add ISSUE CONTEXT to spawn prompt) + → Agent works + → Coordinator follows After Agent Work flow + → Coordinator checks ceremonies.md (any auto-triggered "after" ceremony?) + → Coordinator checks routing.md Rules section (any post-work rules to enforce?) +``` + +**The critical insight:** `routing.md` Rules section and `ceremonies.md` are the two enforcement mechanisms. If a rule isn't in one of these, the coordinator has no way to know about it. + +--- + +## How to Wire Up a New Team Member + +### Step 1: Create the member (files) + +``` +.squad/agents/{name}/ + charter.md ← Identity, role, boundaries, what they own + history.md ← Seeded with project context from team.md +``` + +### Step 2: Add to roster (`team.md`) + +Add a row to the `## Members` table: +``` +| {emoji} {Name} | {Role} | `.squad/agents/{name}/charter.md` | ✅ Active | +``` + +### Step 3: Add routing entry (`routing.md`) + +Add a row to the routing table: +``` +| {Work Type} | {emoji} {Name} | {Output Location} | {Examples} | +``` + +### Step 4: Add issue routing (if applicable) + +Add to the Issue Routing table in `routing.md`: +``` +| squad:{name} | {Description of work} | {emoji} {Name} | +``` + +### Step 5: Add to casting registry + +Update `.squad/casting/registry.json` with the new entry. + +### Step 6: Wire any gates (if this member is a reviewer/gate) + +**This is the step most people miss.** If the new member should review or gate other members' work, you need to wire enforcement. See "How to Wire Up a Reviewer Gate" below. + +--- + +## How to Wire Up a Reviewer Gate + +A reviewer gate means: "Agent X must review Agent Y's output before it proceeds." The framework supports this but does NOT automatically enforce it. You must wire it. + +### Option A: Routing Rule (recommended for simple gates) + +Add to `routing.md` → `## Rules` section: + +```markdown +N. **{GateName} Gate** — Every {output type} from {Author} MUST be reviewed by {ReviewerName} before {next step}. The coordinator routes {Author}'s output to {ReviewerName} (sync spawn), collects the verdict, and only proceeds if approved. On rejection, {Author} revises based on {ReviewerName}'s feedback. +``` + +**Example — reviewer for all PRs:** +```markdown +9. **{ReviewerName} PR Gate** — Every PR created by any agent MUST be reviewed by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) with the PR diff, collects APPROVE/REJECT verdict. On rejection, the original author addresses feedback. +``` + +**Example — design review gate:** +```markdown +10. **{DesignReviewer} Design Gate** — Every design doc produced by the architect MUST be reviewed by {DesignReviewer} before implementation begins. {DesignReviewer} always rejects the first draft on concept/approach. Implementation is BLOCKED until {DesignReviewer} approves. +``` + +**Why this works:** The coordinator reads the Rules section before and after every work batch. Rules are behavioral constraints the coordinator must follow. + +### Option B: Ceremony (recommended for multi-participant gates) + +Add to `ceremonies.md` using the Markdown table format the file uses: + +```markdown +## Design Review + +| Field | Value | +|-------|-------| +| **Trigger** | auto | +| **When** | before | +| **Condition** | task involves implementing a design doc | +| **Facilitator** | {DesignReviewer} | +| **Participants** | Architect, {DesignReviewer} | +| **Time budget** | focused | +| **Enabled** | ✅ yes | + +**Agenda:** +1. Read the design doc +2. Challenge the premise and approach +3. Demand alternatives and evidence +4. Verdict: APPROVE or REJECT +``` + +**Why this works:** The coordinator checks ceremonies.md for `before` ceremonies whose condition matches the current task. If matched, the ceremony runs before work begins. + +### Option A vs Option B + +| Use Case | Use Routing Rule | Use Ceremony | +|----------|-----------------|--------------| +| Simple 1-on-1 review (reviewer → author) | ✅ | Overkill | +| Multi-participant alignment (3+ agents) | Too simple | ✅ | +| Needs structured facilitation | No | ✅ | +| Must run automatically before specific work | Either works | ✅ | +| One-line behavioral constraint | ✅ | Overkill | + +--- + +## How to Wire Up an Issue Lifecycle (Git Workflow) + +This is where you define what happens after an agent completes work on a GitHub issue. The framework references `.squad/templates/issue-lifecycle.md` but does NOT create it — you must create it yourself. + +> **⚠️ This file is required if your project uses GitHub Issues Mode.** Without it, the coordinator has no post-work steps for push/PR/review and will treat agent commit as "done." + +See `.squad/templates/issue-lifecycle.md` for the full template if your project already has one. If not, create it following the pattern below. + +### Step 1: Create `templates/issue-lifecycle.md` + +Create `.squad/templates/issue-lifecycle.md` with your project's git workflow. At minimum it should include: + +- An ISSUE CONTEXT block template (for spawn prompts) +- Coordinator post-work steps (verify push → verify PR → route to reviewer → merge on approval) +- Issue closure rules (PR merge auto-close vs manual close) +- Worktree requirements (if applicable) + +### Step 2: Add enforcement rules to `routing.md` + +Add numbered rules to the `## Rules` section that reference the lifecycle: + +```markdown +N. **Issue lifecycle enforcement** — all issue-linked work follows the lifecycle + in `.squad/templates/issue-lifecycle.md`. The coordinator adds the ISSUE CONTEXT + block to spawn prompts and follows the post-work steps (verify push → verify PR + → route to reviewer → merge on approval). Read `issue-lifecycle.md` before + spawning any agent for issue work. + +N+1. **{ReviewerName} PR Gate** — every PR created by any agent MUST be reviewed + by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) + with the PR diff. On REJECT, the original author addresses feedback. On APPROVE, + the coordinator merges. No PR merges without {ReviewerName}'s approval. + +N+2. **Issue closure restriction** — issues that produced files (code, docs, scripts, + designs, tests) close ONLY via PR merge auto-close ("Closes #N" in PR body). + Never use `gh issue close` for file-producing work. Exception: tracking/strategic + issues and superseded issues may be closed with a comment. + +N+3. **Worktree for all file-producing work** — every task that creates or modifies + files (including documentation) requires a worktree. Exceptions: read-only queries, + Scribe (.squad/ state), pure analysis producing no files. +``` + +### Step 3: Verify your wiring + +After creating both files, run the verification checklist (below) to confirm a clean session coordinator would follow the lifecycle. + +--- + +## How to Wire Up a Custom Workflow Step + +If you need something that isn't a reviewer gate or issue lifecycle — for example, "always run tests before pushing" or "docs must be reviewed by the author before merge" — here's where to put it: + +### If it's a behavioral rule the coordinator should always follow: +→ Add to `routing.md` → `## Rules` section + +### If it should trigger automatically before/after specific work: +→ Add to `ceremonies.md` as a `before` or `after` ceremony + +### If it's something agents should do as part of their work: +→ Add to the agent's `charter.md` under a new section + +### If it's something that applies only to issue-linked work: +→ Add to `templates/issue-lifecycle.md` + +### If it's a team-wide constraint that should be visible to all agents: +→ Capture as a decision in `decisions.md` (via directive or decision inbox) + +--- + +## Verification Checklist + +After wiring any new member, gate, or workflow, verify: + +- [ ] **Clean session test:** Start a new session (no memory). Give a task. Does the coordinator follow the new rule? +- [ ] **File completeness:** Is the rule/gate/workflow encoded in a file the coordinator reads? (routing.md, ceremonies.md, issue-lifecycle.md, charter.md) +- [ ] **No verbal-only rules:** Is there anything the coordinator should do that's only in chat history or your memory? If yes, it will be lost on session restart. +- [ ] **Gate enforcement:** If you added a reviewer gate, does the routing.md Rules section or ceremonies.md explicitly say the coordinator must route to the reviewer? "Having a reviewer on the roster" is not the same as "enforcing that they review." +- [ ] **Issue lifecycle:** If your project uses PRs, does `templates/issue-lifecycle.md` exist? Does routing.md reference it? + +--- + +## Common Mistakes + +1. **Adding a reviewer to the roster but not wiring a gate.** Having a reviewer on the team doesn't mean they review anything. You must add a rule in routing.md that says "route PRs to {ReviewerName}." + +2. **Closing issues via `gh issue close` instead of PR merge.** If your project uses PRs, issue closure should happen via "Closes #N" in the PR body. Wire this in issue-lifecycle.md. + +3. **Writing docs/scripts directly on main.** If your project requires branches for all changes, the worktree gate must apply to ALL file-producing work — including docs. Make this explicit in routing.md Rules. + +4. **Assuming the coordinator remembers verbal instructions.** Each session starts fresh. If you told the coordinator "always use opus" in session 1, session 2 won't know unless it's in decisions.md or routing.md. + +5. **Not creating `issue-lifecycle.md`.** The framework references it but doesn't create it. If your project uses GitHub Issues Mode, create this template. + +6. **Capturing a decision but never encoding it as a rule.** `decisions.md` is a historical record. The coordinator reads it for context but doesn't treat entries as enforceable constraints. If a decision should be enforced, it must become a numbered rule in `routing.md` Rules section. + +--- + +## Decisions Audit + +Periodically scan `decisions.md` for directives that should be routing rules but aren't: + +1. Search for phrases like "always", "never", "must", "every", "required" +2. For each match, ask: "Is this enforced by a numbered rule in routing.md?" +3. If no → either add a rule, or accept that it's advisory-only +4. If yes → verify the rule text matches the decision + +This prevents `decisions.md` from becoming a graveyard of good intentions that the coordinator reads but doesn't act on. + +--- + +## Appendices + +For detailed end-to-end walkthroughs of specific wiring scenarios, see: + +- **[Appendix A: Wiring a Code Reviewer](workflow-wiring-appendix-a-code-reviewer.md)** — Full walkthrough of adding a code reviewer member and wiring their gate so it actually gets enforced. Includes every file that needs modification with exact content. + +- **[Appendix B: Wiring a Documenter/Librarian](workflow-wiring-appendix-b-documenter.md)** — Full walkthrough of adding a documenter role that ensures all significant changes are documented. Shows a follow-up trigger pattern rather than a gate pattern. diff --git a/templates/workflow-wiring-appendix-a-code-reviewer.md b/templates/workflow-wiring-appendix-a-code-reviewer.md index 324e0ef60..c447f7f9c 100644 --- a/templates/workflow-wiring-appendix-a-code-reviewer.md +++ b/templates/workflow-wiring-appendix-a-code-reviewer.md @@ -1,131 +1,131 @@ -# Appendix A: Wiring a Code Reviewer — Complete Walkthrough - -> End-to-end example of adding a code reviewer to your squad and wiring their gate so it actually gets enforced. This walkthrough addresses a common failure: a reviewer is on the roster but never reviews a single PR because the gate wasn't wired. - -## The Problem This Solves - -Adding a reviewer to `team.md` gives them an identity. It does NOT: -- Tell the coordinator to route PRs to them -- Prevent PRs from being merged without their approval -- Prevent issues from being closed before review happens - -**What goes wrong without enforcement:** A reviewer can be on the roster as "Reviewer" from day one. Their charter says they review PRs. The routing table says "PR code review → {ReviewerName}." But PRs get merged and issues get closed without them ever being spawned. Why? - -Because the routing table says WHO handles what — it's for incoming requests ("review PR #42"). It does NOT say "after every agent completes work, route their output to {ReviewerName}." The coordinator routes work TO agents, but nothing tells it to route COMPLETED work to a reviewer. The "After Agent Work" flow in `squad.agent.md` says: collect results → present → spawn Scribe. No review step. - -**The fix has three layers:** - -| Layer | What it does | Where it lives | -|-------|-------------|----------------| -| Identity | Reviewer exists and knows how to review | `team.md` roster + `charter.md` | -| Routing | User can explicitly request "review this" | `routing.md` routing table | -| **Enforcement** | Coordinator MUST route every PR to reviewer before merge | `routing.md` Rules section + `issue-lifecycle.md` post-work steps | - -Most squads get layers 1 and 2 right. Layer 3 — enforcement — is what's usually missing. - -## Step-by-Step Walkthrough - -### Step 1: Create the reviewer's identity - -Create `.squad/agents/{name}/charter.md`: - -```markdown -# {Name} — Code Reviewer - -## Identity -- **Name:** {Name} -- **Role:** Code Reviewer -- **Expertise:** Code quality, correctness, test coverage, security, patterns -- **Style:** Thorough, fair, specific. Provides actionable feedback. - -## What I Own -- Reviewing PRs for code quality, correctness, and test coverage -- Identifying bugs, security issues, and design problems -- Providing specific, actionable feedback (not vague suggestions) - -## How I Review -1. Read the PR diff completely -2. Check: does it do what the issue asked for? -3. Check: are there tests? Do they cover the important cases? -4. Check: are there bugs, edge cases, or security issues? -5. Check: does it follow project patterns and conventions? -6. Verdict: APPROVE or REJECT with specific feedback - -## Boundaries -**I handle:** Code review, PR review, quality gates -**I don't handle:** Implementation, design, research, documentation - -## On REJECT -I provide specific feedback: what's wrong, why, and what to do instead. -The original author fixes their work. I re-review after fixes. -``` - -Create `.squad/agents/{name}/history.md` seeded with project context. - -### Step 2: Add to team.md roster - -```markdown -| 👑 {Name} | Code Reviewer | `.squad/agents/{name}/charter.md` | ✅ Active | -``` - -### Step 3: Add routing table entry - -In `routing.md` → routing table: - -```markdown -| PR code review | 👑 {Name} | — | "Review PR #42", code quality, finding reports | -``` - -**⚠️ This is necessary but NOT sufficient.** This only handles explicit review requests. It does NOT enforce automatic review of every PR. - -### Step 4: Add enforcement rule (THIS IS THE CRITICAL STEP) - -In `routing.md` → `## Rules` section, add a numbered rule: - -```markdown -N. **{Name} PR Gate** — every PR created by any agent MUST be reviewed by {Name} - before merge. The coordinator spawns {Name} (sync) with the PR diff after - the author pushes and creates the PR. On REJECT, the original author addresses - feedback. On APPROVE, the coordinator merges via `gh pr merge`. No PR merges - without {Name}'s approval. -``` - -**Why this works when the routing table alone didn't:** The routing table is for matching incoming work to agents. Rules are behavioral constraints the coordinator must follow AFTER work completes. The rule says "after a PR exists, you MUST do X before proceeding." The routing table says "if someone asks for a review, route to X." - -### Step 5: Wire into issue-lifecycle.md - -In `.squad/templates/issue-lifecycle.md`, the "Coordinator Post-Work Steps" section should reference your reviewer by name: - -```markdown -4. **Route to reviewer.** Spawn {Name} (sync) with the PR diff for code review. -``` - -This is the operational detail — the step-by-step instructions the coordinator follows after an agent completes issue work. The routing rule (Step 4) is the mandate; the lifecycle template is the procedure. - -### Step 6: Add to casting registry - -Update `.squad/casting/registry.json` with the new entry. - -### Step 7: Verify - -Ask yourself these questions: - -- [ ] If a clean session coordinator reads `routing.md` Rules, will it know to route PRs to this reviewer? → Check rule N exists. -- [ ] If an agent completes work and pushes a PR, does the coordinator's post-work flow include a review step? → Check `issue-lifecycle.md` step 4. -- [ ] Can the coordinator merge a PR without the reviewer's approval? → The rule should say "No PR merges without {Name}'s approval." -- [ ] Can the coordinator close an issue without a merged PR? → Check the issue closure rule exists. - -If any answer is wrong, you have a gap. - -## What Each File Controls (Summary) - -| File | What it contributes to the reviewer gate | -|------|----------------------------------------| -| `charter.md` | WHO the reviewer is and HOW they review | -| `team.md` | That the reviewer EXISTS on the team | -| `routing.md` routing table | That explicit review requests go to this reviewer | -| `routing.md` Rules section | That the coordinator MUST route EVERY PR to this reviewer (enforcement) | -| `issue-lifecycle.md` | The step-by-step procedure for the post-work review flow | -| `casting/registry.json` | Persistent name tracking | - -**Remove any one of these and the gate has a hole.** The most commonly missed piece is the Rules section entry (Step 4). +# Appendix A: Wiring a Code Reviewer — Complete Walkthrough + +> End-to-end example of adding a code reviewer to your squad and wiring their gate so it actually gets enforced. This walkthrough addresses a common failure: a reviewer is on the roster but never reviews a single PR because the gate wasn't wired. + +## The Problem This Solves + +Adding a reviewer to `team.md` gives them an identity. It does NOT: +- Tell the coordinator to route PRs to them +- Prevent PRs from being merged without their approval +- Prevent issues from being closed before review happens + +**What goes wrong without enforcement:** A reviewer can be on the roster as "Reviewer" from day one. Their charter says they review PRs. The routing table says "PR code review → {ReviewerName}." But PRs get merged and issues get closed without them ever being spawned. Why? + +Because the routing table says WHO handles what — it's for incoming requests ("review PR #42"). It does NOT say "after every agent completes work, route their output to {ReviewerName}." The coordinator routes work TO agents, but nothing tells it to route COMPLETED work to a reviewer. The "After Agent Work" flow in `squad.agent.md` says: collect results → present → spawn Scribe. No review step. + +**The fix has three layers:** + +| Layer | What it does | Where it lives | +|-------|-------------|----------------| +| Identity | Reviewer exists and knows how to review | `team.md` roster + `charter.md` | +| Routing | User can explicitly request "review this" | `routing.md` routing table | +| **Enforcement** | Coordinator MUST route every PR to reviewer before merge | `routing.md` Rules section + `issue-lifecycle.md` post-work steps | + +Most squads get layers 1 and 2 right. Layer 3 — enforcement — is what's usually missing. + +## Step-by-Step Walkthrough + +### Step 1: Create the reviewer's identity + +Create `.squad/agents/{name}/charter.md`: + +```markdown +# {Name} — Code Reviewer + +## Identity +- **Name:** {Name} +- **Role:** Code Reviewer +- **Expertise:** Code quality, correctness, test coverage, security, patterns +- **Style:** Thorough, fair, specific. Provides actionable feedback. + +## What I Own +- Reviewing PRs for code quality, correctness, and test coverage +- Identifying bugs, security issues, and design problems +- Providing specific, actionable feedback (not vague suggestions) + +## How I Review +1. Read the PR diff completely +2. Check: does it do what the issue asked for? +3. Check: are there tests? Do they cover the important cases? +4. Check: are there bugs, edge cases, or security issues? +5. Check: does it follow project patterns and conventions? +6. Verdict: APPROVE or REJECT with specific feedback + +## Boundaries +**I handle:** Code review, PR review, quality gates +**I don't handle:** Implementation, design, research, documentation + +## On REJECT +I provide specific feedback: what's wrong, why, and what to do instead. +The original author fixes their work. I re-review after fixes. +``` + +Create `.squad/agents/{name}/history.md` seeded with project context. + +### Step 2: Add to team.md roster + +```markdown +| 👑 {Name} | Code Reviewer | `.squad/agents/{name}/charter.md` | ✅ Active | +``` + +### Step 3: Add routing table entry + +In `routing.md` → routing table: + +```markdown +| PR code review | 👑 {Name} | — | "Review PR #42", code quality, finding reports | +``` + +**⚠️ This is necessary but NOT sufficient.** This only handles explicit review requests. It does NOT enforce automatic review of every PR. + +### Step 4: Add enforcement rule (THIS IS THE CRITICAL STEP) + +In `routing.md` → `## Rules` section, add a numbered rule: + +```markdown +N. **{Name} PR Gate** — every PR created by any agent MUST be reviewed by {Name} + before merge. The coordinator spawns {Name} (sync) with the PR diff after + the author pushes and creates the PR. On REJECT, the original author addresses + feedback. On APPROVE, the coordinator merges via `gh pr merge`. No PR merges + without {Name}'s approval. +``` + +**Why this works when the routing table alone didn't:** The routing table is for matching incoming work to agents. Rules are behavioral constraints the coordinator must follow AFTER work completes. The rule says "after a PR exists, you MUST do X before proceeding." The routing table says "if someone asks for a review, route to X." + +### Step 5: Wire into issue-lifecycle.md + +In `.squad/templates/issue-lifecycle.md`, the "Coordinator Post-Work Steps" section should reference your reviewer by name: + +```markdown +4. **Route to reviewer.** Spawn {Name} (sync) with the PR diff for code review. +``` + +This is the operational detail — the step-by-step instructions the coordinator follows after an agent completes issue work. The routing rule (Step 4) is the mandate; the lifecycle template is the procedure. + +### Step 6: Add to casting registry + +Update `.squad/casting/registry.json` with the new entry. + +### Step 7: Verify + +Ask yourself these questions: + +- [ ] If a clean session coordinator reads `routing.md` Rules, will it know to route PRs to this reviewer? → Check rule N exists. +- [ ] If an agent completes work and pushes a PR, does the coordinator's post-work flow include a review step? → Check `issue-lifecycle.md` step 4. +- [ ] Can the coordinator merge a PR without the reviewer's approval? → The rule should say "No PR merges without {Name}'s approval." +- [ ] Can the coordinator close an issue without a merged PR? → Check the issue closure rule exists. + +If any answer is wrong, you have a gap. + +## What Each File Controls (Summary) + +| File | What it contributes to the reviewer gate | +|------|----------------------------------------| +| `charter.md` | WHO the reviewer is and HOW they review | +| `team.md` | That the reviewer EXISTS on the team | +| `routing.md` routing table | That explicit review requests go to this reviewer | +| `routing.md` Rules section | That the coordinator MUST route EVERY PR to this reviewer (enforcement) | +| `issue-lifecycle.md` | The step-by-step procedure for the post-work review flow | +| `casting/registry.json` | Persistent name tracking | + +**Remove any one of these and the gate has a hole.** The most commonly missed piece is the Rules section entry (Step 4). diff --git a/templates/workflow-wiring-appendix-b-documenter.md b/templates/workflow-wiring-appendix-b-documenter.md index 870e61ccb..fb8cd26aa 100644 --- a/templates/workflow-wiring-appendix-b-documenter.md +++ b/templates/workflow-wiring-appendix-b-documenter.md @@ -1,140 +1,140 @@ -# Appendix B: Wiring a Documenter/Librarian — Complete Walkthrough - -> End-to-end example of adding a documenter role that ensures significant changes are documented. This is a FOLLOW-UP TRIGGER pattern — not a gate (which blocks), but an automatic downstream task that fires after work completes. - -## The Problem This Solves - -Your project has agents building features, fixing bugs, and writing tools. But nobody documents what was built, how to use it, or what changed. Documentation happens only when someone explicitly asks — and by then, the context is lost. - -A documenter/librarian role solves this by automatically evaluating whether completed work needs documentation and producing it if so. - -## Gate vs Follow-Up Trigger - -| Pattern | Blocks work? | When it runs | Example | -|---------|-------------|-------------|---------| -| **Gate** (Appendix A) | Yes — work cannot proceed without approval | Before merge | Code reviewer must approve PR | -| **Follow-up trigger** | No — work proceeds, documentation happens in parallel | After merge | Documenter evaluates if docs are needed | - -A documenter is typically a follow-up trigger, not a gate. You don't want documentation review to block a hotfix from merging. But you DO want documentation to happen automatically after significant changes. - -## Step-by-Step Walkthrough - -### Step 1: Create the documenter's identity - -Create `.squad/agents/{name}/charter.md`: - -```markdown -# {Name} — Documenter - -## Identity -- **Name:** {Name} -- **Role:** Documenter / Librarian -- **Expertise:** Documentation, guides, READMEs, changelogs, knowledge management -- **Style:** Clear, thorough, user-focused. Makes complex things understandable. - -## What I Own -- Evaluating whether completed work needs documentation -- Writing/updating READMEs, guides, and runbooks -- Maintaining a docs index so nothing gets lost -- Summarizing design decisions and architectural changes - -## How I Work -1. Read the PR diff or agent output -2. Assess: does this change user-facing behavior? Add a new feature? Change configuration? -3. If yes: write or update the relevant documentation -4. If no: report "no docs needed" with brief justification - -## Boundaries -**I handle:** Documentation, guides, READMEs, summaries, knowledge management -**I don't handle:** Code implementation, code review, research, operations -``` - -Create `.squad/agents/{name}/history.md` seeded with project context. - -### Step 2: Add to team.md roster - -```markdown -| 📝 {Name} | Documenter | `.squad/agents/{name}/charter.md` | ✅ Active | -``` - -### Step 3: Add routing table entry - -In `routing.md` → routing table: - -```markdown -| Documentation, reports, summaries | 📝 {Name} | `docs/` | "Write docs for X", "Summarize this", guides, READMEs | -``` - -### Step 4: Add follow-up trigger rule - -In `routing.md` → `## Rules` section, add a numbered rule: - -```markdown -N. **Documentation follow-up** — after any PR is merged that adds or modifies - user-facing features, scripts, tools, or configuration, the coordinator - spawns {Name} (background) to evaluate whether documentation is needed. - {Name} reads the merged PR diff and either writes/updates docs or reports - "no docs needed." This is a follow-up, not a gate — it does not block - the merge. -``` - -**Why a rule and not a ceremony:** Ceremonies are structured multi-participant meetings. This is a single-agent follow-up task. A routing rule is simpler and more appropriate. - -**Why background, not sync:** Documentation doesn't block other work. The documenter runs in parallel with whatever comes next. - -### Step 5: Wire into the coordinator's post-merge flow - -This is the trickiest part. The coordinator's After Agent Work flow doesn't currently have a "post-merge" hook. You wire this through the issue-lifecycle template. - -In `.squad/templates/issue-lifecycle.md`, after the merge step, add: - -```markdown -8. **Documentation follow-up.** After merge, check routing.md Rules for - documentation follow-up rule. If present, spawn the documenter (background) - with the merged PR diff to evaluate whether docs are needed. -``` - -Alternatively, you can wire this as an `after` ceremony in `ceremonies.md`: - -```yaml -- name: "Documentation Check" - when: "after" - condition: "PR merged that adds features, scripts, tools, or config changes" - facilitator: "{DocumenterName}" - participants: ["{DocumenterName}"] - output: "Docs written/updated, or 'no docs needed' with justification" -``` - -### Step 6: Worktree for doc changes - -If the documenter produces files, they need a worktree — docs are files too. The coordinator should: -1. Create a worktree for the doc update (e.g., `squad/{issue}-docs`) -2. The documenter commits and pushes -3. A PR is created for the docs -4. The docs PR goes through the normal review flow (including the code reviewer if you have one) - -This means doc changes also get reviewed. The documenter is not exempt from the review gate. - -### Step 7: Add to casting registry - -Update `.squad/casting/registry.json` with the new entry. - -### Step 8: Verify - -- [ ] After a feature PR merges, does the coordinator spawn the documenter? → Check the routing rule exists. -- [ ] Does the documenter get a worktree for their work? → Check the worktree rule covers docs. -- [ ] Do doc changes go through the review gate? → They should — docs are files, files need PRs, PRs need review. -- [ ] Is the follow-up non-blocking? → The documenter should be background, not sync. - -## What Each File Controls (Summary) - -| File | What it contributes | -|------|-------------------| -| `charter.md` | WHO the documenter is and HOW they evaluate | -| `team.md` | That the documenter EXISTS | -| `routing.md` routing table | That explicit doc requests go to this member | -| `routing.md` Rules section | That the coordinator MUST spawn docs evaluation after merges (enforcement) | -| `issue-lifecycle.md` or `ceremonies.md` | The procedural hook: when exactly the follow-up fires | -| `casting/registry.json` | Persistent name tracking | - -**The most commonly missed piece:** The Rules section entry (Step 4). Without it, the documenter only runs when someone explicitly says "write docs for X." The whole point is that it runs automatically. +# Appendix B: Wiring a Documenter/Librarian — Complete Walkthrough + +> End-to-end example of adding a documenter role that ensures significant changes are documented. This is a FOLLOW-UP TRIGGER pattern — not a gate (which blocks), but an automatic downstream task that fires after work completes. + +## The Problem This Solves + +Your project has agents building features, fixing bugs, and writing tools. But nobody documents what was built, how to use it, or what changed. Documentation happens only when someone explicitly asks — and by then, the context is lost. + +A documenter/librarian role solves this by automatically evaluating whether completed work needs documentation and producing it if so. + +## Gate vs Follow-Up Trigger + +| Pattern | Blocks work? | When it runs | Example | +|---------|-------------|-------------|---------| +| **Gate** (Appendix A) | Yes — work cannot proceed without approval | Before merge | Code reviewer must approve PR | +| **Follow-up trigger** | No — work proceeds, documentation happens in parallel | After merge | Documenter evaluates if docs are needed | + +A documenter is typically a follow-up trigger, not a gate. You don't want documentation review to block a hotfix from merging. But you DO want documentation to happen automatically after significant changes. + +## Step-by-Step Walkthrough + +### Step 1: Create the documenter's identity + +Create `.squad/agents/{name}/charter.md`: + +```markdown +# {Name} — Documenter + +## Identity +- **Name:** {Name} +- **Role:** Documenter / Librarian +- **Expertise:** Documentation, guides, READMEs, changelogs, knowledge management +- **Style:** Clear, thorough, user-focused. Makes complex things understandable. + +## What I Own +- Evaluating whether completed work needs documentation +- Writing/updating READMEs, guides, and runbooks +- Maintaining a docs index so nothing gets lost +- Summarizing design decisions and architectural changes + +## How I Work +1. Read the PR diff or agent output +2. Assess: does this change user-facing behavior? Add a new feature? Change configuration? +3. If yes: write or update the relevant documentation +4. If no: report "no docs needed" with brief justification + +## Boundaries +**I handle:** Documentation, guides, READMEs, summaries, knowledge management +**I don't handle:** Code implementation, code review, research, operations +``` + +Create `.squad/agents/{name}/history.md` seeded with project context. + +### Step 2: Add to team.md roster + +```markdown +| 📝 {Name} | Documenter | `.squad/agents/{name}/charter.md` | ✅ Active | +``` + +### Step 3: Add routing table entry + +In `routing.md` → routing table: + +```markdown +| Documentation, reports, summaries | 📝 {Name} | `docs/` | "Write docs for X", "Summarize this", guides, READMEs | +``` + +### Step 4: Add follow-up trigger rule + +In `routing.md` → `## Rules` section, add a numbered rule: + +```markdown +N. **Documentation follow-up** — after any PR is merged that adds or modifies + user-facing features, scripts, tools, or configuration, the coordinator + spawns {Name} (background) to evaluate whether documentation is needed. + {Name} reads the merged PR diff and either writes/updates docs or reports + "no docs needed." This is a follow-up, not a gate — it does not block + the merge. +``` + +**Why a rule and not a ceremony:** Ceremonies are structured multi-participant meetings. This is a single-agent follow-up task. A routing rule is simpler and more appropriate. + +**Why background, not sync:** Documentation doesn't block other work. The documenter runs in parallel with whatever comes next. + +### Step 5: Wire into the coordinator's post-merge flow + +This is the trickiest part. The coordinator's After Agent Work flow doesn't currently have a "post-merge" hook. You wire this through the issue-lifecycle template. + +In `.squad/templates/issue-lifecycle.md`, after the merge step, add: + +```markdown +8. **Documentation follow-up.** After merge, check routing.md Rules for + documentation follow-up rule. If present, spawn the documenter (background) + with the merged PR diff to evaluate whether docs are needed. +``` + +Alternatively, you can wire this as an `after` ceremony in `ceremonies.md`: + +```yaml +- name: "Documentation Check" + when: "after" + condition: "PR merged that adds features, scripts, tools, or config changes" + facilitator: "{DocumenterName}" + participants: ["{DocumenterName}"] + output: "Docs written/updated, or 'no docs needed' with justification" +``` + +### Step 6: Worktree for doc changes + +If the documenter produces files, they need a worktree — docs are files too. The coordinator should: +1. Create a worktree for the doc update (e.g., `squad/{issue}-docs`) +2. The documenter commits and pushes +3. A PR is created for the docs +4. The docs PR goes through the normal review flow (including the code reviewer if you have one) + +This means doc changes also get reviewed. The documenter is not exempt from the review gate. + +### Step 7: Add to casting registry + +Update `.squad/casting/registry.json` with the new entry. + +### Step 8: Verify + +- [ ] After a feature PR merges, does the coordinator spawn the documenter? → Check the routing rule exists. +- [ ] Does the documenter get a worktree for their work? → Check the worktree rule covers docs. +- [ ] Do doc changes go through the review gate? → They should — docs are files, files need PRs, PRs need review. +- [ ] Is the follow-up non-blocking? → The documenter should be background, not sync. + +## What Each File Controls (Summary) + +| File | What it contributes | +|------|-------------------| +| `charter.md` | WHO the documenter is and HOW they evaluate | +| `team.md` | That the documenter EXISTS | +| `routing.md` routing table | That explicit doc requests go to this member | +| `routing.md` Rules section | That the coordinator MUST spawn docs evaluation after merges (enforcement) | +| `issue-lifecycle.md` or `ceremonies.md` | The procedural hook: when exactly the follow-up fires | +| `casting/registry.json` | Persistent name tracking | + +**The most commonly missed piece:** The Rules section entry (Step 4). Without it, the documenter only runs when someone explicitly says "write docs for X." The whole point is that it runs automatically. diff --git a/templates/workflow-wiring-guide.md b/templates/workflow-wiring-guide.md index e6b775ae1..853a13d0a 100644 --- a/templates/workflow-wiring-guide.md +++ b/templates/workflow-wiring-guide.md @@ -1,276 +1,276 @@ -# Squad Workflow Wiring Guide - -> How to wire up new team members, reviewer gates, and custom workflows so they actually get enforced by the coordinator — even in a clean session with no prior memory. - -## Why This Guide Exists - -The Squad framework (`squad.agent.md`) provides generic orchestration primitives. **It does not prescribe a specific workflow.** Your project's workflow — whether that's "all code goes through PRs and reviews" or "just commit to main" — must be wired into project-level configuration files. - -If a workflow rule exists only in someone's memory, in a chat transcript, or in `decisions.md` but NOT in a configuration file the coordinator reads at decision time — **it will not be followed in a clean session.** - -### Why Existing Patterns Aren't Enough - -The Squad framework already has concepts for routing tables, reviewer roles, and ceremonies. But having these concepts does NOT mean they work automatically: - -- **Adding a reviewer to the roster ≠ enforcing reviews.** A reviewer can be on the roster with "Reviewer" as their role and never review a single PR — because no RULE in `routing.md` tells the coordinator to route PRs to them. The roster says WHO exists. Rules say WHAT they enforce. - -- **Capturing a decision ≠ enforcing it.** `decisions.md` may contain "every change must go through a PR" and "only {ReviewerName} closes PRs." These can get buried in a large file that the coordinator reads for context but doesn't treat as enforcement rules. A decision is a historical record. A routing rule is an enforceable constraint. - -- **Describing a lifecycle ≠ wiring it.** `squad.agent.md` describes issue→branch→PR→review→merge. But if the After Agent Work section (the flow the coordinator actually follows after every agent completes) has no push/PR/review step, the lifecycle is described conceptually but never connected to the coordinator's actual decision flow. - -**The pattern that works:** A numbered rule in `routing.md` → Rules section. The coordinator reads this section, treats each rule as a constraint, and follows them. If your workflow isn't a numbered rule, it's a suggestion. - ---- - -## Configuration Surface Area - -The coordinator reads these files to decide how to behave. If your workflow isn't encoded in one of these, it doesn't exist. - -| File | What It Controls | Read When | -|------|-----------------|-----------| -| `routing.md` | WHO handles what, behavioral RULES, reviewer GATES | Every session start, before every routing decision | -| `ceremonies.md` | Auto-triggered ceremonies (before/after work batches) | Before spawning work batches, after completion | -| `templates/issue-lifecycle.md` | Git workflow: push, PR, review, merge, issue closure | When spawning agents for issue-linked work | -| Agent `charter.md` | Per-agent identity, boundaries, behavior | Inlined into every spawn prompt | -| `team.md` | Roster, member capabilities | Session start | -| `decisions.md` | Captured decisions and directives | Read by agents at spawn time | - -### How They Interact - -``` -User request arrives - → Coordinator reads routing.md (WHO handles this?) - → Coordinator checks ceremonies.md (any auto-triggered "before" ceremony?) - → Coordinator reads agent charter.md (inline into spawn prompt) - → If issue-linked: coordinator reads issue-lifecycle.md (add ISSUE CONTEXT to spawn prompt) - → Agent works - → Coordinator follows After Agent Work flow - → Coordinator checks ceremonies.md (any auto-triggered "after" ceremony?) - → Coordinator checks routing.md Rules section (any post-work rules to enforce?) -``` - -**The critical insight:** `routing.md` Rules section and `ceremonies.md` are the two enforcement mechanisms. If a rule isn't in one of these, the coordinator has no way to know about it. - ---- - -## How to Wire Up a New Team Member - -### Step 1: Create the member (files) - -``` -.squad/agents/{name}/ - charter.md ← Identity, role, boundaries, what they own - history.md ← Seeded with project context from team.md -``` - -### Step 2: Add to roster (`team.md`) - -Add a row to the `## Members` table: -``` -| {emoji} {Name} | {Role} | `.squad/agents/{name}/charter.md` | ✅ Active | -``` - -### Step 3: Add routing entry (`routing.md`) - -Add a row to the routing table: -``` -| {Work Type} | {emoji} {Name} | {Output Location} | {Examples} | -``` - -### Step 4: Add issue routing (if applicable) - -Add to the Issue Routing table in `routing.md`: -``` -| squad:{name} | {Description of work} | {emoji} {Name} | -``` - -### Step 5: Add to casting registry - -Update `.squad/casting/registry.json` with the new entry. - -### Step 6: Wire any gates (if this member is a reviewer/gate) - -**This is the step most people miss.** If the new member should review or gate other members' work, you need to wire enforcement. See "How to Wire Up a Reviewer Gate" below. - ---- - -## How to Wire Up a Reviewer Gate - -A reviewer gate means: "Agent X must review Agent Y's output before it proceeds." The framework supports this but does NOT automatically enforce it. You must wire it. - -### Option A: Routing Rule (recommended for simple gates) - -Add to `routing.md` → `## Rules` section: - -```markdown -N. **{GateName} Gate** — Every {output type} from {Author} MUST be reviewed by {ReviewerName} before {next step}. The coordinator routes {Author}'s output to {ReviewerName} (sync spawn), collects the verdict, and only proceeds if approved. On rejection, {Author} revises based on {ReviewerName}'s feedback. -``` - -**Example — reviewer for all PRs:** -```markdown -9. **{ReviewerName} PR Gate** — Every PR created by any agent MUST be reviewed by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) with the PR diff, collects APPROVE/REJECT verdict. On rejection, the original author addresses feedback. -``` - -**Example — design review gate:** -```markdown -10. **{DesignReviewer} Design Gate** — Every design doc produced by the architect MUST be reviewed by {DesignReviewer} before implementation begins. {DesignReviewer} always rejects the first draft on concept/approach. Implementation is BLOCKED until {DesignReviewer} approves. -``` - -**Why this works:** The coordinator reads the Rules section before and after every work batch. Rules are behavioral constraints the coordinator must follow. - -### Option B: Ceremony (recommended for multi-participant gates) - -Add to `ceremonies.md` using the Markdown table format the file uses: - -```markdown -## Design Review - -| Field | Value | -|-------|-------| -| **Trigger** | auto | -| **When** | before | -| **Condition** | task involves implementing a design doc | -| **Facilitator** | {DesignReviewer} | -| **Participants** | Architect, {DesignReviewer} | -| **Time budget** | focused | -| **Enabled** | ✅ yes | - -**Agenda:** -1. Read the design doc -2. Challenge the premise and approach -3. Demand alternatives and evidence -4. Verdict: APPROVE or REJECT -``` - -**Why this works:** The coordinator checks ceremonies.md for `before` ceremonies whose condition matches the current task. If matched, the ceremony runs before work begins. - -### Option A vs Option B - -| Use Case | Use Routing Rule | Use Ceremony | -|----------|-----------------|--------------| -| Simple 1-on-1 review (reviewer → author) | ✅ | Overkill | -| Multi-participant alignment (3+ agents) | Too simple | ✅ | -| Needs structured facilitation | No | ✅ | -| Must run automatically before specific work | Either works | ✅ | -| One-line behavioral constraint | ✅ | Overkill | - ---- - -## How to Wire Up an Issue Lifecycle (Git Workflow) - -This is where you define what happens after an agent completes work on a GitHub issue. The framework references `.squad/templates/issue-lifecycle.md` but does NOT create it — you must create it yourself. - -> **⚠️ This file is required if your project uses GitHub Issues Mode.** Without it, the coordinator has no post-work steps for push/PR/review and will treat agent commit as "done." - -See `.squad/templates/issue-lifecycle.md` for the full template if your project already has one. If not, create it following the pattern below. - -### Step 1: Create `templates/issue-lifecycle.md` - -Create `.squad/templates/issue-lifecycle.md` with your project's git workflow. At minimum it should include: - -- An ISSUE CONTEXT block template (for spawn prompts) -- Coordinator post-work steps (verify push → verify PR → route to reviewer → merge on approval) -- Issue closure rules (PR merge auto-close vs manual close) -- Worktree requirements (if applicable) - -### Step 2: Add enforcement rules to `routing.md` - -Add numbered rules to the `## Rules` section that reference the lifecycle: - -```markdown -N. **Issue lifecycle enforcement** — all issue-linked work follows the lifecycle - in `.squad/templates/issue-lifecycle.md`. The coordinator adds the ISSUE CONTEXT - block to spawn prompts and follows the post-work steps (verify push → verify PR - → route to reviewer → merge on approval). Read `issue-lifecycle.md` before - spawning any agent for issue work. - -N+1. **{ReviewerName} PR Gate** — every PR created by any agent MUST be reviewed - by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) - with the PR diff. On REJECT, the original author addresses feedback. On APPROVE, - the coordinator merges. No PR merges without {ReviewerName}'s approval. - -N+2. **Issue closure restriction** — issues that produced files (code, docs, scripts, - designs, tests) close ONLY via PR merge auto-close ("Closes #N" in PR body). - Never use `gh issue close` for file-producing work. Exception: tracking/strategic - issues and superseded issues may be closed with a comment. - -N+3. **Worktree for all file-producing work** — every task that creates or modifies - files (including documentation) requires a worktree. Exceptions: read-only queries, - Scribe (.squad/ state), pure analysis producing no files. -``` - -### Step 3: Verify your wiring - -After creating both files, run the verification checklist (below) to confirm a clean session coordinator would follow the lifecycle. - ---- - -## How to Wire Up a Custom Workflow Step - -If you need something that isn't a reviewer gate or issue lifecycle — for example, "always run tests before pushing" or "docs must be reviewed by the author before merge" — here's where to put it: - -### If it's a behavioral rule the coordinator should always follow: -→ Add to `routing.md` → `## Rules` section - -### If it should trigger automatically before/after specific work: -→ Add to `ceremonies.md` as a `before` or `after` ceremony - -### If it's something agents should do as part of their work: -→ Add to the agent's `charter.md` under a new section - -### If it's something that applies only to issue-linked work: -→ Add to `templates/issue-lifecycle.md` - -### If it's a team-wide constraint that should be visible to all agents: -→ Capture as a decision in `decisions.md` (via directive or decision inbox) - ---- - -## Verification Checklist - -After wiring any new member, gate, or workflow, verify: - -- [ ] **Clean session test:** Start a new session (no memory). Give a task. Does the coordinator follow the new rule? -- [ ] **File completeness:** Is the rule/gate/workflow encoded in a file the coordinator reads? (routing.md, ceremonies.md, issue-lifecycle.md, charter.md) -- [ ] **No verbal-only rules:** Is there anything the coordinator should do that's only in chat history or your memory? If yes, it will be lost on session restart. -- [ ] **Gate enforcement:** If you added a reviewer gate, does the routing.md Rules section or ceremonies.md explicitly say the coordinator must route to the reviewer? "Having a reviewer on the roster" is not the same as "enforcing that they review." -- [ ] **Issue lifecycle:** If your project uses PRs, does `templates/issue-lifecycle.md` exist? Does routing.md reference it? - ---- - -## Common Mistakes - -1. **Adding a reviewer to the roster but not wiring a gate.** Having a reviewer on the team doesn't mean they review anything. You must add a rule in routing.md that says "route PRs to {ReviewerName}." - -2. **Closing issues via `gh issue close` instead of PR merge.** If your project uses PRs, issue closure should happen via "Closes #N" in the PR body. Wire this in issue-lifecycle.md. - -3. **Writing docs/scripts directly on main.** If your project requires branches for all changes, the worktree gate must apply to ALL file-producing work — including docs. Make this explicit in routing.md Rules. - -4. **Assuming the coordinator remembers verbal instructions.** Each session starts fresh. If you told the coordinator "always use opus" in session 1, session 2 won't know unless it's in decisions.md or routing.md. - -5. **Not creating `issue-lifecycle.md`.** The framework references it but doesn't create it. If your project uses GitHub Issues Mode, create this template. - -6. **Capturing a decision but never encoding it as a rule.** `decisions.md` is a historical record. The coordinator reads it for context but doesn't treat entries as enforceable constraints. If a decision should be enforced, it must become a numbered rule in `routing.md` Rules section. - ---- - -## Decisions Audit - -Periodically scan `decisions.md` for directives that should be routing rules but aren't: - -1. Search for phrases like "always", "never", "must", "every", "required" -2. For each match, ask: "Is this enforced by a numbered rule in routing.md?" -3. If no → either add a rule, or accept that it's advisory-only -4. If yes → verify the rule text matches the decision - -This prevents `decisions.md` from becoming a graveyard of good intentions that the coordinator reads but doesn't act on. - ---- - -## Appendices - -For detailed end-to-end walkthroughs of specific wiring scenarios, see: - -- **[Appendix A: Wiring a Code Reviewer](workflow-wiring-appendix-a-code-reviewer.md)** — Full walkthrough of adding a code reviewer member and wiring their gate so it actually gets enforced. Includes every file that needs modification with exact content. - -- **[Appendix B: Wiring a Documenter/Librarian](workflow-wiring-appendix-b-documenter.md)** — Full walkthrough of adding a documenter role that ensures all significant changes are documented. Shows a follow-up trigger pattern rather than a gate pattern. +# Squad Workflow Wiring Guide + +> How to wire up new team members, reviewer gates, and custom workflows so they actually get enforced by the coordinator — even in a clean session with no prior memory. + +## Why This Guide Exists + +The Squad framework (`squad.agent.md`) provides generic orchestration primitives. **It does not prescribe a specific workflow.** Your project's workflow — whether that's "all code goes through PRs and reviews" or "just commit to main" — must be wired into project-level configuration files. + +If a workflow rule exists only in someone's memory, in a chat transcript, or in `decisions.md` but NOT in a configuration file the coordinator reads at decision time — **it will not be followed in a clean session.** + +### Why Existing Patterns Aren't Enough + +The Squad framework already has concepts for routing tables, reviewer roles, and ceremonies. But having these concepts does NOT mean they work automatically: + +- **Adding a reviewer to the roster ≠ enforcing reviews.** A reviewer can be on the roster with "Reviewer" as their role and never review a single PR — because no RULE in `routing.md` tells the coordinator to route PRs to them. The roster says WHO exists. Rules say WHAT they enforce. + +- **Capturing a decision ≠ enforcing it.** `decisions.md` may contain "every change must go through a PR" and "only {ReviewerName} closes PRs." These can get buried in a large file that the coordinator reads for context but doesn't treat as enforcement rules. A decision is a historical record. A routing rule is an enforceable constraint. + +- **Describing a lifecycle ≠ wiring it.** `squad.agent.md` describes issue→branch→PR→review→merge. But if the After Agent Work section (the flow the coordinator actually follows after every agent completes) has no push/PR/review step, the lifecycle is described conceptually but never connected to the coordinator's actual decision flow. + +**The pattern that works:** A numbered rule in `routing.md` → Rules section. The coordinator reads this section, treats each rule as a constraint, and follows them. If your workflow isn't a numbered rule, it's a suggestion. + +--- + +## Configuration Surface Area + +The coordinator reads these files to decide how to behave. If your workflow isn't encoded in one of these, it doesn't exist. + +| File | What It Controls | Read When | +|------|-----------------|-----------| +| `routing.md` | WHO handles what, behavioral RULES, reviewer GATES | Every session start, before every routing decision | +| `ceremonies.md` | Auto-triggered ceremonies (before/after work batches) | Before spawning work batches, after completion | +| `templates/issue-lifecycle.md` | Git workflow: push, PR, review, merge, issue closure | When spawning agents for issue-linked work | +| Agent `charter.md` | Per-agent identity, boundaries, behavior | Inlined into every spawn prompt | +| `team.md` | Roster, member capabilities | Session start | +| `decisions.md` | Captured decisions and directives | Read by agents at spawn time | + +### How They Interact + +``` +User request arrives + → Coordinator reads routing.md (WHO handles this?) + → Coordinator checks ceremonies.md (any auto-triggered "before" ceremony?) + → Coordinator reads agent charter.md (inline into spawn prompt) + → If issue-linked: coordinator reads issue-lifecycle.md (add ISSUE CONTEXT to spawn prompt) + → Agent works + → Coordinator follows After Agent Work flow + → Coordinator checks ceremonies.md (any auto-triggered "after" ceremony?) + → Coordinator checks routing.md Rules section (any post-work rules to enforce?) +``` + +**The critical insight:** `routing.md` Rules section and `ceremonies.md` are the two enforcement mechanisms. If a rule isn't in one of these, the coordinator has no way to know about it. + +--- + +## How to Wire Up a New Team Member + +### Step 1: Create the member (files) + +``` +.squad/agents/{name}/ + charter.md ← Identity, role, boundaries, what they own + history.md ← Seeded with project context from team.md +``` + +### Step 2: Add to roster (`team.md`) + +Add a row to the `## Members` table: +``` +| {emoji} {Name} | {Role} | `.squad/agents/{name}/charter.md` | ✅ Active | +``` + +### Step 3: Add routing entry (`routing.md`) + +Add a row to the routing table: +``` +| {Work Type} | {emoji} {Name} | {Output Location} | {Examples} | +``` + +### Step 4: Add issue routing (if applicable) + +Add to the Issue Routing table in `routing.md`: +``` +| squad:{name} | {Description of work} | {emoji} {Name} | +``` + +### Step 5: Add to casting registry + +Update `.squad/casting/registry.json` with the new entry. + +### Step 6: Wire any gates (if this member is a reviewer/gate) + +**This is the step most people miss.** If the new member should review or gate other members' work, you need to wire enforcement. See "How to Wire Up a Reviewer Gate" below. + +--- + +## How to Wire Up a Reviewer Gate + +A reviewer gate means: "Agent X must review Agent Y's output before it proceeds." The framework supports this but does NOT automatically enforce it. You must wire it. + +### Option A: Routing Rule (recommended for simple gates) + +Add to `routing.md` → `## Rules` section: + +```markdown +N. **{GateName} Gate** — Every {output type} from {Author} MUST be reviewed by {ReviewerName} before {next step}. The coordinator routes {Author}'s output to {ReviewerName} (sync spawn), collects the verdict, and only proceeds if approved. On rejection, {Author} revises based on {ReviewerName}'s feedback. +``` + +**Example — reviewer for all PRs:** +```markdown +9. **{ReviewerName} PR Gate** — Every PR created by any agent MUST be reviewed by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) with the PR diff, collects APPROVE/REJECT verdict. On rejection, the original author addresses feedback. +``` + +**Example — design review gate:** +```markdown +10. **{DesignReviewer} Design Gate** — Every design doc produced by the architect MUST be reviewed by {DesignReviewer} before implementation begins. {DesignReviewer} always rejects the first draft on concept/approach. Implementation is BLOCKED until {DesignReviewer} approves. +``` + +**Why this works:** The coordinator reads the Rules section before and after every work batch. Rules are behavioral constraints the coordinator must follow. + +### Option B: Ceremony (recommended for multi-participant gates) + +Add to `ceremonies.md` using the Markdown table format the file uses: + +```markdown +## Design Review + +| Field | Value | +|-------|-------| +| **Trigger** | auto | +| **When** | before | +| **Condition** | task involves implementing a design doc | +| **Facilitator** | {DesignReviewer} | +| **Participants** | Architect, {DesignReviewer} | +| **Time budget** | focused | +| **Enabled** | ✅ yes | + +**Agenda:** +1. Read the design doc +2. Challenge the premise and approach +3. Demand alternatives and evidence +4. Verdict: APPROVE or REJECT +``` + +**Why this works:** The coordinator checks ceremonies.md for `before` ceremonies whose condition matches the current task. If matched, the ceremony runs before work begins. + +### Option A vs Option B + +| Use Case | Use Routing Rule | Use Ceremony | +|----------|-----------------|--------------| +| Simple 1-on-1 review (reviewer → author) | ✅ | Overkill | +| Multi-participant alignment (3+ agents) | Too simple | ✅ | +| Needs structured facilitation | No | ✅ | +| Must run automatically before specific work | Either works | ✅ | +| One-line behavioral constraint | ✅ | Overkill | + +--- + +## How to Wire Up an Issue Lifecycle (Git Workflow) + +This is where you define what happens after an agent completes work on a GitHub issue. The framework references `.squad/templates/issue-lifecycle.md` but does NOT create it — you must create it yourself. + +> **⚠️ This file is required if your project uses GitHub Issues Mode.** Without it, the coordinator has no post-work steps for push/PR/review and will treat agent commit as "done." + +See `.squad/templates/issue-lifecycle.md` for the full template if your project already has one. If not, create it following the pattern below. + +### Step 1: Create `templates/issue-lifecycle.md` + +Create `.squad/templates/issue-lifecycle.md` with your project's git workflow. At minimum it should include: + +- An ISSUE CONTEXT block template (for spawn prompts) +- Coordinator post-work steps (verify push → verify PR → route to reviewer → merge on approval) +- Issue closure rules (PR merge auto-close vs manual close) +- Worktree requirements (if applicable) + +### Step 2: Add enforcement rules to `routing.md` + +Add numbered rules to the `## Rules` section that reference the lifecycle: + +```markdown +N. **Issue lifecycle enforcement** — all issue-linked work follows the lifecycle + in `.squad/templates/issue-lifecycle.md`. The coordinator adds the ISSUE CONTEXT + block to spawn prompts and follows the post-work steps (verify push → verify PR + → route to reviewer → merge on approval). Read `issue-lifecycle.md` before + spawning any agent for issue work. + +N+1. **{ReviewerName} PR Gate** — every PR created by any agent MUST be reviewed + by {ReviewerName} before merge. The coordinator spawns {ReviewerName} (sync) + with the PR diff. On REJECT, the original author addresses feedback. On APPROVE, + the coordinator merges. No PR merges without {ReviewerName}'s approval. + +N+2. **Issue closure restriction** — issues that produced files (code, docs, scripts, + designs, tests) close ONLY via PR merge auto-close ("Closes #N" in PR body). + Never use `gh issue close` for file-producing work. Exception: tracking/strategic + issues and superseded issues may be closed with a comment. + +N+3. **Worktree for all file-producing work** — every task that creates or modifies + files (including documentation) requires a worktree. Exceptions: read-only queries, + Scribe (.squad/ state), pure analysis producing no files. +``` + +### Step 3: Verify your wiring + +After creating both files, run the verification checklist (below) to confirm a clean session coordinator would follow the lifecycle. + +--- + +## How to Wire Up a Custom Workflow Step + +If you need something that isn't a reviewer gate or issue lifecycle — for example, "always run tests before pushing" or "docs must be reviewed by the author before merge" — here's where to put it: + +### If it's a behavioral rule the coordinator should always follow: +→ Add to `routing.md` → `## Rules` section + +### If it should trigger automatically before/after specific work: +→ Add to `ceremonies.md` as a `before` or `after` ceremony + +### If it's something agents should do as part of their work: +→ Add to the agent's `charter.md` under a new section + +### If it's something that applies only to issue-linked work: +→ Add to `templates/issue-lifecycle.md` + +### If it's a team-wide constraint that should be visible to all agents: +→ Capture as a decision in `decisions.md` (via directive or decision inbox) + +--- + +## Verification Checklist + +After wiring any new member, gate, or workflow, verify: + +- [ ] **Clean session test:** Start a new session (no memory). Give a task. Does the coordinator follow the new rule? +- [ ] **File completeness:** Is the rule/gate/workflow encoded in a file the coordinator reads? (routing.md, ceremonies.md, issue-lifecycle.md, charter.md) +- [ ] **No verbal-only rules:** Is there anything the coordinator should do that's only in chat history or your memory? If yes, it will be lost on session restart. +- [ ] **Gate enforcement:** If you added a reviewer gate, does the routing.md Rules section or ceremonies.md explicitly say the coordinator must route to the reviewer? "Having a reviewer on the roster" is not the same as "enforcing that they review." +- [ ] **Issue lifecycle:** If your project uses PRs, does `templates/issue-lifecycle.md` exist? Does routing.md reference it? + +--- + +## Common Mistakes + +1. **Adding a reviewer to the roster but not wiring a gate.** Having a reviewer on the team doesn't mean they review anything. You must add a rule in routing.md that says "route PRs to {ReviewerName}." + +2. **Closing issues via `gh issue close` instead of PR merge.** If your project uses PRs, issue closure should happen via "Closes #N" in the PR body. Wire this in issue-lifecycle.md. + +3. **Writing docs/scripts directly on main.** If your project requires branches for all changes, the worktree gate must apply to ALL file-producing work — including docs. Make this explicit in routing.md Rules. + +4. **Assuming the coordinator remembers verbal instructions.** Each session starts fresh. If you told the coordinator "always use opus" in session 1, session 2 won't know unless it's in decisions.md or routing.md. + +5. **Not creating `issue-lifecycle.md`.** The framework references it but doesn't create it. If your project uses GitHub Issues Mode, create this template. + +6. **Capturing a decision but never encoding it as a rule.** `decisions.md` is a historical record. The coordinator reads it for context but doesn't treat entries as enforceable constraints. If a decision should be enforced, it must become a numbered rule in `routing.md` Rules section. + +--- + +## Decisions Audit + +Periodically scan `decisions.md` for directives that should be routing rules but aren't: + +1. Search for phrases like "always", "never", "must", "every", "required" +2. For each match, ask: "Is this enforced by a numbered rule in routing.md?" +3. If no → either add a rule, or accept that it's advisory-only +4. If yes → verify the rule text matches the decision + +This prevents `decisions.md` from becoming a graveyard of good intentions that the coordinator reads but doesn't act on. + +--- + +## Appendices + +For detailed end-to-end walkthroughs of specific wiring scenarios, see: + +- **[Appendix A: Wiring a Code Reviewer](workflow-wiring-appendix-a-code-reviewer.md)** — Full walkthrough of adding a code reviewer member and wiring their gate so it actually gets enforced. Includes every file that needs modification with exact content. + +- **[Appendix B: Wiring a Documenter/Librarian](workflow-wiring-appendix-b-documenter.md)** — Full walkthrough of adding a documenter role that ensures all significant changes are documented. Shows a follow-up trigger pattern rather than a gate pattern. From a09f73b13fca6460d0fa8d6f2bfffe012825ec3d Mon Sep 17 00:00:00 2001 From: Brady Gaster Date: Tue, 21 Jul 2026 01:08:25 -0700 Subject: [PATCH 06/15] docs(canary): reclassify dual canary as two-state payload-integrity detector Records the peer-agreed reclassification: the dual markers detect payload integrity (loaded vs truncated) within an independently-known Squad session only; "no markers" is externally unobservable and is NOT a success state. Documents the owning runtime surface for cases 3/4 (host-level untruncatable pre-ingestion identity beacon) and adds the payload-removal test requirement. squad.agent.md artifact sha256 unchanged (525a919f...). Refs #1461 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09d05820-0b7e-482b-b942-c86b1225dcf9 --- .squad-templates/copilot-instructions.md | 4 +- docs/canary-identity-matrix.md | 27 +++++++++++ docs/open-issue-runtime-identity-beacon.md | 48 +++++++++++++++++++ .../templates/copilot-instructions.md | 4 +- .../templates/copilot-instructions.md | 4 +- templates/copilot-instructions.md | 4 +- 6 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 docs/open-issue-runtime-identity-beacon.md diff --git a/.squad-templates/copilot-instructions.md b/.squad-templates/copilot-instructions.md index 851913676..e5bab9f71 100644 --- a/.squad-templates/copilot-instructions.md +++ b/.squad-templates/copilot-instructions.md @@ -6,9 +6,9 @@ You are working on a project that uses **Squad**, an AI team framework. When pic **IMPORTANT — run this check on EVERY session start, before doing any work:** -Inspect your `agent_instructions` for the coordinator canaries: +Inspect your `agent_instructions` for the coordinator canaries. This is a **two-state payload-integrity check within a known Squad coordinator session**; it verifies whether the coordinator payload loaded fully or was visibly truncated. It is **not** proof that an agent with no canaries is safely non-Squad — wholly absent payloads and proven non-Squad agents require a host-level identity beacon before custom-agent instruction ingestion. -1. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **absent**, you are **not** running the Squad coordinator prompt. You are a spawned sub-agent, a `squad:{member}` coding agent, or a non-Squad/third-party agent. **Skip this check entirely. Do not warn, do not halt.** Proceed with your assigned task normally. +1. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **absent**, the Squad coordinator payload is not observable from inside this prompt. Treat non-Squad discrimination as an **OPEN host-level concern**; do not claim this check proved the current agent is non-Squad. 2. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **present** and the EOF canary `SQUAD_COORDINATOR_CANARY_a8f3` is **present**, the coordinator prompt loaded fully. Proceed with normal Squad behavior. 3. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **present** but the EOF canary `SQUAD_COORDINATOR_CANARY_a8f3` is **absent**, the coordinator prompt loaded but was **truncated** before the end. diff --git a/docs/canary-identity-matrix.md b/docs/canary-identity-matrix.md index f8b7a414f..8b3bab5d1 100644 --- a/docs/canary-identity-matrix.md +++ b/docs/canary-identity-matrix.md @@ -13,6 +13,33 @@ Therefore marker-only logic cannot discriminate these two cases: Both present the same in-prompt observation: HEAD canary absent and EOF canary absent. Treating `head absent` as `not coordinator; safe skip` converts Case 3 from a loud governance failure into a silent bypass. +## Reclassification + +The dual canary is a **two-state payload-integrity detector** that only operates inside an independently-known Squad coordinator session: + +- HEAD present + EOF present = the coordinator payload is visible and loaded fully. +- HEAD present + EOF absent = the coordinator payload is visible but truncated; halt. + +`HEAD absent + EOF absent` is externally unobservable from inside the missing payload. It is **not** a third success state and does not prove a safe non-Squad skip. Cases 3 and 4 remain open until an instruction-independent runtime identity signal can distinguish “the selected Squad coordinator payload failed to inject” from “this is not a Squad coordinator session.” + +## Owning runtime surface (cases 3 & 4 — OPEN) + +Repository inspection points to the custom-agent host runtime as the only layer that can close the zero-marker ambiguity: + +- `squad init` installs the coordinator prompt at the git-root custom-agent path. The manifest maps `squad.agent.md.template` to `../.github/agents/squad.agent.md`, and monorepo init explicitly places `squad.agent.md` under the git root because Copilot resolves `.github/agents/` there (`packages/squad-cli/src/cli/core/templates.ts:31-36`, `packages/squad-cli/src/cli/core/init.ts:142-155`). Init then stamps `.github/agents/squad.agent.md` after creation (`packages/squad-cli/src/cli/core/init.ts:308-313`). +- `squad upgrade` refreshes the same `.github/agents/squad.agent.md` target from `squad.agent.md.template` (`packages/squad-cli/src/cli/core/upgrade.ts:1048`, `packages/squad-cli/src/cli/core/upgrade.ts:1102-1108`, `packages/squad-cli/src/cli/core/upgrade.ts:1124-1137`). +- The repository identifies GitHub Copilot CLI's `--agent` custom-agent path as the host that loads `.github/agents/*.agent.md`: `SquadAgent` adds `--agent squad` when `.github/agents/squad.agent.md` exists, and comments state that this CLI flag is the path that reads on-disk agent definitions (`src/Squad.Agents.AI/SquadAgent.cs:249-282`, `src/Squad.Agents.AI/SquadAgentOptions.cs:87-112`, `src/Squad.Agents.AI/README.md:155-165`). +- Squad also writes a repo-root `.mcp.json` `squad_state` entry for Copilot CLI to load (`packages/squad-cli/src/cli/core/mcp-root.ts:9-14`, `packages/squad-cli/src/cli/core/mcp-root.ts:59-73`) and explicitly injects `--additional-mcp-config @` for non-interactive spawned sessions so `squad_state_*` tools register (`packages/squad-cli/src/cli/core/copilot-invocation.ts:1-16`, `packages/squad-cli/src/cli/core/copilot-invocation.ts:53-66`). The MCP server exposes `squad_state_*`, `squad_decide`, and `memory.*` tools (`packages/squad-cli/src/cli/commands/state-mcp.ts:24-35`). **Fatal flaw:** if the coordinator payload is wholly absent, no instruction inside that absent payload can tell the model to call an MCP identity tool, so a Squad-provided MCP beacon cannot close Case 3. +- The only Copilot-related environment signal found in the inspected CLI code is `COPILOT_CLI`, used as a coarse environment heuristic (`packages/squad-cli/src/cli/copilot-install.ts:69-85`). It is not a custom-agent identity, role, selected agent file, or payload-load attestation. + +**IN Squad's control:** install and upgrade the custom-agent file, keep canary payload-integrity wording accurate, write/load `.mcp.json`, and document/test Cases 1 and 2. Squad can also request and consume a future host signal. + +**UPSTREAM Copilot CLI capability required:** a host-guaranteed, untruncatable, pre-ingestion identity beacon emitted by the runtime that selected the custom agent file. Candidate fields include a system-level preamble or metadata/tool property such as `custom_agent.name=squad`, `custom_agent.file=.github/agents/squad.agent.md`, `custom_agent.role=root-coordinator`, and `custom_agent.payload_status=loaded|missing|truncated`. The agent file must not be able to overwrite or suppress this value. + +## Required runtime test + +Add a host-level behavioral test that selects the Squad coordinator custom agent, removes or suppresses the coordinator payload entirely, and asserts a visible `UNKNOWN`/halt instead of silent continuation. This test cannot pass today because the only instructions that could classify the failure live inside the payload being removed; that is the proof that Cases 3 and 4 remain open. + ## Candidate independent identity signals | Signal | Observable from inside the agent? | Orthogonal to truncatable coordinator payload? | Reliability / risks | Resolves wholly-absent payload ambiguity? | diff --git a/docs/open-issue-runtime-identity-beacon.md b/docs/open-issue-runtime-identity-beacon.md new file mode 100644 index 000000000..b37aa3ee6 --- /dev/null +++ b/docs/open-issue-runtime-identity-beacon.md @@ -0,0 +1,48 @@ +# Host-level pre-ingestion identity beacon for Squad coordinator sessions + +Date: 2026-07-21 +Related: PR #1517, `docs/canary-identity-matrix.md`, issue #1461 + +## Problem + +The current dual canary can only report on the coordinator prompt when that prompt is visible to the model. If both canaries are absent, the in-prompt observation is identical for two different situations: + +1. the Squad coordinator custom agent was selected, but its `squad.agent.md` payload failed to inject entirely; and +2. the running agent is a proven non-Squad agent that should not receive the Squad coordinator payload. + +A prompt-contained check cannot classify the first situation because the instructions that would perform the check are absent too. + +## Reclassification + +The dual canary should be treated as a two-state payload-integrity detector within an independently-known Squad coordinator session: + +- HEAD present + EOF present: coordinator payload loaded fully. +- HEAD present + EOF absent: coordinator payload loaded but was truncated; halt. + +`HEAD absent + EOF absent` is externally unobservable from inside the missing payload. It is not a success state and cannot prove safe non-Squad discrimination. + +## Owning runtime surface + +Repository inspection shows Squad installs `.github/agents/squad.agent.md`, and the GitHub Copilot CLI custom-agent runtime loads it when invoked with `--agent squad`. Therefore the owner of a reliable identity beacon is the host runtime that selects and injects the custom-agent file before parsing its contents. + +Squad-provided MCP state tools cannot close this gap: if the coordinator payload is wholly absent, no instruction inside that absent payload can tell the model to call any `squad_state_*` identity tool. + +## Required capability + +Add a host-guaranteed, untruncatable, pre-ingestion identity beacon visible to the running agent and not overrideable by `.agent.md` content. Candidate metadata: + +```text +custom_agent.name = "squad" +custom_agent.file = ".github/agents/squad.agent.md" +custom_agent.role = "root-coordinator" +custom_agent.payload_status = "loaded" | "missing" | "truncated" +``` + +The exact schema can differ, but it must distinguish selected-custom-agent identity from prompt content visibility. + +## Acceptance criteria + +- A runtime test can select `--agent squad`, suppress the coordinator payload entirely, and assert a visible `UNKNOWN`/halt rather than silent continuation. +- A non-Squad control can be proven non-Squad by the same host-level beacon, not by absence of canary text. +- The beacon is emitted before custom-agent instruction ingestion and cannot be modified or suppressed by `.github/agents/*.agent.md` content. +- Squad documentation references the beacon as the owner for Cases 3 and 4 in `docs/canary-identity-matrix.md`. diff --git a/packages/squad-cli/templates/copilot-instructions.md b/packages/squad-cli/templates/copilot-instructions.md index 851913676..e5bab9f71 100644 --- a/packages/squad-cli/templates/copilot-instructions.md +++ b/packages/squad-cli/templates/copilot-instructions.md @@ -6,9 +6,9 @@ You are working on a project that uses **Squad**, an AI team framework. When pic **IMPORTANT — run this check on EVERY session start, before doing any work:** -Inspect your `agent_instructions` for the coordinator canaries: +Inspect your `agent_instructions` for the coordinator canaries. This is a **two-state payload-integrity check within a known Squad coordinator session**; it verifies whether the coordinator payload loaded fully or was visibly truncated. It is **not** proof that an agent with no canaries is safely non-Squad — wholly absent payloads and proven non-Squad agents require a host-level identity beacon before custom-agent instruction ingestion. -1. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **absent**, you are **not** running the Squad coordinator prompt. You are a spawned sub-agent, a `squad:{member}` coding agent, or a non-Squad/third-party agent. **Skip this check entirely. Do not warn, do not halt.** Proceed with your assigned task normally. +1. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **absent**, the Squad coordinator payload is not observable from inside this prompt. Treat non-Squad discrimination as an **OPEN host-level concern**; do not claim this check proved the current agent is non-Squad. 2. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **present** and the EOF canary `SQUAD_COORDINATOR_CANARY_a8f3` is **present**, the coordinator prompt loaded fully. Proceed with normal Squad behavior. 3. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **present** but the EOF canary `SQUAD_COORDINATOR_CANARY_a8f3` is **absent**, the coordinator prompt loaded but was **truncated** before the end. diff --git a/packages/squad-sdk/templates/copilot-instructions.md b/packages/squad-sdk/templates/copilot-instructions.md index 851913676..e5bab9f71 100644 --- a/packages/squad-sdk/templates/copilot-instructions.md +++ b/packages/squad-sdk/templates/copilot-instructions.md @@ -6,9 +6,9 @@ You are working on a project that uses **Squad**, an AI team framework. When pic **IMPORTANT — run this check on EVERY session start, before doing any work:** -Inspect your `agent_instructions` for the coordinator canaries: +Inspect your `agent_instructions` for the coordinator canaries. This is a **two-state payload-integrity check within a known Squad coordinator session**; it verifies whether the coordinator payload loaded fully or was visibly truncated. It is **not** proof that an agent with no canaries is safely non-Squad — wholly absent payloads and proven non-Squad agents require a host-level identity beacon before custom-agent instruction ingestion. -1. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **absent**, you are **not** running the Squad coordinator prompt. You are a spawned sub-agent, a `squad:{member}` coding agent, or a non-Squad/third-party agent. **Skip this check entirely. Do not warn, do not halt.** Proceed with your assigned task normally. +1. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **absent**, the Squad coordinator payload is not observable from inside this prompt. Treat non-Squad discrimination as an **OPEN host-level concern**; do not claim this check proved the current agent is non-Squad. 2. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **present** and the EOF canary `SQUAD_COORDINATOR_CANARY_a8f3` is **present**, the coordinator prompt loaded fully. Proceed with normal Squad behavior. 3. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **present** but the EOF canary `SQUAD_COORDINATOR_CANARY_a8f3` is **absent**, the coordinator prompt loaded but was **truncated** before the end. diff --git a/templates/copilot-instructions.md b/templates/copilot-instructions.md index 851913676..e5bab9f71 100644 --- a/templates/copilot-instructions.md +++ b/templates/copilot-instructions.md @@ -6,9 +6,9 @@ You are working on a project that uses **Squad**, an AI team framework. When pic **IMPORTANT — run this check on EVERY session start, before doing any work:** -Inspect your `agent_instructions` for the coordinator canaries: +Inspect your `agent_instructions` for the coordinator canaries. This is a **two-state payload-integrity check within a known Squad coordinator session**; it verifies whether the coordinator payload loaded fully or was visibly truncated. It is **not** proof that an agent with no canaries is safely non-Squad — wholly absent payloads and proven non-Squad agents require a host-level identity beacon before custom-agent instruction ingestion. -1. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **absent**, you are **not** running the Squad coordinator prompt. You are a spawned sub-agent, a `squad:{member}` coding agent, or a non-Squad/third-party agent. **Skip this check entirely. Do not warn, do not halt.** Proceed with your assigned task normally. +1. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **absent**, the Squad coordinator payload is not observable from inside this prompt. Treat non-Squad discrimination as an **OPEN host-level concern**; do not claim this check proved the current agent is non-Squad. 2. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **present** and the EOF canary `SQUAD_COORDINATOR_CANARY_a8f3` is **present**, the coordinator prompt loaded fully. Proceed with normal Squad behavior. 3. If the HEAD canary `SQUAD_COORDINATOR_CANARY_HEAD_b7d2` is **present** but the EOF canary `SQUAD_COORDINATOR_CANARY_a8f3` is **absent**, the coordinator prompt loaded but was **truncated** before the end. From 32c6174331e577b05bf1f123c8cbc99cb388c8a6 Mon Sep 17 00:00:00 2001 From: Brady Gaster Date: Tue, 21 Jul 2026 01:13:15 -0700 Subject: [PATCH 07/15] docs(canary): require selection-time identity beacon Clarifies that the runtime beacon must be captured when the host resolves the custom agent selection, before any .agent.md file read or prompt assembly. Documents that packages/squad-cli only installs and passes through --agent selection while Copilot CLI owns the resolver/read path, and adds the four-fixture runtime acceptance matrix. Refs #1461 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09d05820-0b7e-482b-b942-c86b1225dcf9 --- docs/canary-identity-matrix.md | 12 +++++--- docs/open-issue-runtime-identity-beacon.md | 36 ++++++++++++++++------ 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/docs/canary-identity-matrix.md b/docs/canary-identity-matrix.md index 8b3bab5d1..ebde3ad3c 100644 --- a/docs/canary-identity-matrix.md +++ b/docs/canary-identity-matrix.md @@ -24,17 +24,19 @@ The dual canary is a **two-state payload-integrity detector** that only operates ## Owning runtime surface (cases 3 & 4 — OPEN) -Repository inspection points to the custom-agent host runtime as the only layer that can close the zero-marker ambiguity: +Repository inspection points to the custom-agent host runtime's **agent-selection step** as the only layer that can close the zero-marker ambiguity. The beacon must be captured before file read / prompt assembly, not after it: - `squad init` installs the coordinator prompt at the git-root custom-agent path. The manifest maps `squad.agent.md.template` to `../.github/agents/squad.agent.md`, and monorepo init explicitly places `squad.agent.md` under the git root because Copilot resolves `.github/agents/` there (`packages/squad-cli/src/cli/core/templates.ts:31-36`, `packages/squad-cli/src/cli/core/init.ts:142-155`). Init then stamps `.github/agents/squad.agent.md` after creation (`packages/squad-cli/src/cli/core/init.ts:308-313`). - `squad upgrade` refreshes the same `.github/agents/squad.agent.md` target from `squad.agent.md.template` (`packages/squad-cli/src/cli/core/upgrade.ts:1048`, `packages/squad-cli/src/cli/core/upgrade.ts:1102-1108`, `packages/squad-cli/src/cli/core/upgrade.ts:1124-1137`). -- The repository identifies GitHub Copilot CLI's `--agent` custom-agent path as the host that loads `.github/agents/*.agent.md`: `SquadAgent` adds `--agent squad` when `.github/agents/squad.agent.md` exists, and comments state that this CLI flag is the path that reads on-disk agent definitions (`src/Squad.Agents.AI/SquadAgent.cs:249-282`, `src/Squad.Agents.AI/SquadAgentOptions.cs:87-112`, `src/Squad.Agents.AI/README.md:155-165`). +- In `packages/squad-cli`, Squad does not implement the custom-agent resolver or read `.github/agents/*.agent.md` into the model prompt. Init prints the user-facing invocation `copilot --agent squad` (`packages/squad-cli/src/cli/core/init.ts:515-518`); spawned-agent helpers pass user/config-supplied `copilotFlags` or `agentCmd` through to the `copilot` process (`packages/squad-cli/src/cli/commands/watch/agent-spawn.ts:95-140`, `packages/squad-cli/src/cli/commands/loop.ts:136-148`, `packages/squad-cli/src/cli/commands/watch/index.ts:608-615`); and the ACP bridge appends `--agent ` before spawning `copilot --acp --stdio` (`packages/squad-cli/src/cli/commands/copilot-bridge.ts:72-84`). None of these code paths persists selected-agent identity into host/session metadata before instruction loading, and none reads/assembles the agent file. +- The repository's SDK wrapper confirms the boundary: `SquadAgent` can add `--agent squad` when `.github/agents/squad.agent.md` exists, but its comments state that the Copilot CLI `--agent` flag is the path that reads on-disk agent definitions (`src/Squad.Agents.AI/SquadAgent.cs:249-282`, `src/Squad.Agents.AI/SquadAgentOptions.cs:87-112`, `src/Squad.Agents.AI/README.md:155-165`). +- Therefore a beacon derived only after a successful `.agent.md` file read or message assembly is insufficient: it vanishes in the payload-absent case along with the canaries. The host must persist raw selection metadata when it resolves `--agent squad` (or equivalent custom-agent selection), then separately record payload-load status (`loaded`, `truncated`, `empty`, `absent`) during file read / assembly. - Squad also writes a repo-root `.mcp.json` `squad_state` entry for Copilot CLI to load (`packages/squad-cli/src/cli/core/mcp-root.ts:9-14`, `packages/squad-cli/src/cli/core/mcp-root.ts:59-73`) and explicitly injects `--additional-mcp-config @` for non-interactive spawned sessions so `squad_state_*` tools register (`packages/squad-cli/src/cli/core/copilot-invocation.ts:1-16`, `packages/squad-cli/src/cli/core/copilot-invocation.ts:53-66`). The MCP server exposes `squad_state_*`, `squad_decide`, and `memory.*` tools (`packages/squad-cli/src/cli/commands/state-mcp.ts:24-35`). **Fatal flaw:** if the coordinator payload is wholly absent, no instruction inside that absent payload can tell the model to call an MCP identity tool, so a Squad-provided MCP beacon cannot close Case 3. -- The only Copilot-related environment signal found in the inspected CLI code is `COPILOT_CLI`, used as a coarse environment heuristic (`packages/squad-cli/src/cli/copilot-install.ts:69-85`). It is not a custom-agent identity, role, selected agent file, or payload-load attestation. +- The only Copilot-related environment signal found in the inspected CLI code is `COPILOT_CLI`, used as a coarse environment heuristic (`packages/squad-cli/src/cli/copilot-install.ts:69-85`). It is not a custom-agent identity, role, selected agent file, selection timestamp/order, or payload-load attestation. -**IN Squad's control:** install and upgrade the custom-agent file, keep canary payload-integrity wording accurate, write/load `.mcp.json`, and document/test Cases 1 and 2. Squad can also request and consume a future host signal. +**IN Squad's control:** install and upgrade the custom-agent file, pass/advise `--agent squad`, keep canary payload-integrity wording accurate, write/load `.mcp.json`, and document/test Cases 1 and 2. Squad can also request and consume a future host signal. -**UPSTREAM Copilot CLI capability required:** a host-guaranteed, untruncatable, pre-ingestion identity beacon emitted by the runtime that selected the custom agent file. Candidate fields include a system-level preamble or metadata/tool property such as `custom_agent.name=squad`, `custom_agent.file=.github/agents/squad.agent.md`, `custom_agent.role=root-coordinator`, and `custom_agent.payload_status=loaded|missing|truncated`. The agent file must not be able to overwrite or suppress this value. +**UPSTREAM Copilot CLI capability required:** a host-guaranteed, untruncatable identity beacon captured at custom-agent **selection time**, persisted in session metadata before file read / instruction assembly, and then augmented with payload-load status. Candidate fields include a system-level preamble or metadata/tool property such as `custom_agent.selected_at`, `custom_agent.name=squad`, `custom_agent.file=.github/agents/squad.agent.md`, `custom_agent.role=root-coordinator`, `custom_agent.selection_order`, and `custom_agent.payload_status=loaded|truncated|empty|absent`. The agent file must not be able to overwrite or suppress this value. ## Required runtime test diff --git a/docs/open-issue-runtime-identity-beacon.md b/docs/open-issue-runtime-identity-beacon.md index b37aa3ee6..a284d5ec5 100644 --- a/docs/open-issue-runtime-identity-beacon.md +++ b/docs/open-issue-runtime-identity-beacon.md @@ -23,26 +23,44 @@ The dual canary should be treated as a two-state payload-integrity detector with ## Owning runtime surface -Repository inspection shows Squad installs `.github/agents/squad.agent.md`, and the GitHub Copilot CLI custom-agent runtime loads it when invoked with `--agent squad`. Therefore the owner of a reliable identity beacon is the host runtime that selects and injects the custom-agent file before parsing its contents. +Repository inspection shows Squad installs `.github/agents/squad.agent.md` and points users or wrapper processes at `copilot --agent squad`, but this repo does not own the custom-agent resolver or the file-read / prompt-assembly path. The reliable identity beacon must be captured by the GitHub Copilot CLI host at the **agent-selection step** (when `--agent squad` or an equivalent custom-agent selection is resolved), persisted in host/session metadata, and only then augmented by file-read / payload-assembly status. -Squad-provided MCP state tools cannot close this gap: if the coordinator payload is wholly absent, no instruction inside that absent payload can tell the model to call any `squad_state_*` identity tool. +An identity value derived only after successfully reading `.github/agents/squad.agent.md` is not sufficient: it disappears in exactly the empty/missing-payload case that must become `UNKNOWN`/halt. Squad-provided MCP state tools cannot close this gap either; if the coordinator payload is wholly absent, no instruction inside that absent payload can tell the model to call any `squad_state_*` identity tool. ## Required capability -Add a host-guaranteed, untruncatable, pre-ingestion identity beacon visible to the running agent and not overrideable by `.agent.md` content. Candidate metadata: +Add a host-guaranteed, untruncatable, pre-ingestion identity beacon visible to the running agent and not overrideable by `.agent.md` content. Required fields must be captured with timestamp/order at selection time and payload-load time: ```text -custom_agent.name = "squad" -custom_agent.file = ".github/agents/squad.agent.md" -custom_agent.role = "root-coordinator" -custom_agent.payload_status = "loaded" | "missing" | "truncated" +custom_agent.selected_at = "" +custom_agent.selection_order = +custom_agent.selected_id = "squad" +custom_agent.selected_name = "squad" +custom_agent.selected_file = ".github/agents/squad.agent.md" +custom_agent.selected_role = "root-coordinator" +custom_agent.payload_status = "loaded" | "truncated" | "empty" | "absent" +custom_agent.payload_status_at = "" ``` -The exact schema can differ, but it must distinguish selected-custom-agent identity from prompt content visibility. +The exact schema can differ, but it must expose both raw host selection identity and payload-load status, and it must distinguish selected-custom-agent identity from prompt content visibility. + +## Design constraint: selection before message assembly + +The host must persist the selected custom-agent identity before reading `.github/agents/{name}.agent.md` and before assembling model messages. File-read success may update `payload_status`, but it must not be the source of selected-agent identity. This ordering is required because post-read identity disappears in the empty/missing-payload case. ## Acceptance criteria +### Four-fixture runtime matrix + +| Fixture | Setup | Expected outcome | +|---|---|---| +| A. Full Squad payload | Select `--agent squad`; inject full `squad.agent.md` with HEAD and EOF canaries | `loaded`; normal Squad behavior may proceed | +| B. Head-only Squad payload | Select `--agent squad`; inject content containing HEAD canary but not EOF canary | `HALT`; visible truncation warning | +| C. Empty/missing Squad payload | Select `--agent squad`; suppress, empty, or fail reading the coordinator payload entirely | `UNKNOWN`/halt; no silent continuation | +| D. Proven non-Squad | Select a host-proven non-Squad/default agent with no Squad coordinator payload | `skip`; safe non-Squad classification is based on host beacon, not canary absence | + +- The beacon is emitted at selection time before custom-agent instruction ingestion and cannot be modified or suppressed by `.github/agents/*.agent.md` content. +- The beacon includes raw host identity (`selected_id`, `selected_name`, `selected_file`, `selected_role`) and payload-load status (`loaded`, `truncated`, `empty`, `absent`) with timestamp/order fields. - A runtime test can select `--agent squad`, suppress the coordinator payload entirely, and assert a visible `UNKNOWN`/halt rather than silent continuation. - A non-Squad control can be proven non-Squad by the same host-level beacon, not by absence of canary text. -- The beacon is emitted before custom-agent instruction ingestion and cannot be modified or suppressed by `.github/agents/*.agent.md` content. - Squad documentation references the beacon as the owner for Cases 3 and 4 in `docs/canary-identity-matrix.md`. From c645b46f789f5ef78452e73bccd10d4b66e8e97b Mon Sep 17 00:00:00 2001 From: Brady Gaster Date: Tue, 21 Jul 2026 01:15:10 -0700 Subject: [PATCH 08/15] docs(canary): define host-owned identity state machine Defines the runtime identity beacon as an explicit host-owned state machine with separate selection, payload-read, and prompt-attach fields. Adds the decision function and four-fixture acceptance test requiring raw selection records and status transitions. Refs #1461 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09d05820-0b7e-482b-b942-c86b1225dcf9 --- docs/open-issue-runtime-identity-beacon.md | 91 ++++++++++++++-------- 1 file changed, 59 insertions(+), 32 deletions(-) diff --git a/docs/open-issue-runtime-identity-beacon.md b/docs/open-issue-runtime-identity-beacon.md index a284d5ec5..2b459c18b 100644 --- a/docs/open-issue-runtime-identity-beacon.md +++ b/docs/open-issue-runtime-identity-beacon.md @@ -1,4 +1,4 @@ -# Host-level pre-ingestion identity beacon for Squad coordinator sessions +# Runtime identity beacon for coordinator canary cases 3 & 4 Date: 2026-07-21 Related: PR #1517, `docs/canary-identity-matrix.md`, issue #1461 @@ -12,7 +12,7 @@ The current dual canary can only report on the coordinator prompt when that prom A prompt-contained check cannot classify the first situation because the instructions that would perform the check are absent too. -## Reclassification +## Two-state reclassification recap The dual canary should be treated as a two-state payload-integrity detector within an independently-known Squad coordinator session: @@ -23,44 +23,71 @@ The dual canary should be treated as a two-state payload-integrity detector with ## Owning runtime surface -Repository inspection shows Squad installs `.github/agents/squad.agent.md` and points users or wrapper processes at `copilot --agent squad`, but this repo does not own the custom-agent resolver or the file-read / prompt-assembly path. The reliable identity beacon must be captured by the GitHub Copilot CLI host at the **agent-selection step** (when `--agent squad` or an equivalent custom-agent selection is resolved), persisted in host/session metadata, and only then augmented by file-read / payload-assembly status. +Repository inspection shows Squad installs `.github/agents/squad.agent.md` and points users or wrapper processes at `copilot --agent squad`, but this repo does not own the custom-agent resolver or the file-read / prompt-assembly path. -An identity value derived only after successfully reading `.github/agents/squad.agent.md` is not sufficient: it disappears in exactly the empty/missing-payload case that must become `UNKNOWN`/halt. Squad-provided MCP state tools cannot close this gap either; if the coordinator payload is wholly absent, no instruction inside that absent payload can tell the model to call any `squad_state_*` identity tool. +Observed boundary: -## Required capability +- `squad init` installs/advises the custom-agent path and invocation (`packages/squad-cli/src/cli/core/templates.ts:31-36`, `packages/squad-cli/src/cli/core/init.ts:142-155`, `packages/squad-cli/src/cli/core/init.ts:515-518`). +- `squad upgrade` refreshes `.github/agents/squad.agent.md` from the template (`packages/squad-cli/src/cli/core/upgrade.ts:1048`, `packages/squad-cli/src/cli/core/upgrade.ts:1102-1108`, `packages/squad-cli/src/cli/core/upgrade.ts:1124-1137`). +- `packages/squad-cli` passes through `copilotFlags`, `agentCmd`, or `--agent ` to the `copilot` process, but does not read/assemble `.github/agents/*.agent.md` into model messages (`packages/squad-cli/src/cli/commands/watch/agent-spawn.ts:95-140`, `packages/squad-cli/src/cli/commands/loop.ts:136-148`, `packages/squad-cli/src/cli/commands/watch/index.ts:608-615`, `packages/squad-cli/src/cli/commands/copilot-bridge.ts:72-84`). +- The SDK wrapper also confirms that the Copilot CLI `--agent` flag is the path that reads on-disk agent definitions (`src/Squad.Agents.AI/SquadAgent.cs:249-282`, `src/Squad.Agents.AI/SquadAgentOptions.cs:87-112`, `src/Squad.Agents.AI/README.md:155-165`). -Add a host-guaranteed, untruncatable, pre-ingestion identity beacon visible to the running agent and not overrideable by `.agent.md` content. Required fields must be captured with timestamp/order at selection time and payload-load time: +Therefore the reliable identity beacon must be a GitHub Copilot CLI host capability captured at the **agent-selection step** and persisted in host/session metadata before any `.agent.md` file access. An identity value derived only after successfully reading `.github/agents/squad.agent.md` is not sufficient: it disappears in exactly the empty/missing-payload case that must become `UNKNOWN`/halt. -```text -custom_agent.selected_at = "" -custom_agent.selection_order = -custom_agent.selected_id = "squad" -custom_agent.selected_name = "squad" -custom_agent.selected_file = ".github/agents/squad.agent.md" -custom_agent.selected_role = "root-coordinator" -custom_agent.payload_status = "loaded" | "truncated" | "empty" | "absent" -custom_agent.payload_status_at = "" -``` +Squad-provided MCP state tools cannot close this gap either; if the coordinator payload is wholly absent, no instruction inside that absent payload can tell the model to call any `squad_state_*` identity tool. -The exact schema can differ, but it must expose both raw host selection identity and payload-load status, and it must distinguish selected-custom-agent identity from prompt content visibility. +## Host-owned pre-ingestion state machine -## Design constraint: selection before message assembly +The host must evaluate this state machine outside the prompt. A single pre-read field is insufficient; the state machine needs three distinct fields with ordering guarantees. -The host must persist the selected custom-agent identity before reading `.github/agents/{name}.agent.md` and before assembling model messages. File-read success may update `payload_status`, but it must not be the source of selected-agent identity. This ordering is required because post-read identity disappears in the empty/missing-payload case. +### Fields -## Acceptance criteria +1. `selected_agent_id` + - Immutable selection record created at agent-selection time **before any file access**. + - Carries raw host identity, for example: + - `custom_agent.name = "squad"` + - `custom_agent.file = ".github/agents/squad.agent.md"` + - `custom_agent.role = "root-coordinator"` + - Must include host timestamp or monotonic order (for example, `selected_at` / `selection_order`). -### Four-fixture runtime matrix +2. `payload_read_status` + - Separate transition recorded by the host **after** the `.agent.md` read attempt. + - Enum committed here: `LOADED`, `TRUNCATED`, `EMPTY`, `ABSENT`. + - Must include host timestamp or monotonic order. -| Fixture | Setup | Expected outcome | -|---|---|---| -| A. Full Squad payload | Select `--agent squad`; inject full `squad.agent.md` with HEAD and EOF canaries | `loaded`; normal Squad behavior may proceed | -| B. Head-only Squad payload | Select `--agent squad`; inject content containing HEAD canary but not EOF canary | `HALT`; visible truncation warning | -| C. Empty/missing Squad payload | Select `--agent squad`; suppress, empty, or fail reading the coordinator payload entirely | `UNKNOWN`/halt; no silent continuation | -| D. Proven non-Squad | Select a host-proven non-Squad/default agent with no Squad coordinator payload | `skip`; safe non-Squad classification is based on host beacon, not canary absence | +3. `prompt_attach_status` + - Records what actually reached the assembled prompt. + - Enum: `ATTACHED_FULL`, `ATTACHED_PARTIAL`, `NOT_ATTACHED`. + - Must include host timestamp or monotonic order. -- The beacon is emitted at selection time before custom-agent instruction ingestion and cannot be modified or suppressed by `.github/agents/*.agent.md` content. -- The beacon includes raw host identity (`selected_id`, `selected_name`, `selected_file`, `selected_role`) and payload-load status (`loaded`, `truncated`, `empty`, `absent`) with timestamp/order fields. -- A runtime test can select `--agent squad`, suppress the coordinator payload entirely, and assert a visible `UNKNOWN`/halt rather than silent continuation. -- A non-Squad control can be proven non-Squad by the same host-level beacon, not by absence of canary text. -- Squad documentation references the beacon as the owner for Cases 3 and 4 in `docs/canary-identity-matrix.md`. +### Ordering guarantee + +The `selected_agent_id` record must exist before the host attempts to read `.github/agents/{name}.agent.md` and before prompt/message assembly. This is the critical invariant: `selected + missing` must never collapse into `not selected`. + +### Decision function + +Evaluated outside the prompt: + +| Host state | Outcome | +|---|---| +| `selected_agent_id` set + `payload_read_status=LOADED` + `prompt_attach_status=ATTACHED_FULL` | `LOADED`; normal Squad behavior may proceed | +| `selected_agent_id` set + `payload_read_status=TRUNCATED` or `prompt_attach_status=ATTACHED_PARTIAL` | `HALT`; visible truncation warning | +| `selected_agent_id` set + `payload_read_status=EMPTY` or `payload_read_status=ABSENT` or `prompt_attach_status=NOT_ATTACHED` | `UNKNOWN`/halt; no silent continuation | +| `selected_agent_id` absent because a Squad coordinator was not selected | `SKIP`; safe non-Squad classification is based on host selection state, not canary absence | + +## Four-fixture acceptance test + +Each fixture must emit the raw selection record and the status transitions (`selected_agent_id`, `payload_read_status`, `prompt_attach_status`) for assertion. + +| Fixture | Setup | Required emitted state | Expected outcome | +|---|---|---|---| +| A. Full Squad payload | Select `--agent squad`; inject full `squad.agent.md` with HEAD and EOF canaries | `selected_agent_id` set; `payload_read_status=LOADED`; `prompt_attach_status=ATTACHED_FULL` | `LOADED`; normal Squad behavior may proceed | +| B. Head-only Squad payload | Select `--agent squad`; inject content containing HEAD canary but not EOF canary | `selected_agent_id` set; `payload_read_status=TRUNCATED`; `prompt_attach_status=ATTACHED_PARTIAL` | `HALT`; visible truncation warning | +| C. Missing/empty Squad payload | Select `--agent squad`; suppress, empty, or fail reading the coordinator payload entirely | `selected_agent_id` set; `payload_read_status=EMPTY` or `payload_read_status=ABSENT`; `prompt_attach_status=NOT_ATTACHED` | `UNKNOWN`/halt; no silent continuation | +| D. Proven non-Squad | Select a host-proven non-Squad/default agent with no Squad coordinator payload | `selected_agent_id` absent for Squad; non-Squad/default selection evidence emitted by host; no Squad coordinator payload attached | `SKIP`; safe non-Squad classification is based on host beacon, not canary absence | + +## In-Squad vs upstream split + +**In Squad's control:** install and upgrade the custom-agent file, pass/advise `--agent squad`, keep canary payload-integrity wording accurate, write/load `.mcp.json`, document Cases 1-4, and consume the future host state machine when exposed. + +**Upstream Copilot CLI capability required:** the host-owned state machine above, evaluated before and outside prompt ingestion. The `.agent.md` file must not be able to overwrite, suppress, or fabricate `selected_agent_id`, `payload_read_status`, or `prompt_attach_status`. From 9783bef5a95bdc19e3c13b1559797bbe44b69d16 Mon Sep 17 00:00:00 2001 From: Brady Gaster Date: Tue, 21 Jul 2026 01:17:57 -0700 Subject: [PATCH 09/15] docs(canary): add assembled prompt integrity state Extends the host-owned coordinator identity state machine with attach_integrity computed on the assembled artifact. Documents the refined decision function, frozen transition invariants, and ordered event-sequence acceptance requirements for assembly-time truncation. Refs #1461 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09d05820-0b7e-482b-b942-c86b1225dcf9 --- docs/canary-identity-matrix.md | 6 ++-- docs/open-issue-runtime-identity-beacon.md | 42 ++++++++++++++-------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/docs/canary-identity-matrix.md b/docs/canary-identity-matrix.md index ebde3ad3c..c39d51ae0 100644 --- a/docs/canary-identity-matrix.md +++ b/docs/canary-identity-matrix.md @@ -30,17 +30,17 @@ Repository inspection points to the custom-agent host runtime's **agent-selectio - `squad upgrade` refreshes the same `.github/agents/squad.agent.md` target from `squad.agent.md.template` (`packages/squad-cli/src/cli/core/upgrade.ts:1048`, `packages/squad-cli/src/cli/core/upgrade.ts:1102-1108`, `packages/squad-cli/src/cli/core/upgrade.ts:1124-1137`). - In `packages/squad-cli`, Squad does not implement the custom-agent resolver or read `.github/agents/*.agent.md` into the model prompt. Init prints the user-facing invocation `copilot --agent squad` (`packages/squad-cli/src/cli/core/init.ts:515-518`); spawned-agent helpers pass user/config-supplied `copilotFlags` or `agentCmd` through to the `copilot` process (`packages/squad-cli/src/cli/commands/watch/agent-spawn.ts:95-140`, `packages/squad-cli/src/cli/commands/loop.ts:136-148`, `packages/squad-cli/src/cli/commands/watch/index.ts:608-615`); and the ACP bridge appends `--agent ` before spawning `copilot --acp --stdio` (`packages/squad-cli/src/cli/commands/copilot-bridge.ts:72-84`). None of these code paths persists selected-agent identity into host/session metadata before instruction loading, and none reads/assembles the agent file. - The repository's SDK wrapper confirms the boundary: `SquadAgent` can add `--agent squad` when `.github/agents/squad.agent.md` exists, but its comments state that the Copilot CLI `--agent` flag is the path that reads on-disk agent definitions (`src/Squad.Agents.AI/SquadAgent.cs:249-282`, `src/Squad.Agents.AI/SquadAgentOptions.cs:87-112`, `src/Squad.Agents.AI/README.md:155-165`). -- Therefore a beacon derived only after a successful `.agent.md` file read or message assembly is insufficient: it vanishes in the payload-absent case along with the canaries. The host must persist raw selection metadata when it resolves `--agent squad` (or equivalent custom-agent selection), then separately record payload-load status (`loaded`, `truncated`, `empty`, `absent`) during file read / assembly. +- Therefore a beacon derived only after a successful `.agent.md` file read or message assembly is insufficient: it vanishes in the payload-absent case along with the canaries. The host must persist raw selection metadata when it resolves `--agent squad` (or equivalent custom-agent selection), then separately record `payload_read_status` (`LOADED`, `TRUNCATED`, `EMPTY`, `ABSENT`) during file read. Because head-only truncation can happen after a successful read, the host must also record `prompt_attach_status` plus `attach_integrity` (`INTACT`, `BOUNDARY_MISSING`, `PARTIAL`, `SHA_MISMATCH`) computed on the assembled artifact using expected-vs-attached SHA and HEAD/EOF boundary markers. - Squad also writes a repo-root `.mcp.json` `squad_state` entry for Copilot CLI to load (`packages/squad-cli/src/cli/core/mcp-root.ts:9-14`, `packages/squad-cli/src/cli/core/mcp-root.ts:59-73`) and explicitly injects `--additional-mcp-config @` for non-interactive spawned sessions so `squad_state_*` tools register (`packages/squad-cli/src/cli/core/copilot-invocation.ts:1-16`, `packages/squad-cli/src/cli/core/copilot-invocation.ts:53-66`). The MCP server exposes `squad_state_*`, `squad_decide`, and `memory.*` tools (`packages/squad-cli/src/cli/commands/state-mcp.ts:24-35`). **Fatal flaw:** if the coordinator payload is wholly absent, no instruction inside that absent payload can tell the model to call an MCP identity tool, so a Squad-provided MCP beacon cannot close Case 3. - The only Copilot-related environment signal found in the inspected CLI code is `COPILOT_CLI`, used as a coarse environment heuristic (`packages/squad-cli/src/cli/copilot-install.ts:69-85`). It is not a custom-agent identity, role, selected agent file, selection timestamp/order, or payload-load attestation. **IN Squad's control:** install and upgrade the custom-agent file, pass/advise `--agent squad`, keep canary payload-integrity wording accurate, write/load `.mcp.json`, and document/test Cases 1 and 2. Squad can also request and consume a future host signal. -**UPSTREAM Copilot CLI capability required:** a host-guaranteed, untruncatable identity beacon captured at custom-agent **selection time**, persisted in session metadata before file read / instruction assembly, and then augmented with payload-load status. Candidate fields include a system-level preamble or metadata/tool property such as `custom_agent.selected_at`, `custom_agent.name=squad`, `custom_agent.file=.github/agents/squad.agent.md`, `custom_agent.role=root-coordinator`, `custom_agent.selection_order`, and `custom_agent.payload_status=loaded|truncated|empty|absent`. The agent file must not be able to overwrite or suppress this value. +**UPSTREAM Copilot CLI capability required:** a host-guaranteed, untruncatable identity beacon captured at custom-agent **selection time**, persisted in session metadata before file read / instruction assembly, and then augmented with read and attach status. Required state fields are `selected_agent_id`, `payload_read_status=LOADED|TRUNCATED|EMPTY|ABSENT`, `prompt_attach_status=ATTACHED_FULL|ATTACHED_PARTIAL|NOT_ATTACHED`, and `attach_integrity=INTACT|BOUNDARY_MISSING|PARTIAL|SHA_MISMATCH`. The agent file must not be able to overwrite or suppress these values. ## Required runtime test -Add a host-level behavioral test that selects the Squad coordinator custom agent, removes or suppresses the coordinator payload entirely, and asserts a visible `UNKNOWN`/halt instead of silent continuation. This test cannot pass today because the only instructions that could classify the failure live inside the payload being removed; that is the proof that Cases 3 and 4 remain open. +Add host-level behavioral tests that assert both the final outcome and the raw ordered event sequence: `selected_agent_id` -> `payload_read_status` -> `prompt_attach_status` / `attach_integrity`. The four required fixtures are full payload (`LOADED`, `ATTACHED_FULL`, `INTACT`), head-only or assembly-truncated payload (`HALT` via `TRUNCATED`, `BOUNDARY_MISSING`, or `PARTIAL`), empty/absent selected Squad payload (`UNKNOWN`/halt), and proven non-Squad (`SKIP`). Each fixture must include the attached-artifact SHA when available and preserve `UNKNOWN` for any missing event. These tests cannot pass today because the host does not expose the ordered pre-ingestion state machine; that is the proof that Cases 3 and 4 remain open. ## Candidate independent identity signals diff --git a/docs/open-issue-runtime-identity-beacon.md b/docs/open-issue-runtime-identity-beacon.md index 2b459c18b..a9a0153ac 100644 --- a/docs/open-issue-runtime-identity-beacon.md +++ b/docs/open-issue-runtime-identity-beacon.md @@ -38,7 +38,7 @@ Squad-provided MCP state tools cannot close this gap either; if the coordinator ## Host-owned pre-ingestion state machine -The host must evaluate this state machine outside the prompt. A single pre-read field is insufficient; the state machine needs three distinct fields with ordering guarantees. +The host must evaluate this state machine outside the prompt. A single pre-read field is insufficient; the state machine needs three distinct fields plus an attach-integrity result computed on the assembled artifact. ### Fields @@ -53,16 +53,25 @@ The host must evaluate this state machine outside the prompt. A single pre-read 2. `payload_read_status` - Separate transition recorded by the host **after** the `.agent.md` read attempt. - Enum committed here: `LOADED`, `TRUNCATED`, `EMPTY`, `ABSENT`. + - Important: `LOADED` only proves the source file was read; it does **not** prove the full payload reached the assembled prompt. - Must include host timestamp or monotonic order. 3. `prompt_attach_status` - Records what actually reached the assembled prompt. - - Enum: `ATTACHED_FULL`, `ATTACHED_PARTIAL`, `NOT_ATTACHED`. - - Must include host timestamp or monotonic order. + - Attach enum: `ATTACHED_FULL`, `ATTACHED_PARTIAL`, `NOT_ATTACHED`. + - Integrity subfield: `attach_integrity` enum `INTACT`, `BOUNDARY_MISSING`, `PARTIAL`, `SHA_MISMATCH`. + - `INTACT` means both boundary markers (HEAD canary and EOF canary) are present in the assembled artifact **and** the attached-artifact SHA matches the expected payload SHA when known (for the current Squad artifact, `525a919f2b8c3c2586dcb2862de3cff63c85128cd63702e96c83be83d0ed631f`). + - `BOUNDARY_MISSING` / `PARTIAL` means the host read succeeded but prompt assembly dropped a boundary or tail; this is a truncation HALT even when `payload_read_status=LOADED`. + - `SHA_MISMATCH` means the wrong or mutated payload was attached; this is a HALT. + - Must include host timestamp or monotonic order and the attached-artifact SHA when available. -### Ordering guarantee +### Frozen transition invariants -The `selected_agent_id` record must exist before the host attempts to read `.github/agents/{name}.agent.md` and before prompt/message assembly. This is the critical invariant: `selected + missing` must never collapse into `not selected`. +1. `selected_agent_id` is emitted before `payload_read_status`. +2. `payload_read_status` is emitted before `prompt_attach_status` / `attach_integrity`. +3. `attach_integrity` is computed by the host on the **assembled artifact**, outside the prompt, using both the attached-artifact SHA and boundary-marker check. +4. Any missing event in the ordered sequence resolves to `UNKNOWN`; absent evidence never resolves to success. +5. `selected + missing` must never collapse into `not selected`. ### Decision function @@ -70,24 +79,27 @@ Evaluated outside the prompt: | Host state | Outcome | |---|---| -| `selected_agent_id` set + `payload_read_status=LOADED` + `prompt_attach_status=ATTACHED_FULL` | `LOADED`; normal Squad behavior may proceed | -| `selected_agent_id` set + `payload_read_status=TRUNCATED` or `prompt_attach_status=ATTACHED_PARTIAL` | `HALT`; visible truncation warning | -| `selected_agent_id` set + `payload_read_status=EMPTY` or `payload_read_status=ABSENT` or `prompt_attach_status=NOT_ATTACHED` | `UNKNOWN`/halt; no silent continuation | +| `selected_agent_id` set + `payload_read_status=LOADED` + `prompt_attach_status=ATTACHED_FULL` + `attach_integrity=INTACT` | `LOADED`; normal Squad behavior may proceed | +| `selected_agent_id` set + `payload_read_status=LOADED` + `attach_integrity=BOUNDARY_MISSING` or `attach_integrity=PARTIAL` | `HALT`; truncated during prompt assembly | +| `selected_agent_id` set + `payload_read_status=TRUNCATED` | `HALT`; visible truncation warning | +| `selected_agent_id` set + `payload_read_status=EMPTY` or `payload_read_status=ABSENT` | `UNKNOWN`/halt; no silent continuation | +| `selected_agent_id` set + `attach_integrity=SHA_MISMATCH` | `HALT`; wrong or mutated payload attached | | `selected_agent_id` absent because a Squad coordinator was not selected | `SKIP`; safe non-Squad classification is based on host selection state, not canary absence | +| Any missing event in the expected ordered sequence | `UNKNOWN`; absent evidence never resolves to success | ## Four-fixture acceptance test -Each fixture must emit the raw selection record and the status transitions (`selected_agent_id`, `payload_read_status`, `prompt_attach_status`) for assertion. +Each fixture must assert the final outcome **and** the raw ordered event sequence: selection record -> read result -> attach status/integrity. Each fixture must include the attached-artifact SHA when available and explicitly preserve `UNKNOWN` for absent evidence. -| Fixture | Setup | Required emitted state | Expected outcome | +| Fixture | Setup | Required ordered event sequence | Expected outcome | |---|---|---|---| -| A. Full Squad payload | Select `--agent squad`; inject full `squad.agent.md` with HEAD and EOF canaries | `selected_agent_id` set; `payload_read_status=LOADED`; `prompt_attach_status=ATTACHED_FULL` | `LOADED`; normal Squad behavior may proceed | -| B. Head-only Squad payload | Select `--agent squad`; inject content containing HEAD canary but not EOF canary | `selected_agent_id` set; `payload_read_status=TRUNCATED`; `prompt_attach_status=ATTACHED_PARTIAL` | `HALT`; visible truncation warning | -| C. Missing/empty Squad payload | Select `--agent squad`; suppress, empty, or fail reading the coordinator payload entirely | `selected_agent_id` set; `payload_read_status=EMPTY` or `payload_read_status=ABSENT`; `prompt_attach_status=NOT_ATTACHED` | `UNKNOWN`/halt; no silent continuation | -| D. Proven non-Squad | Select a host-proven non-Squad/default agent with no Squad coordinator payload | `selected_agent_id` absent for Squad; non-Squad/default selection evidence emitted by host; no Squad coordinator payload attached | `SKIP`; safe non-Squad classification is based on host beacon, not canary absence | +| A. Full Squad payload | Select `--agent squad`; inject full `squad.agent.md` with HEAD and EOF canaries | `selected_agent_id` set -> `payload_read_status=LOADED` -> `prompt_attach_status=ATTACHED_FULL`, `attach_integrity=INTACT`, attached SHA equals expected SHA when known | `LOADED`; normal Squad behavior may proceed | +| B. Head-only / assembly-truncated Squad payload | Select `--agent squad`; source read may succeed, but assembled prompt contains HEAD canary without EOF canary | `selected_agent_id` set -> `payload_read_status=LOADED` or `TRUNCATED` -> `prompt_attach_status=ATTACHED_PARTIAL`, `attach_integrity=BOUNDARY_MISSING` or `PARTIAL`, attached SHA recorded when available | `HALT`; visible truncation warning | +| C. Missing/empty Squad payload | Select `--agent squad`; suppress, empty, or fail reading the coordinator payload entirely | `selected_agent_id` set -> `payload_read_status=EMPTY` or `payload_read_status=ABSENT` -> `prompt_attach_status=NOT_ATTACHED`; no attached SHA | `UNKNOWN`/halt; no silent continuation | +| D. Proven non-Squad | Select a host-proven non-Squad/default agent with no Squad coordinator payload | Squad `selected_agent_id` absent; non-Squad/default selection evidence emitted by host; no Squad coordinator payload attached | `SKIP`; safe non-Squad classification is based on host beacon, not canary absence | ## In-Squad vs upstream split **In Squad's control:** install and upgrade the custom-agent file, pass/advise `--agent squad`, keep canary payload-integrity wording accurate, write/load `.mcp.json`, document Cases 1-4, and consume the future host state machine when exposed. -**Upstream Copilot CLI capability required:** the host-owned state machine above, evaluated before and outside prompt ingestion. The `.agent.md` file must not be able to overwrite, suppress, or fabricate `selected_agent_id`, `payload_read_status`, or `prompt_attach_status`. +**Upstream Copilot CLI capability required:** the host-owned state machine above, evaluated before and outside prompt ingestion. The `.agent.md` file must not be able to overwrite, suppress, or fabricate `selected_agent_id`, `payload_read_status`, `prompt_attach_status`, or `attach_integrity`. From 5fe5eab23ebd04ebcf7dc951e1667371a6df7ffa Mon Sep 17 00:00:00 2001 From: Brady Gaster Date: Tue, 21 Jul 2026 01:20:12 -0700 Subject: [PATCH 10/15] docs(canary): separate read truncation from attach clipping Clarifies payload_read_status=TRUNCATED as read-dimension only and maps attach clipping to LOADED plus ATTACHED_PARTIAL. Updates the state machine and matrix to fail closed: only selected+LOADED+ATTACHED_FULL+INTACT succeeds; absent or partial evidence becomes UNKNOWN/halt. Refs #1461 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09d05820-0b7e-482b-b942-c86b1225dcf9 --- docs/canary-identity-matrix.md | 6 +++--- docs/open-issue-runtime-identity-beacon.md | 23 ++++++++++++---------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/canary-identity-matrix.md b/docs/canary-identity-matrix.md index c39d51ae0..5d28a0ba8 100644 --- a/docs/canary-identity-matrix.md +++ b/docs/canary-identity-matrix.md @@ -30,17 +30,17 @@ Repository inspection points to the custom-agent host runtime's **agent-selectio - `squad upgrade` refreshes the same `.github/agents/squad.agent.md` target from `squad.agent.md.template` (`packages/squad-cli/src/cli/core/upgrade.ts:1048`, `packages/squad-cli/src/cli/core/upgrade.ts:1102-1108`, `packages/squad-cli/src/cli/core/upgrade.ts:1124-1137`). - In `packages/squad-cli`, Squad does not implement the custom-agent resolver or read `.github/agents/*.agent.md` into the model prompt. Init prints the user-facing invocation `copilot --agent squad` (`packages/squad-cli/src/cli/core/init.ts:515-518`); spawned-agent helpers pass user/config-supplied `copilotFlags` or `agentCmd` through to the `copilot` process (`packages/squad-cli/src/cli/commands/watch/agent-spawn.ts:95-140`, `packages/squad-cli/src/cli/commands/loop.ts:136-148`, `packages/squad-cli/src/cli/commands/watch/index.ts:608-615`); and the ACP bridge appends `--agent ` before spawning `copilot --acp --stdio` (`packages/squad-cli/src/cli/commands/copilot-bridge.ts:72-84`). None of these code paths persists selected-agent identity into host/session metadata before instruction loading, and none reads/assembles the agent file. - The repository's SDK wrapper confirms the boundary: `SquadAgent` can add `--agent squad` when `.github/agents/squad.agent.md` exists, but its comments state that the Copilot CLI `--agent` flag is the path that reads on-disk agent definitions (`src/Squad.Agents.AI/SquadAgent.cs:249-282`, `src/Squad.Agents.AI/SquadAgentOptions.cs:87-112`, `src/Squad.Agents.AI/README.md:155-165`). -- Therefore a beacon derived only after a successful `.agent.md` file read or message assembly is insufficient: it vanishes in the payload-absent case along with the canaries. The host must persist raw selection metadata when it resolves `--agent squad` (or equivalent custom-agent selection), then separately record `payload_read_status` (`LOADED`, `TRUNCATED`, `EMPTY`, `ABSENT`) during file read. Because head-only truncation can happen after a successful read, the host must also record `prompt_attach_status` plus `attach_integrity` (`INTACT`, `BOUNDARY_MISSING`, `PARTIAL`, `SHA_MISMATCH`) computed on the assembled artifact using expected-vs-attached SHA and HEAD/EOF boundary markers. +- Therefore a beacon derived only after a successful `.agent.md` file read or message assembly is insufficient: it vanishes in the payload-absent case along with the canaries. The host must persist raw selection metadata when it resolves `--agent squad` (or equivalent custom-agent selection), then separately record `payload_read_status` (`LOADED`, `TRUNCATED`, `EMPTY`, `ABSENT`) during file read. Because head-only clipping can happen after a successful read, the host must also record `prompt_attach_status` plus `attach_integrity` (`INTACT`, `BOUNDARY_MISSING`, `PARTIAL`, `SHA_MISMATCH`) computed on the assembled artifact using expected-vs-attached SHA and HEAD/EOF boundary markers. `payload_read_status=TRUNCATED` is reserved for a partial file read proven by read evidence; a completed read whose attachment is clipped is `LOADED + ATTACHED_PARTIAL`, not read-truncated. - Squad also writes a repo-root `.mcp.json` `squad_state` entry for Copilot CLI to load (`packages/squad-cli/src/cli/core/mcp-root.ts:9-14`, `packages/squad-cli/src/cli/core/mcp-root.ts:59-73`) and explicitly injects `--additional-mcp-config @` for non-interactive spawned sessions so `squad_state_*` tools register (`packages/squad-cli/src/cli/core/copilot-invocation.ts:1-16`, `packages/squad-cli/src/cli/core/copilot-invocation.ts:53-66`). The MCP server exposes `squad_state_*`, `squad_decide`, and `memory.*` tools (`packages/squad-cli/src/cli/commands/state-mcp.ts:24-35`). **Fatal flaw:** if the coordinator payload is wholly absent, no instruction inside that absent payload can tell the model to call an MCP identity tool, so a Squad-provided MCP beacon cannot close Case 3. - The only Copilot-related environment signal found in the inspected CLI code is `COPILOT_CLI`, used as a coarse environment heuristic (`packages/squad-cli/src/cli/copilot-install.ts:69-85`). It is not a custom-agent identity, role, selected agent file, selection timestamp/order, or payload-load attestation. **IN Squad's control:** install and upgrade the custom-agent file, pass/advise `--agent squad`, keep canary payload-integrity wording accurate, write/load `.mcp.json`, and document/test Cases 1 and 2. Squad can also request and consume a future host signal. -**UPSTREAM Copilot CLI capability required:** a host-guaranteed, untruncatable identity beacon captured at custom-agent **selection time**, persisted in session metadata before file read / instruction assembly, and then augmented with read and attach status. Required state fields are `selected_agent_id`, `payload_read_status=LOADED|TRUNCATED|EMPTY|ABSENT`, `prompt_attach_status=ATTACHED_FULL|ATTACHED_PARTIAL|NOT_ATTACHED`, and `attach_integrity=INTACT|BOUNDARY_MISSING|PARTIAL|SHA_MISMATCH`. The agent file must not be able to overwrite or suppress these values. +**UPSTREAM Copilot CLI capability required:** a host-guaranteed, untruncatable identity beacon captured at custom-agent **selection time**, persisted in session metadata before file read / instruction assembly, and then augmented with read and attach status. Required state fields are `selected_agent_id`, READ-dimension `payload_read_status=LOADED|TRUNCATED|EMPTY|ABSENT`, attach-dimension `prompt_attach_status=ATTACHED_FULL|ATTACHED_PARTIAL|NOT_ATTACHED`, and `attach_integrity=INTACT|BOUNDARY_MISSING|PARTIAL|SHA_MISMATCH`. The agent file must not be able to overwrite or suppress these values. ## Required runtime test -Add host-level behavioral tests that assert both the final outcome and the raw ordered event sequence: `selected_agent_id` -> `payload_read_status` -> `prompt_attach_status` / `attach_integrity`. The four required fixtures are full payload (`LOADED`, `ATTACHED_FULL`, `INTACT`), head-only or assembly-truncated payload (`HALT` via `TRUNCATED`, `BOUNDARY_MISSING`, or `PARTIAL`), empty/absent selected Squad payload (`UNKNOWN`/halt), and proven non-Squad (`SKIP`). Each fixture must include the attached-artifact SHA when available and preserve `UNKNOWN` for any missing event. These tests cannot pass today because the host does not expose the ordered pre-ingestion state machine; that is the proof that Cases 3 and 4 remain open. +Add host-level behavioral tests that assert both the final outcome and the raw ordered event sequence: `selected_agent_id` -> `payload_read_status` -> `prompt_attach_status` / `attach_integrity`. The four required fixtures are full payload (`LOADED`, `ATTACHED_FULL`, `INTACT`), head-only / assembly-clipped payload (`LOADED`, `ATTACHED_PARTIAL`, `BOUNDARY_MISSING|PARTIAL` -> `UNKNOWN`/halt), empty/absent selected Squad payload (`UNKNOWN`/halt), and proven non-Squad (`SKIP`). A distinct partial-file-read fixture may assert `payload_read_status=TRUNCATED -> HALT`, but attach clipping must not be encoded as read truncation. Each fixture must include the attached-artifact SHA when available and preserve `UNKNOWN` for any missing event. These tests cannot pass today because the host does not expose the ordered pre-ingestion state machine; that is the proof that Cases 3 and 4 remain open. ## Candidate independent identity signals diff --git a/docs/open-issue-runtime-identity-beacon.md b/docs/open-issue-runtime-identity-beacon.md index a9a0153ac..74799595b 100644 --- a/docs/open-issue-runtime-identity-beacon.md +++ b/docs/open-issue-runtime-identity-beacon.md @@ -51,8 +51,10 @@ The host must evaluate this state machine outside the prompt. A single pre-read - Must include host timestamp or monotonic order (for example, `selected_at` / `selection_order`). 2. `payload_read_status` - - Separate transition recorded by the host **after** the `.agent.md` read attempt. + - Separate READ-dimension transition recorded by the host **after** the `.agent.md` read attempt. - Enum committed here: `LOADED`, `TRUNCATED`, `EMPTY`, `ABSENT`. + - `TRUNCATED` is reserved for a partial **file read** proven by expected/actual byte counts, expected EOF evidence, or equivalent host read evidence. + - A completed file read whose prompt attachment is clipped is `payload_read_status=LOADED` plus `prompt_attach_status=ATTACHED_PARTIAL`; do not encode attach clipping as read truncation. - Important: `LOADED` only proves the source file was read; it does **not** prove the full payload reached the assembled prompt. - Must include host timestamp or monotonic order. @@ -61,7 +63,7 @@ The host must evaluate this state machine outside the prompt. A single pre-read - Attach enum: `ATTACHED_FULL`, `ATTACHED_PARTIAL`, `NOT_ATTACHED`. - Integrity subfield: `attach_integrity` enum `INTACT`, `BOUNDARY_MISSING`, `PARTIAL`, `SHA_MISMATCH`. - `INTACT` means both boundary markers (HEAD canary and EOF canary) are present in the assembled artifact **and** the attached-artifact SHA matches the expected payload SHA when known (for the current Squad artifact, `525a919f2b8c3c2586dcb2862de3cff63c85128cd63702e96c83be83d0ed631f`). - - `BOUNDARY_MISSING` / `PARTIAL` means the host read succeeded but prompt assembly dropped a boundary or tail; this is a truncation HALT even when `payload_read_status=LOADED`. + - `BOUNDARY_MISSING` / `PARTIAL` means the host read may have succeeded but prompt assembly dropped a boundary or tail; this is attach-dimension clipping and fails closed even when `payload_read_status=LOADED`. - `SHA_MISMATCH` means the wrong or mutated payload was attached; this is a HALT. - Must include host timestamp or monotonic order and the attached-artifact SHA when available. @@ -75,26 +77,27 @@ The host must evaluate this state machine outside the prompt. A single pre-read ### Decision function -Evaluated outside the prompt: +Evaluated outside the prompt. This mapping fails closed: absent or partial evidence never resolves to success. | Host state | Outcome | |---|---| -| `selected_agent_id` set + `payload_read_status=LOADED` + `prompt_attach_status=ATTACHED_FULL` + `attach_integrity=INTACT` | `LOADED`; normal Squad behavior may proceed | -| `selected_agent_id` set + `payload_read_status=LOADED` + `attach_integrity=BOUNDARY_MISSING` or `attach_integrity=PARTIAL` | `HALT`; truncated during prompt assembly | -| `selected_agent_id` set + `payload_read_status=TRUNCATED` | `HALT`; visible truncation warning | -| `selected_agent_id` set + `payload_read_status=EMPTY` or `payload_read_status=ABSENT` | `UNKNOWN`/halt; no silent continuation | +| `selected_agent_id` set + `payload_read_status=LOADED` + `prompt_attach_status=ATTACHED_FULL` + `attach_integrity=INTACT` | `LOADED`; this is the sole success state for selected Squad | +| `selected_agent_id` set + `payload_read_status=EMPTY` or `payload_read_status=ABSENT` | `UNKNOWN`/halt; empty or absent read evidence is not success | +| `selected_agent_id` set + `prompt_attach_status=ATTACHED_PARTIAL` or `prompt_attach_status=NOT_ATTACHED` | `UNKNOWN`/halt; partial or absent attach evidence is not success | +| `selected_agent_id` set + `payload_read_status=TRUNCATED` | `HALT`; partial file read proven in the READ dimension | | `selected_agent_id` set + `attach_integrity=SHA_MISMATCH` | `HALT`; wrong or mutated payload attached | -| `selected_agent_id` absent because a Squad coordinator was not selected | `SKIP`; safe non-Squad classification is based on host selection state, not canary absence | +| `selected_agent_id` set + `attach_integrity=BOUNDARY_MISSING` or `attach_integrity=PARTIAL` | `UNKNOWN`/halt unless the host can additionally prove a read-dimension `TRUNCATED`; attach clipping is not success | +| `selected_agent_id` absent because a Squad coordinator was independently proven not selected | `SKIP`; safe non-Squad classification is based on host selection state, not canary absence | | Any missing event in the expected ordered sequence | `UNKNOWN`; absent evidence never resolves to success | ## Four-fixture acceptance test -Each fixture must assert the final outcome **and** the raw ordered event sequence: selection record -> read result -> attach status/integrity. Each fixture must include the attached-artifact SHA when available and explicitly preserve `UNKNOWN` for absent evidence. +Each fixture must assert the final outcome **and** the raw ordered event sequence: selection record -> read result -> attach status/integrity. Each fixture must include the attached-artifact SHA when available and explicitly preserve `UNKNOWN` for absent or partial evidence. A separate read-truncation fixture may assert `payload_read_status=TRUNCATED -> HALT`, but attach clipping must remain `LOADED + ATTACHED_PARTIAL`, not read `TRUNCATED`. | Fixture | Setup | Required ordered event sequence | Expected outcome | |---|---|---|---| | A. Full Squad payload | Select `--agent squad`; inject full `squad.agent.md` with HEAD and EOF canaries | `selected_agent_id` set -> `payload_read_status=LOADED` -> `prompt_attach_status=ATTACHED_FULL`, `attach_integrity=INTACT`, attached SHA equals expected SHA when known | `LOADED`; normal Squad behavior may proceed | -| B. Head-only / assembly-truncated Squad payload | Select `--agent squad`; source read may succeed, but assembled prompt contains HEAD canary without EOF canary | `selected_agent_id` set -> `payload_read_status=LOADED` or `TRUNCATED` -> `prompt_attach_status=ATTACHED_PARTIAL`, `attach_integrity=BOUNDARY_MISSING` or `PARTIAL`, attached SHA recorded when available | `HALT`; visible truncation warning | +| B. Head-only / assembly-clipped Squad payload | Select `--agent squad`; source read succeeds, but assembled prompt contains HEAD canary without EOF canary | `selected_agent_id` set -> `payload_read_status=LOADED` -> `prompt_attach_status=ATTACHED_PARTIAL`, `attach_integrity=BOUNDARY_MISSING` or `PARTIAL`, attached SHA recorded when available | `UNKNOWN`/halt; fail closed because attach evidence is partial | | C. Missing/empty Squad payload | Select `--agent squad`; suppress, empty, or fail reading the coordinator payload entirely | `selected_agent_id` set -> `payload_read_status=EMPTY` or `payload_read_status=ABSENT` -> `prompt_attach_status=NOT_ATTACHED`; no attached SHA | `UNKNOWN`/halt; no silent continuation | | D. Proven non-Squad | Select a host-proven non-Squad/default agent with no Squad coordinator payload | Squad `selected_agent_id` absent; non-Squad/default selection evidence emitted by host; no Squad coordinator payload attached | `SKIP`; safe non-Squad classification is based on host beacon, not canary absence | From b555e12e9f80c8dafa9d91a6bf7afcf3b40ab1d3 Mon Sep 17 00:00:00 2001 From: Brady Gaster Date: Tue, 21 Jul 2026 01:24:53 -0700 Subject: [PATCH 11/15] docs(canary): require trusted expected sha provenance Clarifies that attach integrity can only compare against an expected SHA from a trusted host-side registry keyed by agent and client version. Adds selection-record version keys and distinguishes host-computed attached_sha from trusted-registry expected_sha. Refs #1461 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09d05820-0b7e-482b-b942-c86b1225dcf9 --- docs/canary-identity-matrix.md | 6 +++--- docs/open-issue-runtime-identity-beacon.md | 20 +++++++++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/docs/canary-identity-matrix.md b/docs/canary-identity-matrix.md index 5d28a0ba8..ab84da6f2 100644 --- a/docs/canary-identity-matrix.md +++ b/docs/canary-identity-matrix.md @@ -30,17 +30,17 @@ Repository inspection points to the custom-agent host runtime's **agent-selectio - `squad upgrade` refreshes the same `.github/agents/squad.agent.md` target from `squad.agent.md.template` (`packages/squad-cli/src/cli/core/upgrade.ts:1048`, `packages/squad-cli/src/cli/core/upgrade.ts:1102-1108`, `packages/squad-cli/src/cli/core/upgrade.ts:1124-1137`). - In `packages/squad-cli`, Squad does not implement the custom-agent resolver or read `.github/agents/*.agent.md` into the model prompt. Init prints the user-facing invocation `copilot --agent squad` (`packages/squad-cli/src/cli/core/init.ts:515-518`); spawned-agent helpers pass user/config-supplied `copilotFlags` or `agentCmd` through to the `copilot` process (`packages/squad-cli/src/cli/commands/watch/agent-spawn.ts:95-140`, `packages/squad-cli/src/cli/commands/loop.ts:136-148`, `packages/squad-cli/src/cli/commands/watch/index.ts:608-615`); and the ACP bridge appends `--agent ` before spawning `copilot --acp --stdio` (`packages/squad-cli/src/cli/commands/copilot-bridge.ts:72-84`). None of these code paths persists selected-agent identity into host/session metadata before instruction loading, and none reads/assembles the agent file. - The repository's SDK wrapper confirms the boundary: `SquadAgent` can add `--agent squad` when `.github/agents/squad.agent.md` exists, but its comments state that the Copilot CLI `--agent` flag is the path that reads on-disk agent definitions (`src/Squad.Agents.AI/SquadAgent.cs:249-282`, `src/Squad.Agents.AI/SquadAgentOptions.cs:87-112`, `src/Squad.Agents.AI/README.md:155-165`). -- Therefore a beacon derived only after a successful `.agent.md` file read or message assembly is insufficient: it vanishes in the payload-absent case along with the canaries. The host must persist raw selection metadata when it resolves `--agent squad` (or equivalent custom-agent selection), then separately record `payload_read_status` (`LOADED`, `TRUNCATED`, `EMPTY`, `ABSENT`) during file read. Because head-only clipping can happen after a successful read, the host must also record `prompt_attach_status` plus `attach_integrity` (`INTACT`, `BOUNDARY_MISSING`, `PARTIAL`, `SHA_MISMATCH`) computed on the assembled artifact using expected-vs-attached SHA and HEAD/EOF boundary markers. `payload_read_status=TRUNCATED` is reserved for a partial file read proven by read evidence; a completed read whose attachment is clipped is `LOADED + ATTACHED_PARTIAL`, not read-truncated. +- Therefore a beacon derived only after a successful `.agent.md` file read or message assembly is insufficient: it vanishes in the payload-absent case along with the canaries. The host must persist raw selection metadata when it resolves `--agent squad` (or equivalent custom-agent selection), then separately record `payload_read_status` (`LOADED`, `TRUNCATED`, `EMPTY`, `ABSENT`) during file read. Because head-only clipping can happen after a successful read, the host must also record `prompt_attach_status` plus `attach_integrity` (`INTACT`, `BOUNDARY_MISSING`, `PARTIAL`, `SHA_MISMATCH`) computed on the assembled artifact using HEAD/EOF boundary markers and expected-vs-attached SHA, where `expected_sha` comes from a trusted host-side registry keyed by (`agent_version`, `client_id`, `client_version`) and is never derived from attached bytes. `payload_read_status=TRUNCATED` is reserved for a partial file read proven by read evidence; a completed read whose attachment is clipped is `LOADED + ATTACHED_PARTIAL`, not read-truncated. - Squad also writes a repo-root `.mcp.json` `squad_state` entry for Copilot CLI to load (`packages/squad-cli/src/cli/core/mcp-root.ts:9-14`, `packages/squad-cli/src/cli/core/mcp-root.ts:59-73`) and explicitly injects `--additional-mcp-config @` for non-interactive spawned sessions so `squad_state_*` tools register (`packages/squad-cli/src/cli/core/copilot-invocation.ts:1-16`, `packages/squad-cli/src/cli/core/copilot-invocation.ts:53-66`). The MCP server exposes `squad_state_*`, `squad_decide`, and `memory.*` tools (`packages/squad-cli/src/cli/commands/state-mcp.ts:24-35`). **Fatal flaw:** if the coordinator payload is wholly absent, no instruction inside that absent payload can tell the model to call an MCP identity tool, so a Squad-provided MCP beacon cannot close Case 3. - The only Copilot-related environment signal found in the inspected CLI code is `COPILOT_CLI`, used as a coarse environment heuristic (`packages/squad-cli/src/cli/copilot-install.ts:69-85`). It is not a custom-agent identity, role, selected agent file, selection timestamp/order, or payload-load attestation. **IN Squad's control:** install and upgrade the custom-agent file, pass/advise `--agent squad`, keep canary payload-integrity wording accurate, write/load `.mcp.json`, and document/test Cases 1 and 2. Squad can also request and consume a future host signal. -**UPSTREAM Copilot CLI capability required:** a host-guaranteed, untruncatable identity beacon captured at custom-agent **selection time**, persisted in session metadata before file read / instruction assembly, and then augmented with read and attach status. Required state fields are `selected_agent_id`, READ-dimension `payload_read_status=LOADED|TRUNCATED|EMPTY|ABSENT`, attach-dimension `prompt_attach_status=ATTACHED_FULL|ATTACHED_PARTIAL|NOT_ATTACHED`, and `attach_integrity=INTACT|BOUNDARY_MISSING|PARTIAL|SHA_MISMATCH`. The agent file must not be able to overwrite or suppress these values. +**UPSTREAM Copilot CLI capability required:** a host-guaranteed, untruncatable identity beacon captured at custom-agent **selection time**, persisted in session metadata before file read / instruction assembly, and then augmented with read and attach status. Required state fields are `selected_agent_id` (including `agent_version`, `client_id`, `client_version`), READ-dimension `payload_read_status=LOADED|TRUNCATED|EMPTY|ABSENT`, attach-dimension `prompt_attach_status=ATTACHED_FULL|ATTACHED_PARTIAL|NOT_ATTACHED`, `attached_sha`, trusted-registry `expected_sha`, and `attach_integrity=INTACT|BOUNDARY_MISSING|PARTIAL|SHA_MISMATCH`. The agent file must not be able to overwrite or suppress these values. ## Required runtime test -Add host-level behavioral tests that assert both the final outcome and the raw ordered event sequence: `selected_agent_id` -> `payload_read_status` -> `prompt_attach_status` / `attach_integrity`. The four required fixtures are full payload (`LOADED`, `ATTACHED_FULL`, `INTACT`), head-only / assembly-clipped payload (`LOADED`, `ATTACHED_PARTIAL`, `BOUNDARY_MISSING|PARTIAL` -> `UNKNOWN`/halt), empty/absent selected Squad payload (`UNKNOWN`/halt), and proven non-Squad (`SKIP`). A distinct partial-file-read fixture may assert `payload_read_status=TRUNCATED -> HALT`, but attach clipping must not be encoded as read truncation. Each fixture must include the attached-artifact SHA when available and preserve `UNKNOWN` for any missing event. These tests cannot pass today because the host does not expose the ordered pre-ingestion state machine; that is the proof that Cases 3 and 4 remain open. +Add host-level behavioral tests that assert both the final outcome and the raw ordered event sequence: `selected_agent_id` -> `payload_read_status` -> `prompt_attach_status` / `attach_integrity`. The four required fixtures are full payload (`LOADED`, `ATTACHED_FULL`, `INTACT`), head-only / assembly-clipped payload (`LOADED`, `ATTACHED_PARTIAL`, `BOUNDARY_MISSING|PARTIAL` -> `UNKNOWN`/halt), empty/absent selected Squad payload (`UNKNOWN`/halt), and proven non-Squad (`SKIP`). A distinct partial-file-read fixture may assert `payload_read_status=TRUNCATED -> HALT`, but attach clipping must not be encoded as read truncation. Each fixture must include `attached_sha` when available, the trusted `expected_sha` registry provenance, and preserve `UNKNOWN` for any missing event. These tests cannot pass today because the host does not expose the ordered pre-ingestion state machine; that is the proof that Cases 3 and 4 remain open. ## Candidate independent identity signals diff --git a/docs/open-issue-runtime-identity-beacon.md b/docs/open-issue-runtime-identity-beacon.md index 74799595b..18c81ef4a 100644 --- a/docs/open-issue-runtime-identity-beacon.md +++ b/docs/open-issue-runtime-identity-beacon.md @@ -48,7 +48,11 @@ The host must evaluate this state machine outside the prompt. A single pre-read - `custom_agent.name = "squad"` - `custom_agent.file = ".github/agents/squad.agent.md"` - `custom_agent.role = "root-coordinator"` + - `agent_version = ""` + - `client_id = ""` + - `client_version = ""` - Must include host timestamp or monotonic order (for example, `selected_at` / `selection_order`). + - `agent_version`, `client_id`, and `client_version` key the trusted expected-SHA registry used by attach integrity. 2. `payload_read_status` - Separate READ-dimension transition recorded by the host **after** the `.agent.md` read attempt. @@ -62,16 +66,18 @@ The host must evaluate this state machine outside the prompt. A single pre-read - Records what actually reached the assembled prompt. - Attach enum: `ATTACHED_FULL`, `ATTACHED_PARTIAL`, `NOT_ATTACHED`. - Integrity subfield: `attach_integrity` enum `INTACT`, `BOUNDARY_MISSING`, `PARTIAL`, `SHA_MISMATCH`. - - `INTACT` means both boundary markers (HEAD canary and EOF canary) are present in the assembled artifact **and** the attached-artifact SHA matches the expected payload SHA when known (for the current Squad artifact, `525a919f2b8c3c2586dcb2862de3cff63c85128cd63702e96c83be83d0ed631f`). + - `expected_sha` MUST come from a trusted host-side registry keyed by (`agent_version`, `client_id`, `client_version`); it must NEVER be derived from the attached or assembled bytes. + - `attached_sha` is computed by the host over the **assembled artifact**. + - `INTACT` means both boundary markers (HEAD canary and EOF canary) are present in the assembled artifact **and** `attached_sha == expected_sha` from trusted provenance (for the current Squad artifact, the expected SHA is `525a919f2b8c3c2586dcb2862de3cff63c85128cd63702e96c83be83d0ed631f` when that registry key applies). - `BOUNDARY_MISSING` / `PARTIAL` means the host read may have succeeded but prompt assembly dropped a boundary or tail; this is attach-dimension clipping and fails closed even when `payload_read_status=LOADED`. - - `SHA_MISMATCH` means the wrong or mutated payload was attached; this is a HALT. - - Must include host timestamp or monotonic order and the attached-artifact SHA when available. + - `SHA_MISMATCH` means `attached_sha != expected_sha`; the wrong or mutated payload was attached, so this is a HALT. + - Must include host timestamp or monotonic order, `attached_sha` when available, and the trusted `expected_sha` registry key/provenance. ### Frozen transition invariants 1. `selected_agent_id` is emitted before `payload_read_status`. 2. `payload_read_status` is emitted before `prompt_attach_status` / `attach_integrity`. -3. `attach_integrity` is computed by the host on the **assembled artifact**, outside the prompt, using both the attached-artifact SHA and boundary-marker check. +3. `attach_integrity` is computed by the host on the **assembled artifact**, outside the prompt, using both `attached_sha` and the boundary-marker check; `expected_sha` comes only from the trusted host-side registry keyed by (`agent_version`, `client_id`, `client_version`). 4. Any missing event in the ordered sequence resolves to `UNKNOWN`; absent evidence never resolves to success. 5. `selected + missing` must never collapse into `not selected`. @@ -96,8 +102,8 @@ Each fixture must assert the final outcome **and** the raw ordered event sequenc | Fixture | Setup | Required ordered event sequence | Expected outcome | |---|---|---|---| -| A. Full Squad payload | Select `--agent squad`; inject full `squad.agent.md` with HEAD and EOF canaries | `selected_agent_id` set -> `payload_read_status=LOADED` -> `prompt_attach_status=ATTACHED_FULL`, `attach_integrity=INTACT`, attached SHA equals expected SHA when known | `LOADED`; normal Squad behavior may proceed | -| B. Head-only / assembly-clipped Squad payload | Select `--agent squad`; source read succeeds, but assembled prompt contains HEAD canary without EOF canary | `selected_agent_id` set -> `payload_read_status=LOADED` -> `prompt_attach_status=ATTACHED_PARTIAL`, `attach_integrity=BOUNDARY_MISSING` or `PARTIAL`, attached SHA recorded when available | `UNKNOWN`/halt; fail closed because attach evidence is partial | +| A. Full Squad payload | Select `--agent squad`; inject full `squad.agent.md` with HEAD and EOF canaries | `selected_agent_id` set with `agent_version`, `client_id`, `client_version` -> `payload_read_status=LOADED` -> `prompt_attach_status=ATTACHED_FULL`, `attach_integrity=INTACT`, `attached_sha` equals trusted-registry `expected_sha` | `LOADED`; normal Squad behavior may proceed | +| B. Head-only / assembly-clipped Squad payload | Select `--agent squad`; source read succeeds, but assembled prompt contains HEAD canary without EOF canary | `selected_agent_id` set with registry keys -> `payload_read_status=LOADED` -> `prompt_attach_status=ATTACHED_PARTIAL`, `attach_integrity=BOUNDARY_MISSING` or `PARTIAL`, `attached_sha` recorded when available | `UNKNOWN`/halt; fail closed because attach evidence is partial | | C. Missing/empty Squad payload | Select `--agent squad`; suppress, empty, or fail reading the coordinator payload entirely | `selected_agent_id` set -> `payload_read_status=EMPTY` or `payload_read_status=ABSENT` -> `prompt_attach_status=NOT_ATTACHED`; no attached SHA | `UNKNOWN`/halt; no silent continuation | | D. Proven non-Squad | Select a host-proven non-Squad/default agent with no Squad coordinator payload | Squad `selected_agent_id` absent; non-Squad/default selection evidence emitted by host; no Squad coordinator payload attached | `SKIP`; safe non-Squad classification is based on host beacon, not canary absence | @@ -105,4 +111,4 @@ Each fixture must assert the final outcome **and** the raw ordered event sequenc **In Squad's control:** install and upgrade the custom-agent file, pass/advise `--agent squad`, keep canary payload-integrity wording accurate, write/load `.mcp.json`, document Cases 1-4, and consume the future host state machine when exposed. -**Upstream Copilot CLI capability required:** the host-owned state machine above, evaluated before and outside prompt ingestion. The `.agent.md` file must not be able to overwrite, suppress, or fabricate `selected_agent_id`, `payload_read_status`, `prompt_attach_status`, or `attach_integrity`. +**Upstream Copilot CLI capability required:** the host-owned state machine above, evaluated before and outside prompt ingestion, plus the trusted expected-SHA registry keyed by (`agent_version`, `client_id`, `client_version`). The `.agent.md` file must not be able to overwrite, suppress, or fabricate `selected_agent_id`, `payload_read_status`, `prompt_attach_status`, `attach_integrity`, `expected_sha`, or `attached_sha`. From c2890e8123d89109d51dd235d799ae7c78582c2f Mon Sep 17 00:00:00 2001 From: Brady Gaster Date: Tue, 21 Jul 2026 01:30:38 -0700 Subject: [PATCH 12/15] docs(canary): split transport and release integrity axes Restructures the host-owned runtime beacon into orthogonal transport and release verdicts. Transport integrity is the fail-closed enforcement axis, while release integrity reports verified, mismatched, or unregistered artifacts without false-halting healthy local or fresh artifacts. Adds six acceptance fixtures and the Caveman telemetry join protocol. Refs #1461 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09d05820-0b7e-482b-b942-c86b1225dcf9 --- docs/canary-identity-matrix.md | 6 +- docs/open-issue-runtime-identity-beacon.md | 103 +++++++++++++-------- 2 files changed, 69 insertions(+), 40 deletions(-) diff --git a/docs/canary-identity-matrix.md b/docs/canary-identity-matrix.md index ab84da6f2..32bf20486 100644 --- a/docs/canary-identity-matrix.md +++ b/docs/canary-identity-matrix.md @@ -30,17 +30,17 @@ Repository inspection points to the custom-agent host runtime's **agent-selectio - `squad upgrade` refreshes the same `.github/agents/squad.agent.md` target from `squad.agent.md.template` (`packages/squad-cli/src/cli/core/upgrade.ts:1048`, `packages/squad-cli/src/cli/core/upgrade.ts:1102-1108`, `packages/squad-cli/src/cli/core/upgrade.ts:1124-1137`). - In `packages/squad-cli`, Squad does not implement the custom-agent resolver or read `.github/agents/*.agent.md` into the model prompt. Init prints the user-facing invocation `copilot --agent squad` (`packages/squad-cli/src/cli/core/init.ts:515-518`); spawned-agent helpers pass user/config-supplied `copilotFlags` or `agentCmd` through to the `copilot` process (`packages/squad-cli/src/cli/commands/watch/agent-spawn.ts:95-140`, `packages/squad-cli/src/cli/commands/loop.ts:136-148`, `packages/squad-cli/src/cli/commands/watch/index.ts:608-615`); and the ACP bridge appends `--agent ` before spawning `copilot --acp --stdio` (`packages/squad-cli/src/cli/commands/copilot-bridge.ts:72-84`). None of these code paths persists selected-agent identity into host/session metadata before instruction loading, and none reads/assembles the agent file. - The repository's SDK wrapper confirms the boundary: `SquadAgent` can add `--agent squad` when `.github/agents/squad.agent.md` exists, but its comments state that the Copilot CLI `--agent` flag is the path that reads on-disk agent definitions (`src/Squad.Agents.AI/SquadAgent.cs:249-282`, `src/Squad.Agents.AI/SquadAgentOptions.cs:87-112`, `src/Squad.Agents.AI/README.md:155-165`). -- Therefore a beacon derived only after a successful `.agent.md` file read or message assembly is insufficient: it vanishes in the payload-absent case along with the canaries. The host must persist raw selection metadata when it resolves `--agent squad` (or equivalent custom-agent selection), then separately record `payload_read_status` (`LOADED`, `TRUNCATED`, `EMPTY`, `ABSENT`) during file read. Because head-only clipping can happen after a successful read, the host must also record `prompt_attach_status` plus `attach_integrity` (`INTACT`, `BOUNDARY_MISSING`, `PARTIAL`, `SHA_MISMATCH`) computed on the assembled artifact using HEAD/EOF boundary markers and expected-vs-attached SHA, where `expected_sha` comes from a trusted host-side registry keyed by (`agent_version`, `client_id`, `client_version`) and is never derived from attached bytes. `payload_read_status=TRUNCATED` is reserved for a partial file read proven by read evidence; a completed read whose attachment is clipped is `LOADED + ATTACHED_PARTIAL`, not read-truncated. +- Therefore a beacon derived only after a successful `.agent.md` file read or message assembly is insufficient: it vanishes in the payload-absent case along with the canaries. The host must persist raw selection metadata when it resolves `--agent squad` (or equivalent custom-agent selection), then separately record read and attach events. Integrity must split into two orthogonal verdicts: `transport_integrity=INTACT|PARTIAL|UNKNOWN` for enforcement, and `release_integrity=VERIFIED|MISMATCH|UNREGISTERED` for release provenance/reporting. Transport compares `source_sha_after_read` to host-computed `attached_sha` and requires independent completeness evidence (EOF/boundary/expected length); hash equality alone is insufficient. Release compares `source_sha_after_read` to an optional trusted manifest keyed by (`agent_version`, `client_id`, `client_version`); missing manifest entries are `UNREGISTERED`, not `MISMATCH`, and must not false-halt healthy local/new artifacts. - Squad also writes a repo-root `.mcp.json` `squad_state` entry for Copilot CLI to load (`packages/squad-cli/src/cli/core/mcp-root.ts:9-14`, `packages/squad-cli/src/cli/core/mcp-root.ts:59-73`) and explicitly injects `--additional-mcp-config @` for non-interactive spawned sessions so `squad_state_*` tools register (`packages/squad-cli/src/cli/core/copilot-invocation.ts:1-16`, `packages/squad-cli/src/cli/core/copilot-invocation.ts:53-66`). The MCP server exposes `squad_state_*`, `squad_decide`, and `memory.*` tools (`packages/squad-cli/src/cli/commands/state-mcp.ts:24-35`). **Fatal flaw:** if the coordinator payload is wholly absent, no instruction inside that absent payload can tell the model to call an MCP identity tool, so a Squad-provided MCP beacon cannot close Case 3. - The only Copilot-related environment signal found in the inspected CLI code is `COPILOT_CLI`, used as a coarse environment heuristic (`packages/squad-cli/src/cli/copilot-install.ts:69-85`). It is not a custom-agent identity, role, selected agent file, selection timestamp/order, or payload-load attestation. **IN Squad's control:** install and upgrade the custom-agent file, pass/advise `--agent squad`, keep canary payload-integrity wording accurate, write/load `.mcp.json`, and document/test Cases 1 and 2. Squad can also request and consume a future host signal. -**UPSTREAM Copilot CLI capability required:** a host-guaranteed, untruncatable identity beacon captured at custom-agent **selection time**, persisted in session metadata before file read / instruction assembly, and then augmented with read and attach status. Required state fields are `selected_agent_id` (including `agent_version`, `client_id`, `client_version`), READ-dimension `payload_read_status=LOADED|TRUNCATED|EMPTY|ABSENT`, attach-dimension `prompt_attach_status=ATTACHED_FULL|ATTACHED_PARTIAL|NOT_ATTACHED`, `attached_sha`, trusted-registry `expected_sha`, and `attach_integrity=INTACT|BOUNDARY_MISSING|PARTIAL|SHA_MISMATCH`. The agent file must not be able to overwrite or suppress these values. +**UPSTREAM Copilot CLI capability required:** a host-guaranteed, untruncatable identity beacon captured at custom-agent **selection time**, persisted in session metadata before file read / instruction assembly, and then augmented with read, attach, transport, and release verdicts. Required state fields are `selected_agent_id` (including `agent_version`, `client_id`, `client_version`), `payload_read_status=LOADED|TRUNCATED|EMPTY|ABSENT`, `prompt_attach_status=ATTACHED_FULL|ATTACHED_PARTIAL|NOT_ATTACHED`, `source_sha_after_read`, `attached_sha`, `transport_integrity=INTACT|PARTIAL|UNKNOWN`, and `release_integrity=VERIFIED|MISMATCH|UNREGISTERED`. The agent file must not be able to overwrite or suppress these values. ## Required runtime test -Add host-level behavioral tests that assert both the final outcome and the raw ordered event sequence: `selected_agent_id` -> `payload_read_status` -> `prompt_attach_status` / `attach_integrity`. The four required fixtures are full payload (`LOADED`, `ATTACHED_FULL`, `INTACT`), head-only / assembly-clipped payload (`LOADED`, `ATTACHED_PARTIAL`, `BOUNDARY_MISSING|PARTIAL` -> `UNKNOWN`/halt), empty/absent selected Squad payload (`UNKNOWN`/halt), and proven non-Squad (`SKIP`). A distinct partial-file-read fixture may assert `payload_read_status=TRUNCATED -> HALT`, but attach clipping must not be encoded as read truncation. Each fixture must include `attached_sha` when available, the trusted `expected_sha` registry provenance, and preserve `UNKNOWN` for any missing event. These tests cannot pass today because the host does not expose the ordered pre-ingestion state machine; that is the proof that Cases 3 and 4 remain open. +Add host-level behavioral tests that assert both the final outcome and raw ordered event sequence: `selected_agent_id` -> `payload_read_status` -> `prompt_attach_status` -> `transport_integrity` -> `release_integrity`. Required fixtures: registered full payload (`INTACT`, `VERIFIED` -> `LOADED`), head-only/oversized-prompt clip (`PARTIAL` -> `HALT`), missing/empty selected Squad payload (`UNKNOWN` -> `UNKNOWN`/halt), proven non-Squad (`SKIP`), wrong-version (`INTACT`, `MISMATCH` -> `HALT`), and legitimate unregistered-local/fresh artifact (`INTACT`, `UNREGISTERED` -> `LOADED` with report, no false halt). Each fixture records both axes and artifact SHAs when available. These tests cannot pass today because the host does not expose the ordered pre-ingestion state machine; that is the proof that Cases 3 and 4 remain open. ## Candidate independent identity signals diff --git a/docs/open-issue-runtime-identity-beacon.md b/docs/open-issue-runtime-identity-beacon.md index 18c81ef4a..fd228bd9f 100644 --- a/docs/open-issue-runtime-identity-beacon.md +++ b/docs/open-issue-runtime-identity-beacon.md @@ -32,19 +32,17 @@ Observed boundary: - `packages/squad-cli` passes through `copilotFlags`, `agentCmd`, or `--agent ` to the `copilot` process, but does not read/assemble `.github/agents/*.agent.md` into model messages (`packages/squad-cli/src/cli/commands/watch/agent-spawn.ts:95-140`, `packages/squad-cli/src/cli/commands/loop.ts:136-148`, `packages/squad-cli/src/cli/commands/watch/index.ts:608-615`, `packages/squad-cli/src/cli/commands/copilot-bridge.ts:72-84`). - The SDK wrapper also confirms that the Copilot CLI `--agent` flag is the path that reads on-disk agent definitions (`src/Squad.Agents.AI/SquadAgent.cs:249-282`, `src/Squad.Agents.AI/SquadAgentOptions.cs:87-112`, `src/Squad.Agents.AI/README.md:155-165`). -Therefore the reliable identity beacon must be a GitHub Copilot CLI host capability captured at the **agent-selection step** and persisted in host/session metadata before any `.agent.md` file access. An identity value derived only after successfully reading `.github/agents/squad.agent.md` is not sufficient: it disappears in exactly the empty/missing-payload case that must become `UNKNOWN`/halt. - -Squad-provided MCP state tools cannot close this gap either; if the coordinator payload is wholly absent, no instruction inside that absent payload can tell the model to call any `squad_state_*` identity tool. +Therefore the reliable identity beacon must be a GitHub Copilot CLI host capability captured at the **agent-selection step** and persisted in host/session metadata before any `.agent.md` file access. Squad-provided MCP state tools cannot close this gap; if the coordinator payload is wholly absent, no instruction inside that absent payload can tell the model to call any `squad_state_*` identity tool. ## Host-owned pre-ingestion state machine -The host must evaluate this state machine outside the prompt. A single pre-read field is insufficient; the state machine needs three distinct fields plus an attach-integrity result computed on the assembled artifact. +The host must evaluate this state machine outside the prompt. The state machine has ordered selection/read/attach events, then two orthogonal integrity verdicts: transport integrity for enforcement and release integrity for reporting/provenance. -### Fields +### Ordered fields 1. `selected_agent_id` - Immutable selection record created at agent-selection time **before any file access**. - - Carries raw host identity, for example: + - Carries raw host identity and registry keys, for example: - `custom_agent.name = "squad"` - `custom_agent.file = ".github/agents/squad.agent.md"` - `custom_agent.role = "root-coordinator"` @@ -52,63 +50,94 @@ The host must evaluate this state machine outside the prompt. A single pre-read - `client_id = ""` - `client_version = ""` - Must include host timestamp or monotonic order (for example, `selected_at` / `selection_order`). - - `agent_version`, `client_id`, and `client_version` key the trusted expected-SHA registry used by attach integrity. 2. `payload_read_status` - Separate READ-dimension transition recorded by the host **after** the `.agent.md` read attempt. - - Enum committed here: `LOADED`, `TRUNCATED`, `EMPTY`, `ABSENT`. + - Enum: `LOADED`, `TRUNCATED`, `EMPTY`, `ABSENT`. - `TRUNCATED` is reserved for a partial **file read** proven by expected/actual byte counts, expected EOF evidence, or equivalent host read evidence. - A completed file read whose prompt attachment is clipped is `payload_read_status=LOADED` plus `prompt_attach_status=ATTACHED_PARTIAL`; do not encode attach clipping as read truncation. - - Important: `LOADED` only proves the source file was read; it does **not** prove the full payload reached the assembled prompt. - - Must include host timestamp or monotonic order. + - Must record `source_sha_after_read` when bytes were read. 3. `prompt_attach_status` - Records what actually reached the assembled prompt. - - Attach enum: `ATTACHED_FULL`, `ATTACHED_PARTIAL`, `NOT_ATTACHED`. - - Integrity subfield: `attach_integrity` enum `INTACT`, `BOUNDARY_MISSING`, `PARTIAL`, `SHA_MISMATCH`. - - `expected_sha` MUST come from a trusted host-side registry keyed by (`agent_version`, `client_id`, `client_version`); it must NEVER be derived from the attached or assembled bytes. - - `attached_sha` is computed by the host over the **assembled artifact**. - - `INTACT` means both boundary markers (HEAD canary and EOF canary) are present in the assembled artifact **and** `attached_sha == expected_sha` from trusted provenance (for the current Squad artifact, the expected SHA is `525a919f2b8c3c2586dcb2862de3cff63c85128cd63702e96c83be83d0ed631f` when that registry key applies). - - `BOUNDARY_MISSING` / `PARTIAL` means the host read may have succeeded but prompt assembly dropped a boundary or tail; this is attach-dimension clipping and fails closed even when `payload_read_status=LOADED`. - - `SHA_MISMATCH` means `attached_sha != expected_sha`; the wrong or mutated payload was attached, so this is a HALT. - - Must include host timestamp or monotonic order, `attached_sha` when available, and the trusted `expected_sha` registry key/provenance. + - Enum: `ATTACHED_FULL`, `ATTACHED_PARTIAL`, `NOT_ATTACHED`. + - Must record `attached_sha` when bytes were attached, computed by the host over the **assembled artifact**. + +### Transport integrity (enforcement axis) + +`transport_integrity ∈ { INTACT, PARTIAL, UNKNOWN }` + +This axis detects read/assembly loss and is enforced fail-closed. It compares `source_sha_after_read` to `attached_sha` **and** requires independent source completeness proof. Hash equality alone is insufficient: a partial host read can match an equally partial attachment. + +- `INTACT`: source proven fully read by EOF/boundary evidence or expected byte length; `attached_sha == source_sha_after_read`; attached artifact contains required boundaries (HEAD and EOF canaries). +- `PARTIAL`: read or assembly truncation detected, including missing EOF/boundary, short byte length, partial file read, or attach clipping. +- `UNKNOWN`: insufficient evidence to prove transport integrity. + +Policy: `PARTIAL` or `UNKNOWN` => HALT / UNKNOWN-halt. Transport loss dominates release status. + +### Release integrity (reporting/provenance axis) + +`release_integrity ∈ { VERIFIED, MISMATCH, UNREGISTERED }` + +This axis compares `source_sha_after_read` with an optional trusted, preferably signed or release-bound manifest keyed by (`agent_version`, `client_id`, `client_version`). The expected SHA provenance rule applies here only: expected release SHAs must come from the trusted host-side manifest and must NEVER be derived from attached or assembled bytes. + +- `VERIFIED`: `source_sha_after_read` matches the trusted manifest entry. +- `MISMATCH`: `source_sha_after_read` differs from the trusted manifest entry for that version (wrong or mutated release). +- `UNREGISTERED`: no manifest entry exists, the version key is unknown, the artifact is user-local, or the Squad artifact is newly updated/unpublished. + +Policy: no manifest entry or unknown version key => `UNREGISTERED`, never `MISMATCH`, and never accept `attached_sha` as fallback. `UNREGISTERED` is surfaced distinctly and does **not** by itself halt a healthy session. Only `MISMATCH` escalates/halts as a proven wrong release. ### Frozen transition invariants 1. `selected_agent_id` is emitted before `payload_read_status`. -2. `payload_read_status` is emitted before `prompt_attach_status` / `attach_integrity`. -3. `attach_integrity` is computed by the host on the **assembled artifact**, outside the prompt, using both `attached_sha` and the boundary-marker check; `expected_sha` comes only from the trusted host-side registry keyed by (`agent_version`, `client_id`, `client_version`). -4. Any missing event in the ordered sequence resolves to `UNKNOWN`; absent evidence never resolves to success. -5. `selected + missing` must never collapse into `not selected`. +2. `payload_read_status` is emitted before `prompt_attach_status`. +3. `transport_integrity` is computed by the host on source/read/assembled artifacts, outside the prompt, using `source_sha_after_read`, `attached_sha`, boundary markers, and independent source completeness proof. +4. `release_integrity` is computed separately from a trusted manifest keyed by (`agent_version`, `client_id`, `client_version`) and `source_sha_after_read`. +5. Any missing event in the ordered sequence resolves to `UNKNOWN`; absent evidence never resolves to success. +6. `selected + missing` must never collapse into `not selected`. ### Decision function -Evaluated outside the prompt. This mapping fails closed: absent or partial evidence never resolves to success. +Evaluated outside the prompt: | Host state | Outcome | |---|---| -| `selected_agent_id` set + `payload_read_status=LOADED` + `prompt_attach_status=ATTACHED_FULL` + `attach_integrity=INTACT` | `LOADED`; this is the sole success state for selected Squad | -| `selected_agent_id` set + `payload_read_status=EMPTY` or `payload_read_status=ABSENT` | `UNKNOWN`/halt; empty or absent read evidence is not success | -| `selected_agent_id` set + `prompt_attach_status=ATTACHED_PARTIAL` or `prompt_attach_status=NOT_ATTACHED` | `UNKNOWN`/halt; partial or absent attach evidence is not success | -| `selected_agent_id` set + `payload_read_status=TRUNCATED` | `HALT`; partial file read proven in the READ dimension | -| `selected_agent_id` set + `attach_integrity=SHA_MISMATCH` | `HALT`; wrong or mutated payload attached | -| `selected_agent_id` set + `attach_integrity=BOUNDARY_MISSING` or `attach_integrity=PARTIAL` | `UNKNOWN`/halt unless the host can additionally prove a read-dimension `TRUNCATED`; attach clipping is not success | +| `selected_agent_id` set + `transport_integrity=INTACT` + `release_integrity=VERIFIED` | `LOADED` | +| `selected_agent_id` set + `transport_integrity=INTACT` + `release_integrity=UNREGISTERED` | `LOADED` with unregistered-local / unregistered-release report; MUST NOT halt | +| `selected_agent_id` set + `transport_integrity=INTACT` + `release_integrity=MISMATCH` | `HALT`; proven wrong or mutated release | +| `selected_agent_id` set + `transport_integrity=PARTIAL` | `HALT`; read/assembly transport loss dominates release status | +| `selected_agent_id` set + `transport_integrity=UNKNOWN` | `UNKNOWN`/halt; insufficient transport evidence dominates release status | | `selected_agent_id` absent because a Squad coordinator was independently proven not selected | `SKIP`; safe non-Squad classification is based on host selection state, not canary absence | | Any missing event in the expected ordered sequence | `UNKNOWN`; absent evidence never resolves to success | -## Four-fixture acceptance test +## Acceptance tests -Each fixture must assert the final outcome **and** the raw ordered event sequence: selection record -> read result -> attach status/integrity. Each fixture must include the attached-artifact SHA when available and explicitly preserve `UNKNOWN` for absent or partial evidence. A separate read-truncation fixture may assert `payload_read_status=TRUNCATED -> HALT`, but attach clipping must remain `LOADED + ATTACHED_PARTIAL`, not read `TRUNCATED`. +Each fixture must assert the final outcome **and** the raw ordered event sequence: selection record -> read result -> attach result -> `transport_integrity` -> `release_integrity`. Each fixture records both verdict axes and artifact SHAs when available. -| Fixture | Setup | Required ordered event sequence | Expected outcome | +| Fixture | Setup | Required ordered evidence | Expected outcome | |---|---|---|---| -| A. Full Squad payload | Select `--agent squad`; inject full `squad.agent.md` with HEAD and EOF canaries | `selected_agent_id` set with `agent_version`, `client_id`, `client_version` -> `payload_read_status=LOADED` -> `prompt_attach_status=ATTACHED_FULL`, `attach_integrity=INTACT`, `attached_sha` equals trusted-registry `expected_sha` | `LOADED`; normal Squad behavior may proceed | -| B. Head-only / assembly-clipped Squad payload | Select `--agent squad`; source read succeeds, but assembled prompt contains HEAD canary without EOF canary | `selected_agent_id` set with registry keys -> `payload_read_status=LOADED` -> `prompt_attach_status=ATTACHED_PARTIAL`, `attach_integrity=BOUNDARY_MISSING` or `PARTIAL`, `attached_sha` recorded when available | `UNKNOWN`/halt; fail closed because attach evidence is partial | -| C. Missing/empty Squad payload | Select `--agent squad`; suppress, empty, or fail reading the coordinator payload entirely | `selected_agent_id` set -> `payload_read_status=EMPTY` or `payload_read_status=ABSENT` -> `prompt_attach_status=NOT_ATTACHED`; no attached SHA | `UNKNOWN`/halt; no silent continuation | -| D. Proven non-Squad | Select a host-proven non-Squad/default agent with no Squad coordinator payload | Squad `selected_agent_id` absent; non-Squad/default selection evidence emitted by host; no Squad coordinator payload attached | `SKIP`; safe non-Squad classification is based on host beacon, not canary absence | +| 1. Full payload, registered | Select `--agent squad`; full `squad.agent.md` read and attached; manifest entry exists | `selected_agent_id` set -> `payload_read_status=LOADED`, `source_sha_after_read` -> `prompt_attach_status=ATTACHED_FULL`, `attached_sha` -> `transport_integrity=INTACT` -> `release_integrity=VERIFIED` | `LOADED` | +| 2. Head-only / oversized-prompt clip | Select `--agent squad`; source may read fully, but assembled prompt loses tail/EOF | ordered events emitted; `prompt_attach_status=ATTACHED_PARTIAL` or missing boundary; `transport_integrity=PARTIAL`; release verdict still recorded if possible | `HALT` | +| 3. Missing/empty Squad payload | Select `--agent squad`; suppress, empty, or fail reading coordinator payload | `selected_agent_id` set -> `payload_read_status=EMPTY` or `ABSENT` -> `prompt_attach_status=NOT_ATTACHED` -> `transport_integrity=UNKNOWN`; release usually `UNREGISTERED`/unavailable | `UNKNOWN`/halt | +| 4. Proven non-Squad | Host proves Squad coordinator was not selected | Squad `selected_agent_id` absent; non-Squad/default selection evidence emitted by host | `SKIP` | +| 5. WRONG-VERSION | Full source read and full attachment, but source SHA differs from trusted manifest for (`agent_version`, `client_id`, `client_version`) | `transport_integrity=INTACT`; `release_integrity=MISMATCH`; source/attached SHA recorded | `HALT` | +| 6. LEGITIMATE UNREGISTERED-LOCAL | Healthy user-local custom agent or freshly-updated/unpublished Squad artifact with no manifest entry | `transport_integrity=INTACT`; `release_integrity=UNREGISTERED`; source/attached SHA recorded | `LOADED` with unregistered-local report; MUST NOT false-halt | + +Fixtures 5 and 6 exist to measure whether `UNKNOWN`/`HALT` protects safety without an unacceptable healthy-session false-halt rate. Fixture 6 is especially important: registry staleness or local customization must not be converted into a release `MISMATCH` or transport failure. + +## Caveman telemetry join protocol (not started) + +When Caveman joins, pin the static identity **before execution**: + +- artifact SHA: `525a919f2b8c3c2586dcb2862de3cff63c85128cd63702e96c83be83d0ed631f` +- client, client version, model, tokenizer +- raw usage/cache objects +- fresh-vs-continued condition + +Run one fresh session and one continued session. Join telemetry only on the exact static identity above. Caveman remains **NOT-STARTED**. ## In-Squad vs upstream split **In Squad's control:** install and upgrade the custom-agent file, pass/advise `--agent squad`, keep canary payload-integrity wording accurate, write/load `.mcp.json`, document Cases 1-4, and consume the future host state machine when exposed. -**Upstream Copilot CLI capability required:** the host-owned state machine above, evaluated before and outside prompt ingestion, plus the trusted expected-SHA registry keyed by (`agent_version`, `client_id`, `client_version`). The `.agent.md` file must not be able to overwrite, suppress, or fabricate `selected_agent_id`, `payload_read_status`, `prompt_attach_status`, `attach_integrity`, `expected_sha`, or `attached_sha`. +**Upstream Copilot CLI capability required:** the host-owned state machine above, evaluated before and outside prompt ingestion, plus the optional trusted/signed release manifest keyed by (`agent_version`, `client_id`, `client_version`). The `.agent.md` file must not be able to overwrite, suppress, or fabricate `selected_agent_id`, `payload_read_status`, `prompt_attach_status`, `source_sha_after_read`, `attached_sha`, `transport_integrity`, or `release_integrity`. From d8e9ee8567f35144eb7734e03fcc97dbc9ed659a Mon Sep 17 00:00:00 2001 From: Brady Gaster Date: Tue, 21 Jul 2026 01:35:28 -0700 Subject: [PATCH 13/15] docs(canary): distinguish corrupt halt from unknown halt Splits transport halt outcomes into HALT_CORRUPT for proven read/assembly loss and UNKNOWN_HALT for insufficient evidence. Keeps release mismatch and unregistered-local outcomes distinct so telemetry can measure corruption, missing evidence, and false-halt risk separately. Refs #1461 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09d05820-0b7e-482b-b942-c86b1225dcf9 --- docs/canary-identity-matrix.md | 4 ++-- docs/open-issue-runtime-identity-beacon.md | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/canary-identity-matrix.md b/docs/canary-identity-matrix.md index 32bf20486..2586893f1 100644 --- a/docs/canary-identity-matrix.md +++ b/docs/canary-identity-matrix.md @@ -30,7 +30,7 @@ Repository inspection points to the custom-agent host runtime's **agent-selectio - `squad upgrade` refreshes the same `.github/agents/squad.agent.md` target from `squad.agent.md.template` (`packages/squad-cli/src/cli/core/upgrade.ts:1048`, `packages/squad-cli/src/cli/core/upgrade.ts:1102-1108`, `packages/squad-cli/src/cli/core/upgrade.ts:1124-1137`). - In `packages/squad-cli`, Squad does not implement the custom-agent resolver or read `.github/agents/*.agent.md` into the model prompt. Init prints the user-facing invocation `copilot --agent squad` (`packages/squad-cli/src/cli/core/init.ts:515-518`); spawned-agent helpers pass user/config-supplied `copilotFlags` or `agentCmd` through to the `copilot` process (`packages/squad-cli/src/cli/commands/watch/agent-spawn.ts:95-140`, `packages/squad-cli/src/cli/commands/loop.ts:136-148`, `packages/squad-cli/src/cli/commands/watch/index.ts:608-615`); and the ACP bridge appends `--agent ` before spawning `copilot --acp --stdio` (`packages/squad-cli/src/cli/commands/copilot-bridge.ts:72-84`). None of these code paths persists selected-agent identity into host/session metadata before instruction loading, and none reads/assembles the agent file. - The repository's SDK wrapper confirms the boundary: `SquadAgent` can add `--agent squad` when `.github/agents/squad.agent.md` exists, but its comments state that the Copilot CLI `--agent` flag is the path that reads on-disk agent definitions (`src/Squad.Agents.AI/SquadAgent.cs:249-282`, `src/Squad.Agents.AI/SquadAgentOptions.cs:87-112`, `src/Squad.Agents.AI/README.md:155-165`). -- Therefore a beacon derived only after a successful `.agent.md` file read or message assembly is insufficient: it vanishes in the payload-absent case along with the canaries. The host must persist raw selection metadata when it resolves `--agent squad` (or equivalent custom-agent selection), then separately record read and attach events. Integrity must split into two orthogonal verdicts: `transport_integrity=INTACT|PARTIAL|UNKNOWN` for enforcement, and `release_integrity=VERIFIED|MISMATCH|UNREGISTERED` for release provenance/reporting. Transport compares `source_sha_after_read` to host-computed `attached_sha` and requires independent completeness evidence (EOF/boundary/expected length); hash equality alone is insufficient. Release compares `source_sha_after_read` to an optional trusted manifest keyed by (`agent_version`, `client_id`, `client_version`); missing manifest entries are `UNREGISTERED`, not `MISMATCH`, and must not false-halt healthy local/new artifacts. +- Therefore a beacon derived only after a successful `.agent.md` file read or message assembly is insufficient: it vanishes in the payload-absent case along with the canaries. The host must persist raw selection metadata when it resolves `--agent squad` (or equivalent custom-agent selection), then separately record read and attach events. Integrity must split into two orthogonal verdicts: `transport_integrity=INTACT|PARTIAL|UNKNOWN` for enforcement, and `release_integrity=VERIFIED|MISMATCH|UNREGISTERED` for release provenance/reporting. Transport outcomes are further classified as `PARTIAL -> HALT_CORRUPT` (proven corruption/loss) and `UNKNOWN -> UNKNOWN_HALT` (insufficient evidence, not proven corruption). Transport compares `source_sha_after_read` to host-computed `attached_sha` and requires independent completeness evidence (EOF/boundary/expected length); hash equality alone is insufficient. Release compares `source_sha_after_read` to an optional trusted manifest keyed by (`agent_version`, `client_id`, `client_version`); missing manifest entries are `UNREGISTERED`, not `MISMATCH`, and must not false-halt healthy local/new artifacts. - Squad also writes a repo-root `.mcp.json` `squad_state` entry for Copilot CLI to load (`packages/squad-cli/src/cli/core/mcp-root.ts:9-14`, `packages/squad-cli/src/cli/core/mcp-root.ts:59-73`) and explicitly injects `--additional-mcp-config @` for non-interactive spawned sessions so `squad_state_*` tools register (`packages/squad-cli/src/cli/core/copilot-invocation.ts:1-16`, `packages/squad-cli/src/cli/core/copilot-invocation.ts:53-66`). The MCP server exposes `squad_state_*`, `squad_decide`, and `memory.*` tools (`packages/squad-cli/src/cli/commands/state-mcp.ts:24-35`). **Fatal flaw:** if the coordinator payload is wholly absent, no instruction inside that absent payload can tell the model to call an MCP identity tool, so a Squad-provided MCP beacon cannot close Case 3. - The only Copilot-related environment signal found in the inspected CLI code is `COPILOT_CLI`, used as a coarse environment heuristic (`packages/squad-cli/src/cli/copilot-install.ts:69-85`). It is not a custom-agent identity, role, selected agent file, selection timestamp/order, or payload-load attestation. @@ -40,7 +40,7 @@ Repository inspection points to the custom-agent host runtime's **agent-selectio ## Required runtime test -Add host-level behavioral tests that assert both the final outcome and raw ordered event sequence: `selected_agent_id` -> `payload_read_status` -> `prompt_attach_status` -> `transport_integrity` -> `release_integrity`. Required fixtures: registered full payload (`INTACT`, `VERIFIED` -> `LOADED`), head-only/oversized-prompt clip (`PARTIAL` -> `HALT`), missing/empty selected Squad payload (`UNKNOWN` -> `UNKNOWN`/halt), proven non-Squad (`SKIP`), wrong-version (`INTACT`, `MISMATCH` -> `HALT`), and legitimate unregistered-local/fresh artifact (`INTACT`, `UNREGISTERED` -> `LOADED` with report, no false halt). Each fixture records both axes and artifact SHAs when available. These tests cannot pass today because the host does not expose the ordered pre-ingestion state machine; that is the proof that Cases 3 and 4 remain open. +Add host-level behavioral tests that assert both the final outcome and raw ordered event sequence: `selected_agent_id` -> `payload_read_status` -> `prompt_attach_status` -> `transport_integrity` -> `release_integrity`. Required fixtures: registered full payload (`INTACT`, `VERIFIED` -> `LOADED`), head-only/oversized-prompt clip (`PARTIAL` -> `HALT_CORRUPT`), missing/empty selected Squad payload (`UNKNOWN` -> `UNKNOWN_HALT`), proven non-Squad (`SKIP`), wrong-version (`INTACT`, `MISMATCH` -> `HALT_RELEASE_MISMATCH`), and legitimate unregistered-local/fresh artifact (`INTACT`, `UNREGISTERED` -> `LOADED_WITH_REPORT`, no false halt). `HALT_CORRUPT` vs `UNKNOWN_HALT` is a telemetry-classification distinction so insufficient evidence is not counted as proven corruption. Each fixture records both axes and artifact SHAs when available. These tests cannot pass today because the host does not expose the ordered pre-ingestion state machine; that is the proof that Cases 3 and 4 remain open. ## Candidate independent identity signals diff --git a/docs/open-issue-runtime-identity-beacon.md b/docs/open-issue-runtime-identity-beacon.md index fd228bd9f..e7833b574 100644 --- a/docs/open-issue-runtime-identity-beacon.md +++ b/docs/open-issue-runtime-identity-beacon.md @@ -73,7 +73,7 @@ This axis detects read/assembly loss and is enforced fail-closed. It compares `s - `PARTIAL`: read or assembly truncation detected, including missing EOF/boundary, short byte length, partial file read, or attach clipping. - `UNKNOWN`: insufficient evidence to prove transport integrity. -Policy: `PARTIAL` or `UNKNOWN` => HALT / UNKNOWN-halt. Transport loss dominates release status. +Policy: `PARTIAL` => `HALT_CORRUPT` (proven read/assembly loss). `UNKNOWN` => `UNKNOWN_HALT` (insufficient evidence; halts for safety but is not classified as proven corruption). Both stop the session, but the distinction is required for telemetry so missing evidence does not inflate the corruption count. ### Release integrity (reporting/provenance axis) @@ -103,12 +103,12 @@ Evaluated outside the prompt: | Host state | Outcome | |---|---| | `selected_agent_id` set + `transport_integrity=INTACT` + `release_integrity=VERIFIED` | `LOADED` | -| `selected_agent_id` set + `transport_integrity=INTACT` + `release_integrity=UNREGISTERED` | `LOADED` with unregistered-local / unregistered-release report; MUST NOT halt | -| `selected_agent_id` set + `transport_integrity=INTACT` + `release_integrity=MISMATCH` | `HALT`; proven wrong or mutated release | -| `selected_agent_id` set + `transport_integrity=PARTIAL` | `HALT`; read/assembly transport loss dominates release status | -| `selected_agent_id` set + `transport_integrity=UNKNOWN` | `UNKNOWN`/halt; insufficient transport evidence dominates release status | +| `selected_agent_id` set + `transport_integrity=INTACT` + `release_integrity=UNREGISTERED` | `LOADED_WITH_REPORT`; unregistered-local / unregistered-release report; MUST NOT halt | +| `selected_agent_id` set + `transport_integrity=INTACT` + `release_integrity=MISMATCH` | `HALT_RELEASE_MISMATCH`; proven wrong or mutated release | +| `selected_agent_id` set + `transport_integrity=PARTIAL` | `HALT_CORRUPT`; proven read/assembly transport loss dominates release status | +| `selected_agent_id` set + `transport_integrity=UNKNOWN` | `UNKNOWN_HALT`; insufficient transport evidence dominates release status and is not proven corruption | | `selected_agent_id` absent because a Squad coordinator was independently proven not selected | `SKIP`; safe non-Squad classification is based on host selection state, not canary absence | -| Any missing event in the expected ordered sequence | `UNKNOWN`; absent evidence never resolves to success | +| Any missing event in the expected ordered sequence | `UNKNOWN_HALT`; absent evidence never resolves to success and is not proven corruption | ## Acceptance tests @@ -117,13 +117,13 @@ Each fixture must assert the final outcome **and** the raw ordered event sequenc | Fixture | Setup | Required ordered evidence | Expected outcome | |---|---|---|---| | 1. Full payload, registered | Select `--agent squad`; full `squad.agent.md` read and attached; manifest entry exists | `selected_agent_id` set -> `payload_read_status=LOADED`, `source_sha_after_read` -> `prompt_attach_status=ATTACHED_FULL`, `attached_sha` -> `transport_integrity=INTACT` -> `release_integrity=VERIFIED` | `LOADED` | -| 2. Head-only / oversized-prompt clip | Select `--agent squad`; source may read fully, but assembled prompt loses tail/EOF | ordered events emitted; `prompt_attach_status=ATTACHED_PARTIAL` or missing boundary; `transport_integrity=PARTIAL`; release verdict still recorded if possible | `HALT` | -| 3. Missing/empty Squad payload | Select `--agent squad`; suppress, empty, or fail reading coordinator payload | `selected_agent_id` set -> `payload_read_status=EMPTY` or `ABSENT` -> `prompt_attach_status=NOT_ATTACHED` -> `transport_integrity=UNKNOWN`; release usually `UNREGISTERED`/unavailable | `UNKNOWN`/halt | +| 2. Head-only / oversized-prompt clip | Select `--agent squad`; source may read fully, but assembled prompt loses tail/EOF | ordered events emitted; `prompt_attach_status=ATTACHED_PARTIAL` or missing boundary; `transport_integrity=PARTIAL`; release verdict still recorded if possible | `HALT_CORRUPT` | +| 3. Missing/empty Squad payload | Select `--agent squad`; suppress, empty, or fail reading coordinator payload | `selected_agent_id` set -> `payload_read_status=EMPTY` or `ABSENT` -> `prompt_attach_status=NOT_ATTACHED` -> `transport_integrity=UNKNOWN`; release usually `UNREGISTERED`/unavailable | `UNKNOWN_HALT` | | 4. Proven non-Squad | Host proves Squad coordinator was not selected | Squad `selected_agent_id` absent; non-Squad/default selection evidence emitted by host | `SKIP` | -| 5. WRONG-VERSION | Full source read and full attachment, but source SHA differs from trusted manifest for (`agent_version`, `client_id`, `client_version`) | `transport_integrity=INTACT`; `release_integrity=MISMATCH`; source/attached SHA recorded | `HALT` | -| 6. LEGITIMATE UNREGISTERED-LOCAL | Healthy user-local custom agent or freshly-updated/unpublished Squad artifact with no manifest entry | `transport_integrity=INTACT`; `release_integrity=UNREGISTERED`; source/attached SHA recorded | `LOADED` with unregistered-local report; MUST NOT false-halt | +| 5. WRONG-VERSION | Full source read and full attachment, but source SHA differs from trusted manifest for (`agent_version`, `client_id`, `client_version`) | `transport_integrity=INTACT`; `release_integrity=MISMATCH`; source/attached SHA recorded | `HALT_RELEASE_MISMATCH` | +| 6. LEGITIMATE UNREGISTERED-LOCAL | Healthy user-local custom agent or freshly-updated/unpublished Squad artifact with no manifest entry | `transport_integrity=INTACT`; `release_integrity=UNREGISTERED`; source/attached SHA recorded | `LOADED_WITH_REPORT`; MUST NOT false-halt | -Fixtures 5 and 6 exist to measure whether `UNKNOWN`/`HALT` protects safety without an unacceptable healthy-session false-halt rate. Fixture 6 is especially important: registry staleness or local customization must not be converted into a release `MISMATCH` or transport failure. +Fixtures 5 and 6 exist to measure whether safety halts protect users without an unacceptable healthy-session false-halt rate. Telemetry must keep `HALT_CORRUPT` separate from `UNKNOWN_HALT` so missing evidence is not counted as proven corruption. Fixture 6 is especially important: registry staleness or local customization must not be converted into a release `MISMATCH` or transport failure. ## Caveman telemetry join protocol (not started) From 8b620f95b8e1ec997b764705ea066b76bb7088af Mon Sep 17 00:00:00 2001 From: Brady Gaster Date: Tue, 21 Jul 2026 02:23:37 -0700 Subject: [PATCH 14/15] docs(canary): add inventory drift envelope dimension Adds host tool inventory to the preregistered execution envelope. Defines per-turn inventory snapshots, manifest hashing provenance, inventory_drift outcomes, variant/quarantine rules, and telemetry-join invalidation semantics orthogonal to artifact integrity. Refs #1461 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09d05820-0b7e-482b-b942-c86b1225dcf9 --- docs/canary-identity-matrix.md | 6 ++-- docs/open-issue-runtime-identity-beacon.md | 37 ++++++++++++++++++---- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/docs/canary-identity-matrix.md b/docs/canary-identity-matrix.md index 2586893f1..1f749e37b 100644 --- a/docs/canary-identity-matrix.md +++ b/docs/canary-identity-matrix.md @@ -30,17 +30,17 @@ Repository inspection points to the custom-agent host runtime's **agent-selectio - `squad upgrade` refreshes the same `.github/agents/squad.agent.md` target from `squad.agent.md.template` (`packages/squad-cli/src/cli/core/upgrade.ts:1048`, `packages/squad-cli/src/cli/core/upgrade.ts:1102-1108`, `packages/squad-cli/src/cli/core/upgrade.ts:1124-1137`). - In `packages/squad-cli`, Squad does not implement the custom-agent resolver or read `.github/agents/*.agent.md` into the model prompt. Init prints the user-facing invocation `copilot --agent squad` (`packages/squad-cli/src/cli/core/init.ts:515-518`); spawned-agent helpers pass user/config-supplied `copilotFlags` or `agentCmd` through to the `copilot` process (`packages/squad-cli/src/cli/commands/watch/agent-spawn.ts:95-140`, `packages/squad-cli/src/cli/commands/loop.ts:136-148`, `packages/squad-cli/src/cli/commands/watch/index.ts:608-615`); and the ACP bridge appends `--agent ` before spawning `copilot --acp --stdio` (`packages/squad-cli/src/cli/commands/copilot-bridge.ts:72-84`). None of these code paths persists selected-agent identity into host/session metadata before instruction loading, and none reads/assembles the agent file. - The repository's SDK wrapper confirms the boundary: `SquadAgent` can add `--agent squad` when `.github/agents/squad.agent.md` exists, but its comments state that the Copilot CLI `--agent` flag is the path that reads on-disk agent definitions (`src/Squad.Agents.AI/SquadAgent.cs:249-282`, `src/Squad.Agents.AI/SquadAgentOptions.cs:87-112`, `src/Squad.Agents.AI/README.md:155-165`). -- Therefore a beacon derived only after a successful `.agent.md` file read or message assembly is insufficient: it vanishes in the payload-absent case along with the canaries. The host must persist raw selection metadata when it resolves `--agent squad` (or equivalent custom-agent selection), then separately record read and attach events. Integrity must split into two orthogonal verdicts: `transport_integrity=INTACT|PARTIAL|UNKNOWN` for enforcement, and `release_integrity=VERIFIED|MISMATCH|UNREGISTERED` for release provenance/reporting. Transport outcomes are further classified as `PARTIAL -> HALT_CORRUPT` (proven corruption/loss) and `UNKNOWN -> UNKNOWN_HALT` (insufficient evidence, not proven corruption). Transport compares `source_sha_after_read` to host-computed `attached_sha` and requires independent completeness evidence (EOF/boundary/expected length); hash equality alone is insufficient. Release compares `source_sha_after_read` to an optional trusted manifest keyed by (`agent_version`, `client_id`, `client_version`); missing manifest entries are `UNREGISTERED`, not `MISMATCH`, and must not false-halt healthy local/new artifacts. +- Therefore a beacon derived only after a successful `.agent.md` file read or message assembly is insufficient: it vanishes in the payload-absent case along with the canaries. The host must persist raw selection metadata when it resolves `--agent squad` (or equivalent custom-agent selection), then separately record read and attach events. Integrity must split into two orthogonal verdicts: `transport_integrity=INTACT|PARTIAL|UNKNOWN` for enforcement, and `release_integrity=VERIFIED|MISMATCH|UNREGISTERED` for release provenance/reporting. Transport outcomes are further classified as `PARTIAL -> HALT_CORRUPT` (proven corruption/loss) and `UNKNOWN -> UNKNOWN_HALT` (insufficient evidence, not proven corruption). Transport compares `source_sha_after_read` to host-computed `attached_sha` and requires independent completeness evidence (EOF/boundary/expected length); hash equality alone is insufficient. Release compares `source_sha_after_read` to an optional trusted manifest keyed by (`agent_version`, `client_id`, `client_version`); missing manifest entries are `UNREGISTERED`, not `MISMATCH`, and must not false-halt healthy local/new artifacts. The preregistration unit is the full execution envelope, so host tool inventory adds an orthogonal `inventory_drift=NONE|EXPECTED_VARIANT|INVALIDATING` dimension measured before the first turn and immediately before each measured turn. - Squad also writes a repo-root `.mcp.json` `squad_state` entry for Copilot CLI to load (`packages/squad-cli/src/cli/core/mcp-root.ts:9-14`, `packages/squad-cli/src/cli/core/mcp-root.ts:59-73`) and explicitly injects `--additional-mcp-config @` for non-interactive spawned sessions so `squad_state_*` tools register (`packages/squad-cli/src/cli/core/copilot-invocation.ts:1-16`, `packages/squad-cli/src/cli/core/copilot-invocation.ts:53-66`). The MCP server exposes `squad_state_*`, `squad_decide`, and `memory.*` tools (`packages/squad-cli/src/cli/commands/state-mcp.ts:24-35`). **Fatal flaw:** if the coordinator payload is wholly absent, no instruction inside that absent payload can tell the model to call an MCP identity tool, so a Squad-provided MCP beacon cannot close Case 3. - The only Copilot-related environment signal found in the inspected CLI code is `COPILOT_CLI`, used as a coarse environment heuristic (`packages/squad-cli/src/cli/copilot-install.ts:69-85`). It is not a custom-agent identity, role, selected agent file, selection timestamp/order, or payload-load attestation. **IN Squad's control:** install and upgrade the custom-agent file, pass/advise `--agent squad`, keep canary payload-integrity wording accurate, write/load `.mcp.json`, and document/test Cases 1 and 2. Squad can also request and consume a future host signal. -**UPSTREAM Copilot CLI capability required:** a host-guaranteed, untruncatable identity beacon captured at custom-agent **selection time**, persisted in session metadata before file read / instruction assembly, and then augmented with read, attach, transport, and release verdicts. Required state fields are `selected_agent_id` (including `agent_version`, `client_id`, `client_version`), `payload_read_status=LOADED|TRUNCATED|EMPTY|ABSENT`, `prompt_attach_status=ATTACHED_FULL|ATTACHED_PARTIAL|NOT_ATTACHED`, `source_sha_after_read`, `attached_sha`, `transport_integrity=INTACT|PARTIAL|UNKNOWN`, and `release_integrity=VERIFIED|MISMATCH|UNREGISTERED`. The agent file must not be able to overwrite or suppress these values. +**UPSTREAM Copilot CLI capability required:** a host-guaranteed, untruncatable identity beacon captured at custom-agent **selection time**, persisted in session metadata before file read / instruction assembly, and then augmented with read, attach, transport, and release verdicts. Required state fields are `selected_agent_id` (including `agent_version`, `client_id`, `client_version`), `payload_read_status=LOADED|TRUNCATED|EMPTY|ABSENT`, `prompt_attach_status=ATTACHED_FULL|ATTACHED_PARTIAL|NOT_ATTACHED`, `source_sha_after_read`, `attached_sha`, `transport_integrity=INTACT|PARTIAL|UNKNOWN`, `release_integrity=VERIFIED|MISMATCH|UNREGISTERED`, and per-turn `inventory_drift=NONE|EXPECTED_VARIANT|INVALIDATING` with raw non-secret tool manifest, manifest hash, canonicalizer version, serializer version, and exact before/after manifest diff. The agent file must not be able to overwrite or suppress these values. ## Required runtime test -Add host-level behavioral tests that assert both the final outcome and raw ordered event sequence: `selected_agent_id` -> `payload_read_status` -> `prompt_attach_status` -> `transport_integrity` -> `release_integrity`. Required fixtures: registered full payload (`INTACT`, `VERIFIED` -> `LOADED`), head-only/oversized-prompt clip (`PARTIAL` -> `HALT_CORRUPT`), missing/empty selected Squad payload (`UNKNOWN` -> `UNKNOWN_HALT`), proven non-Squad (`SKIP`), wrong-version (`INTACT`, `MISMATCH` -> `HALT_RELEASE_MISMATCH`), and legitimate unregistered-local/fresh artifact (`INTACT`, `UNREGISTERED` -> `LOADED_WITH_REPORT`, no false halt). `HALT_CORRUPT` vs `UNKNOWN_HALT` is a telemetry-classification distinction so insufficient evidence is not counted as proven corruption. Each fixture records both axes and artifact SHAs when available. These tests cannot pass today because the host does not expose the ordered pre-ingestion state machine; that is the proof that Cases 3 and 4 remain open. +Add host-level behavioral tests that assert both the final outcome and raw ordered event sequence: `selected_agent_id` -> `payload_read_status` -> `prompt_attach_status` -> `transport_integrity` -> `release_integrity`. Required fixtures: registered full payload (`INTACT`, `VERIFIED` -> `LOADED`), head-only/oversized-prompt clip (`PARTIAL` -> `HALT_CORRUPT`), missing/empty selected Squad payload (`UNKNOWN` -> `UNKNOWN_HALT`), proven non-Squad (`SKIP`), wrong-version (`INTACT`, `MISMATCH` -> `HALT_RELEASE_MISMATCH`), and legitimate unregistered-local/fresh artifact (`INTACT`, `UNREGISTERED` -> `LOADED_WITH_REPORT`, no false halt). `HALT_CORRUPT` vs `UNKNOWN_HALT` is a telemetry-classification distinction so insufficient evidence is not counted as proven corruption. Each fixture records both artifact axes, artifact SHAs when available, and the inventory-drift envelope axis. `EXPECTED_VARIANT` becomes a separately labeled cell, `INVALIDATING` invalidates affected turns without implying artifact corruption, and undeclared captured drift is quarantined as `EXPLORATORY` rather than pooled. These tests cannot pass today because the host does not expose the ordered pre-ingestion state machine; that is the proof that Cases 3 and 4 remain open. ## Candidate independent identity signals diff --git a/docs/open-issue-runtime-identity-beacon.md b/docs/open-issue-runtime-identity-beacon.md index e7833b574..e2afba9a1 100644 --- a/docs/open-issue-runtime-identity-beacon.md +++ b/docs/open-issue-runtime-identity-beacon.md @@ -36,7 +36,7 @@ Therefore the reliable identity beacon must be a GitHub Copilot CLI host capabil ## Host-owned pre-ingestion state machine -The host must evaluate this state machine outside the prompt. The state machine has ordered selection/read/attach events, then two orthogonal integrity verdicts: transport integrity for enforcement and release integrity for reporting/provenance. +The host must evaluate this state machine outside the prompt. The preregistration unit is the **execution envelope**, not just the prompt artifact; the host tool inventory is part of the measured envelope. The state machine has ordered selection/read/attach events, two orthogonal artifact-integrity verdicts (transport integrity for enforcement and release integrity for reporting/provenance), and an orthogonal inventory-drift validity dimension for telemetry joins. ### Ordered fields @@ -87,14 +87,34 @@ This axis compares `source_sha_after_read` with an optional trusted, preferably Policy: no manifest entry or unknown version key => `UNREGISTERED`, never `MISMATCH`, and never accept `attached_sha` as fallback. `UNREGISTERED` is surfaced distinctly and does **not** by itself halt a healthy session. Only `MISMATCH` escalates/halts as a proven wrong release. +### Inventory drift (execution-envelope validity axis) + +`inventory_drift ∈ { NONE, EXPECTED_VARIANT, INVALIDATING }` + +The host tool inventory is part of the execution envelope and must be preregistered before execution. Lazy discovery, auth, and MCP availability can mutate resident tool context after launch, so inventory capture is per-turn, not once per session: + +- Capture a canonical baseline host tool inventory before the first turn. +- Capture the host tool inventory immediately before **each measured turn**. +- Record the raw non-secret tool manifest, the manifest hash, and the canonicalizer + serializer versions used to produce the hash for every snapshot. +- Publish in the first preflight schema emitted before execution: the `inventory_drift` status field plus the exact before-and-after non-secret manifest diff. + +Outcomes: + +- `NONE`: manifest unchanged versus the canonical baseline manifest. +- `EXPECTED_VARIANT`: drift was declared in advance and fully captured. This becomes a separately labeled variant cell and is never silently pooled with the primary cell. +- `INVALIDATING`: manifest is missing, or drift removes a required measurement/instrumentation tool. This invalidates the affected run at per-turn granularity. + +Quarantine rule: drift that is fully captured but undeclared is quarantined as `EXPLORATORY`; it is never pooled with the primary cell. `inventory_drift` is orthogonal to `transport_integrity` and `release_integrity`: invalidating drift invalidates a run's telemetry join without implying artifact corruption. + ### Frozen transition invariants 1. `selected_agent_id` is emitted before `payload_read_status`. 2. `payload_read_status` is emitted before `prompt_attach_status`. 3. `transport_integrity` is computed by the host on source/read/assembled artifacts, outside the prompt, using `source_sha_after_read`, `attached_sha`, boundary markers, and independent source completeness proof. 4. `release_integrity` is computed separately from a trusted manifest keyed by (`agent_version`, `client_id`, `client_version`) and `source_sha_after_read`. -5. Any missing event in the ordered sequence resolves to `UNKNOWN`; absent evidence never resolves to success. -6. `selected + missing` must never collapse into `not selected`. +5. Inventory snapshots are captured before the first turn and immediately before each measured turn, with raw non-secret manifest, manifest hash, canonicalizer version, serializer version, and before/after diff emitted in the first preflight schema before execution. +6. Any missing event in the ordered sequence resolves to `UNKNOWN`; absent evidence never resolves to success. +7. `selected + missing` must never collapse into `not selected`. ### Decision function @@ -107,23 +127,25 @@ Evaluated outside the prompt: | `selected_agent_id` set + `transport_integrity=INTACT` + `release_integrity=MISMATCH` | `HALT_RELEASE_MISMATCH`; proven wrong or mutated release | | `selected_agent_id` set + `transport_integrity=PARTIAL` | `HALT_CORRUPT`; proven read/assembly transport loss dominates release status | | `selected_agent_id` set + `transport_integrity=UNKNOWN` | `UNKNOWN_HALT`; insufficient transport evidence dominates release status and is not proven corruption | +| Any measured turn with `inventory_drift=INVALIDATING` | telemetry join invalidated for that turn; this does not imply artifact corruption | +| Any measured turn with undeclared but captured drift | quarantine as `EXPLORATORY`; never pool with the primary cell | | `selected_agent_id` absent because a Squad coordinator was independently proven not selected | `SKIP`; safe non-Squad classification is based on host selection state, not canary absence | | Any missing event in the expected ordered sequence | `UNKNOWN_HALT`; absent evidence never resolves to success and is not proven corruption | ## Acceptance tests -Each fixture must assert the final outcome **and** the raw ordered event sequence: selection record -> read result -> attach result -> `transport_integrity` -> `release_integrity`. Each fixture records both verdict axes and artifact SHAs when available. +Each fixture must assert the final outcome **and** the raw ordered event sequence: selection record -> read result -> attach result -> `transport_integrity` -> `release_integrity` -> per-turn `inventory_drift`. Each fixture records both artifact-verdict axes, the inventory-drift axis, artifact SHAs when available, and the raw non-secret tool manifest/hash/canonicalizer/serializer metadata for each pre-turn snapshot. | Fixture | Setup | Required ordered evidence | Expected outcome | |---|---|---|---| -| 1. Full payload, registered | Select `--agent squad`; full `squad.agent.md` read and attached; manifest entry exists | `selected_agent_id` set -> `payload_read_status=LOADED`, `source_sha_after_read` -> `prompt_attach_status=ATTACHED_FULL`, `attached_sha` -> `transport_integrity=INTACT` -> `release_integrity=VERIFIED` | `LOADED` | +| 1. Full payload, registered | Select `--agent squad`; full `squad.agent.md` read and attached; manifest entry exists | `selected_agent_id` set -> `payload_read_status=LOADED`, `source_sha_after_read` -> `prompt_attach_status=ATTACHED_FULL`, `attached_sha` -> `transport_integrity=INTACT` -> `release_integrity=VERIFIED` -> `inventory_drift=NONE` or declared `EXPECTED_VARIANT` | `LOADED` | | 2. Head-only / oversized-prompt clip | Select `--agent squad`; source may read fully, but assembled prompt loses tail/EOF | ordered events emitted; `prompt_attach_status=ATTACHED_PARTIAL` or missing boundary; `transport_integrity=PARTIAL`; release verdict still recorded if possible | `HALT_CORRUPT` | | 3. Missing/empty Squad payload | Select `--agent squad`; suppress, empty, or fail reading coordinator payload | `selected_agent_id` set -> `payload_read_status=EMPTY` or `ABSENT` -> `prompt_attach_status=NOT_ATTACHED` -> `transport_integrity=UNKNOWN`; release usually `UNREGISTERED`/unavailable | `UNKNOWN_HALT` | | 4. Proven non-Squad | Host proves Squad coordinator was not selected | Squad `selected_agent_id` absent; non-Squad/default selection evidence emitted by host | `SKIP` | | 5. WRONG-VERSION | Full source read and full attachment, but source SHA differs from trusted manifest for (`agent_version`, `client_id`, `client_version`) | `transport_integrity=INTACT`; `release_integrity=MISMATCH`; source/attached SHA recorded | `HALT_RELEASE_MISMATCH` | | 6. LEGITIMATE UNREGISTERED-LOCAL | Healthy user-local custom agent or freshly-updated/unpublished Squad artifact with no manifest entry | `transport_integrity=INTACT`; `release_integrity=UNREGISTERED`; source/attached SHA recorded | `LOADED_WITH_REPORT`; MUST NOT false-halt | -Fixtures 5 and 6 exist to measure whether safety halts protect users without an unacceptable healthy-session false-halt rate. Telemetry must keep `HALT_CORRUPT` separate from `UNKNOWN_HALT` so missing evidence is not counted as proven corruption. Fixture 6 is especially important: registry staleness or local customization must not be converted into a release `MISMATCH` or transport failure. +Fixtures 5 and 6 exist to measure whether safety halts protect users without an unacceptable healthy-session false-halt rate. Telemetry must keep `HALT_CORRUPT` separate from `UNKNOWN_HALT` so missing evidence is not counted as proven corruption. Fixture 6 is especially important: registry staleness or local customization must not be converted into a release `MISMATCH` or transport failure. Additional inventory-drift fixtures should prove `EXPECTED_VARIANT` creates a separately labeled cell, `INVALIDATING` invalidates only affected turns, and undeclared captured drift is quarantined as `EXPLORATORY`. ## Caveman telemetry join protocol (not started) @@ -133,6 +155,7 @@ When Caveman joins, pin the static identity **before execution**: - client, client version, model, tokenizer - raw usage/cache objects - fresh-vs-continued condition +- per-turn non-secret tool inventory manifests, hashes, canonicalizer versions, serializer versions, and inventory-drift status/diff Run one fresh session and one continued session. Join telemetry only on the exact static identity above. Caveman remains **NOT-STARTED**. @@ -140,4 +163,4 @@ Run one fresh session and one continued session. Join telemetry only on the exac **In Squad's control:** install and upgrade the custom-agent file, pass/advise `--agent squad`, keep canary payload-integrity wording accurate, write/load `.mcp.json`, document Cases 1-4, and consume the future host state machine when exposed. -**Upstream Copilot CLI capability required:** the host-owned state machine above, evaluated before and outside prompt ingestion, plus the optional trusted/signed release manifest keyed by (`agent_version`, `client_id`, `client_version`). The `.agent.md` file must not be able to overwrite, suppress, or fabricate `selected_agent_id`, `payload_read_status`, `prompt_attach_status`, `source_sha_after_read`, `attached_sha`, `transport_integrity`, or `release_integrity`. +**Upstream Copilot CLI capability required:** the host-owned state machine above, evaluated before and outside prompt ingestion, plus the optional trusted/signed release manifest keyed by (`agent_version`, `client_id`, `client_version`) and per-turn non-secret tool-inventory snapshots. The `.agent.md` file must not be able to overwrite, suppress, or fabricate `selected_agent_id`, `payload_read_status`, `prompt_attach_status`, `source_sha_after_read`, `attached_sha`, `transport_integrity`, `release_integrity`, or `inventory_drift`. From 0c3c6b24ce4bd53b8aee8d3b4664e8c47ea83743 Mon Sep 17 00:00:00 2001 From: Brady Gaster Date: Mon, 27 Jul 2026 11:02:08 -0700 Subject: [PATCH 15/15] ci(size-report): pin actions to immutable SHAs Replace mutable actions/checkout@v4 and actions/setup-node@v4 with immutable commit SHAs matching the repo-wide convention: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 #v7 actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e #v6 Closes #1461 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a05b5960-2854-438d-976d-2bc01816ee70 --- .github/workflows/size-regression-report.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/size-regression-report.yml b/.github/workflows/size-regression-report.yml index 6c02a6cbd..eeef056d6 100644 --- a/.github/workflows/size-regression-report.yml +++ b/.github/workflows/size-regression-report.yml @@ -18,12 +18,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 #v7 with: fetch-depth: 0 - name: Setup Node - uses: actions/setup-node@v4 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e #v6 with: node-version: '22'