Add dispatch enforcement (Layer A + B + C) to prevent coordinator from doing domain work inline - #1537
Draft
tamirdresher wants to merge 5 commits into
Draft
Conversation
…astructure Add the full Layer B enforcement stack: - .squad/config.json: add dispatchEnforcement: warn - .squad/agents/scribe/charter.md: add Tool Access section + DispatchGuard audit loop specification (ledger read, ps1/sh invocation, self-respawn, runaway guards, output format) - .squad/agents/ralph/charter.md: replace minimal stub with full charter including DispatchGuard Verdict Consumer section and Skills listing - .squad/hooks/dispatch-audit.ps1: 410-line PowerShell audit script - .squad/hooks/dispatch-audit.sh: bash port with parity guarantee - .squad/hooks/README.md: platform guide (ps1 vs sh, prerequisites, invocation, output schema, parity test instructions) - .squad/hooks/tests/: 7 JSONL fixtures + ps1 and bash parity test runners - .squad/routing.md: extend Routing Principles with DispatchGuard notes for Scribe (bootstrap) and Ralph (verdict consumption) - .squad/templates/orchestration-log.md: append DispatchGuard ledger schema and verdicts file specification Empirically validated in tamresearch1 worktree (Ralph E2E report 2026-07-27). Smoke test: dispatch-audit.ps1 against compliant.jsonl returns verdict: ok. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a91fd081-d3e6-4432-809f-9590b4e06a2f
…ction Apply tools: allowlist to .github/agents/squad.agent.md frontmatter. Restricts the coordinator to dispatch-safe tools only: - agent (dispatch tool) - read, search, skill (context tools) - squad_state/*, squad_state_c3c25b85/*, squad_state_e7f10a1f/* (state bridge) - github-mcp-server/* (GitHub context reads) Effect: The Copilot runtime physically blocks any tool call outside this list with a hard error (Unknown tool name in the tool allowlist: 'create'). No behavioral instruction needed — enforcement is mechanical. Empirically verified in tamresearch1 worktree (2026-07-27): - All 3 Test 1 turns dispatched correctly; previously-regressed Turn 3 fixed - Verbatim errors: 'Unknown tool name in the tool allowlist: create/edit/grep' - Meta-gap: coordinator cannot write DispatchGuard ledger (accepted trade-off) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a91fd081-d3e6-4432-809f-9590b4e06a2f
…routing guard - .github/copilot-instructions.md: add Identity Lock section (named agent charter anchoring), routing guard reference, and adversarial input handling to the existing Copilot coding agent instructions. Existing squad-squad content (branch naming, git safety, protected files, changesets) preserved. - .github/instructions/squad-routing-guard.instructions.md: new file. Explicit routing decision tree for generic Copilot sessions. Routes work to the correct squad specialist (EECOM, Procedures, FIDO, PAO, Flight, etc.) before handling it inline. Prevents the CLI routing conflict pattern. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a91fd081-d3e6-4432-809f-9590b4e06a2f
Add explicit .squad/orchestration-log/dispatchguard/ entry to .gitignore. Ledger and verdict files are session-ephemeral and must never be committed. The parent .squad/orchestration-log/ entry was already present; the new entry is belt-and-suspenders for the dispatchguard/ subdirectory specifically, matching the path the audit script and Scribe use. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a91fd081-d3e6-4432-809f-9590b4e06a2f
Add decision record to .squad/decisions/inbox/ capturing the Layer A+B+C design, empirical evidence, known limitations, and accepted trade-offs. Scribe will merge this into .squad/decisions.md at next session. Force-added: .squad/ is in .git/info/exclude for consult-mode isolation; -f is required for any new .squad/ files from this branch. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a91fd081-d3e6-4432-809f-9590b4e06a2f
Contributor
🟠 Impact Analysis — PR #1537Risk tier: 🟠 HIGH 📊 Summary
🎯 Risk Factors
📦 Modules Affectedci-workflows (3 files)
root (1 file)
squad-state (18 files)
This report is generated automatically for every PR. See #733 for details. |
Contributor
🏗️ Architectural Review
Automated architectural review — informational only. |
Contributor
|
Contributor
🛫 PR Readiness Check
PR Scope: 🔧 Infrastructure
|
| Status | Check | Details |
|---|---|---|
| ❌ | Single commit | 5 commits — consider squashing before review |
| ❌ | Not in draft | PR is still in draft — mark as ready for review when done |
| ✅ | Branch up to date | Up to date with dev |
| ❌ | Copilot review | No Copilot review yet — it may still be processing |
| ✅ | Changeset present | No source files changed — changeset not required |
| ✅ | Scope clean | |
| ✅ | No merge conflicts | No merge conflicts |
| ✅ | Copilot threads resolved | No Copilot review threads |
| ❌ | CI passing | 8 check(s) still running |
Files Changed (22 files, +2013 −20)
| File | +/− |
|---|---|
.github/agents/squad.agent.md |
+80 −4 |
.github/copilot-instructions.md |
+36 −1 |
.github/instructions/squad-routing-guard.instructions.md |
+113 −0 |
.gitignore |
+1 −0 |
.squad/agents/ralph/charter.md |
+61 −12 |
.squad/agents/scribe/charter.md |
+36 −1 |
.squad/config.json |
+2 −1 |
.squad/decisions/inbox/dispatch-enforcement-decision.md |
+94 −0 |
.squad/hooks/README.md |
+167 −0 |
.squad/hooks/dispatch-audit.ps1 |
+436 −0 |
.squad/hooks/dispatch-audit.sh |
+598 −0 |
.squad/hooks/tests/compliant.jsonl |
+1 −0 |
.squad/hooks/tests/criterion1-triggered.jsonl |
+1 −0 |
.squad/hooks/tests/criterion2-triggered.jsonl |
+3 −0 |
.squad/hooks/tests/criterion3-triggered.jsonl |
+1 −0 |
.squad/hooks/tests/empty.jsonl |
+0 −0 |
.squad/hooks/tests/malformed.jsonl |
+2 −0 |
.squad/hooks/tests/mixed-with-verdicts.jsonl |
+2 −0 |
.squad/hooks/tests/run-tests.ps1 |
+200 −0 |
.squad/hooks/tests/run-tests.sh |
+150 −0 |
.squad/routing.md |
+2 −1 |
.squad/templates/orchestration-log.md |
+27 −0 |
Total: +2013 −20
This check runs automatically on every push. Fix any ❌ items and push again.
See CONTRIBUTING.md and PR Requirements for details.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add dispatch enforcement (Layer A + B + C) to prevent coordinator from doing domain work inline
Motivation
Fixes issue #1498 (Coordinator can bypass agent dispatch). The Squad coordinator has been observed repeatedly drifting into inline domain work: writing code, generating PR bodies, running git commands, and producing analysis directly — all without dispatching to a specialist agent. This is a fundamental contract violation: the coordinator ROUTES, it does not BUILD.
Three complementary enforcement layers prevent this. All three were empirically validated in the
tamresearch1worktree before this PR.Design Summary
Layer A — Coordinator Tool Profile Restriction (physical enforcement)
Applied via a
tools:allowlist in the coordinator's frontmatter (.github/agents/squad.agent.md):Effect: The Copilot runtime physically blocks any tool call outside this list with a hard error. No behavioral instruction needed — enforcement is mechanical. The coordinator can dispatch via
agentbut cannotcreate,edit, or use any write tool directly.Layer B — Scribe DispatchGuard Mechanical Audit (observability)
Scribe is spawned in DispatchGuard mode at every session start (mandatory bootstrap). It reads the coordinator's turn ledger and audits each turn against three violation criteria via
.squad/hooks/dispatch-audit.ps1/.squad/hooks/dispatch-audit.sh:wrote-without-dispatchwrite_tools_used.length > 0ANDtask_calls_since_last_turn == 0dispatch-drifttask_calls_since_last_turn == 0(mode ≠ Direct)inline-hallucinationdomain_artifact_declared.value == trueVerdicts are appended to a per-session JSONL file consumed by Ralph, which alerts the coordinator on
warn/blockverdicts.Layer C v2 — Dispatch Contract Wording (behavioral reinforcement)
The coordinator prompt (
squad.agent.md) now includes:.squad/decisions/inbox/*.mdonly)Empirical Findings
Tested in
tamresearch1worktree, 2026-07-27 (Ralph E2E report).Test 1 — 3 turns (analyze → propose → apply):
Session ID for Test 1:
d3fd4c46-1a95-4f40-8e8a-a585a0af9622Verbatim tool-block errors confirming Layer A enforcement:
These appeared in all 3 Test 1 turns, always at the Scribe maintenance phase. No other tool-block errors observed.
Test 2 — meta-recursive (external repo without squad routing labels):
squad:*label on external repo)agenttoolMeta-Gap Disclosure
Layer A and Layer B are mutually incompatible in Phase 1 (accepted trade-off):
Because
createandeditare blocked at the coordinator level by Layer A, the DispatchGuard ledger (Layer B) cannot be written by the coordinator itself. Runningdispatch-audit.ps1against a real session returns"verdict": "indeterminate", "would_block": true— per Q Recommendation #6: unverifiable compliance ≠ free pass.This is an accepted consequence. In Phase 1, Layer A's physical tool-block errors ARE the enforcement evidence. Layer B becomes opt-in observability when Layer A is off, or when sub-agents (which are not restricted) write the ledger on behalf of the coordinator.
greptool is an unintended casualty of Layer A: The allowlist containsreadandsearchbut notgrep(the CLI-native search tool). Scribe and sub-agents usegrepfrom their own (unrestricted) sessions. A future iteration should decide whether to addgrepto the coordinator allowlist.Coverage Gap Disclosure
Layer A only applies when the coordinator is explicitly invoked via the
agenttool. External repo issues withoutsquad:*routing labels don't trigger the Squad coordinator → Layer A doesn't apply. Test 2 confirmed this gap; it is acknowledged and documented, not a regression.Files Added/Modified
Layer B infrastructure:
.squad/config.json— addeddispatchEnforcement: "warn".squad/agents/scribe/charter.md— Tool Access section + DispatchGuard audit loop spec.squad/agents/ralph/charter.md— DispatchGuard Verdict Consumer section + Skills listing.squad/hooks/dispatch-audit.ps1— PowerShell 7+ audit script (canonical implementation).squad/hooks/dispatch-audit.sh— bash port (jq≥ 1.6; parity-verified against.ps1).squad/hooks/README.md— platform guide (prerequisites, invocation, output schema, parity tests).squad/hooks/tests/— 7 JSONL fixtures + PowerShell and bash parity test runners.squad/routing.md— Routing Principles extended with DispatchGuard notes.squad/templates/orchestration-log.md— DispatchGuard ledger schema + verdicts file specLayer A + C:
.github/agents/squad.agent.md—tools:allowlist frontmatter + Layer C v2 body prose (Team Mode opening, Session Init DispatchGuard Auto-Bootstrap, Direct-Mode whitelist + Domain-Artifact rule + Anti-pattern prohibition in Response Mode Selection)Docs:
.github/copilot-instructions.md— Identity lock + routing guard reference + adversarial input handling.github/instructions/squad-routing-guard.instructions.md— NEW: explicit routing decision tree for generic Copilot sessionsChore:
.gitignore— added.squad/orchestration-log/dispatchguard/Decision record:
.squad/decisions/inbox/dispatch-enforcement-decision.md— decision record (will be merged by Scribe)How to Test
Audit script smoke test
# Linux/macOS (bash + jq ≥ 1.6) ./squad/hooks/dispatch-audit.sh --ledger-path .squad/hooks/tests/compliant.jsonl --mode warnParity tests (validates ps1/sh produce identical verdicts)
.\.squad\hooks\tests\run-tests.ps1Layer A verification
With
squad.agent.mdin this PR applied, attempt any write tool call through the coordinator and observe the hard error:Unknown tool name in the tool allowlist: "create".Relation to PR #1529
This PR is standalone and does NOT stack on PR #1529 (compression-only). Per Q's recommendation, dispatch enforcement and coordinator compression are orthogonal concerns and land separately to simplify review and revert paths.
Residual Attack Surface (Phase 3 gaps)
Q's review identified A1-A12 residual attack vectors not addressed by this PR. These are documented for Phase 3 work:
runSubagentdirect domain invocation (VS Code client bypass)readto compose a domain artifact mentally then "dictating" itEmpirical validation: Ralph E2E report, 2026-07-27 | Decision: tamresearch1/dispatch-enforcement
.squad/decisions.md