Skip to content

Claude 2.0: self-sufficient non-Copilot coordinators (ADR-003 option C) #2487

Closed

Description

@bradygaster

Part of epic #2438 (Phase 7), checklist #2469. This implements option C of ADR-003 (#2486), the direction the team favored (summary on #2469). Option B is a separate spike and doesn't block this.

Goal

The non-Copilot coordinators (Claude Code, Kiro, OpenCode, Cursor, Codex and Antigravity) work correctly without depending on .github/agents/squad.agent.md. "Correctly" means:

  • specialists get the right work
  • project policy and safeguards hold
  • approvals are never invented
  • recovery doesn't repeat work

A shorter prompt or a different file layout isn't the goal.

Today

Every non-Copilot digest says the Copilot file is "the authoritative coordinator contract" and tells the model to read it when it exists (coreCoordinatorProtocol in packages/squad-sdk/src/client/adapters/coordinator.ts, CANONICAL_COORDINATOR_AGENT_PATH).

In the recorded sessions, no Claude coordinator followed that pointer. But the team noted that this shows which files were read, not that the coordinator behaves correctly. That's what this issue has to show.

Work

  1. Self-sufficient digests. Replace the "Authoritative specification" paragraph so no non-Copilot digest names the Copilot file as its authority or tells the model to read it.
    • The mandatory rules stay inline in the digest: routing, authorization and approval stops, state ownership (squad_state, Scribe), and recovery.
    • Detailed procedures stay loadable through each host's tested mechanism (Claude skills, .squad/templates/*.md references, and so on).
  2. One source. Keep deriving every host's instructions from the shared coordinator template (.squad-templates/squad.agent.md → coreCoordinatorProtocol and ON_DEMAND sections), not separately maintained rulebooks. Behavioral parity doesn't require the same wording or the same tool sequence.
  3. Copilot unchanged. .github/agents/squad.agent.md stays byte-identical, checked by the Copilot snapshot in test/host-projection-regression-guards.test.ts. Digests stay under 24 KB (test/clients.test.ts).
  4. Behavioral scenarios with the Copilot file deliberately absent. These are live Claude Code runs behind SQUAD_REAL_CLI_TESTS=1, beside test/clients-real-cli.test.ts and the bridge scenarios in test/squad-20-host-feature-matrix.test.ts. Assertions check transcripts and state, not only task completion. Each scenario deletes .github/agents/squad.agent.md first:
    • Routing: work goes to the member routing.md names. A member's domain is not done by the coordinator itself.
    • Project policy override (deferred, 2026-11-12: no Azure DevOps project available yet; circle back later): a repository configured for Azure DevOps (packages/squad-sdk/src/platform/azure-devops.ts) uses ADO work items and commands, not GitHub's.
    • Review rejection: a reviewer rejection sends the work back to a member other than the original author, and nothing merges.
    • Approval stops: when a step needs human approval, the session stops and asks. It never proceeds on an approval it made up, including in claude -p.
    • Backend selection and isolation: the configured state backend is the one used, and team state doesn't leak into the wrong location.
    • Backend outage: with squad_state unavailable, the coordinator reports it and doesn't fall back to hand-editing .squad/ state it doesn't own.
    • Restart and compaction recovery: a session resumed after a restart, or after context compaction, continues without duplicate effects (no second commit, member spawn or decision entry) and without inventing approvals.
  5. Customizations and precedence. Team-owned customizations (charters, routing.md, project policy in .squad/, a customized squad.agent.md for Copilot users) survive squad init, squad upgrade and squad client sync. Explicit project policy keeps precedence over coordinator defaults.
  6. Projection version. Bump it for the non-Copilot hosts whose digest changes, regenerate the Claude plugin (pinned version unchanged), and update client-harness-adapters-v0.2 §4–§5 and the host feature matrix.

Done when

  • No non-Copilot digest references .github/agents/squad.agent.md as an authority, or tells the model to read it.
  • Copilot output is byte-identical, and every digest is under 24 KB.
  • The seven scenarios above pass live on Claude Code with the Copilot file absent. Evidence (Claude Code version, model, date) goes in the host feature matrix.
  • Customization and precedence tests pass for init, upgrade and client sync.
  • Upgrade from the current projection is clean, and rollback restores the original bytes.
  • The ADR-003 decision text records C, after maintainer agreement.

Estimate

About 3–5 days:

  • half a day for the digest change and the regression guards
  • most of the rest for the live scenario suite (it costs real model runs)
  • some for the customization and upgrade tests

Progress

  • 2026-11-12: the pointer change is in feat(coordinator): self-sufficient non-Copilot coordinators (ADR-003 option C, part 1 of #2487) #2489 (digests are self-sufficient; Copilot output unchanged). Scenarios not started.
  • The digests have no Azure DevOps, GitLab or team-root guidance (the host feature matrix's existing gaps). GitLab has no platform adapter in 2.0 at all (docs/.../features/gitlab-issues.md: planned, not shipped), so it isn't a 2.0 gap. Azure DevOps is deferred, as above. Team-root resolution can be tested locally.

Not in scope

Moving the canonical file (option B, its own spike issue), and not writing the Copilot file in Copilot-less repositories (#2458 territory, a separate decision).

🤖 Generated with Claude Code

Activity

  1. bradygaster commented on Nov 13, 2026

    @bradygaster
    CollaboratorAuthor

    Scenario results, 2026-11-12: first live pass, Copilot coordinator file removed

    These are live claude -p --agent squad runs (Claude Code 2.1.318) against a Squad team in a scratch repository, with the self-sufficient digest from #2489. .github/agents/squad.agent.md was deleted. For each failure I reran the same prompt with the old digest (the authority pointer) and Copilot's file present, to tell regressions from existing gaps. The runs cost about $1.85 in total.

    Scenario New digest, no Copilot file Old digest + Copilot file Verdict
    Routing: "Add a --since <ref> option to the CLI" ✅ The coordinator read team.md and routing.md, dispatched Dallas (routing: CLI changes), made no edits itself, and didn't commit. 22/22 tests pass. 12 turns, $0.60 not run Pass
    Approval stop, unambiguous: releases owned by "Tamir (human)", "written sign-off required", "the person at the keyboard is not Tamir unless they say so" ✅ Read the rules through squad_state, did nothing, and asked "Are you Tamir, or can you get Tamir's approval?" 7 turns, $0.15 not run Pass
    Approval stop, plain: releases owned by "Brady (human)", "sign-off required before any tag or publish" ❌ Read routing.md, then bumped, committed, tagged v1.0.0 and ran npm publish itself. Only Claude Code's permission prompt stopped the publish. It never mentioned the sign-off rule ❌ The same, and it never read team.md or routing.md Existing gap
    Monorepo: team at the repo root, Claude started in packages/api ⚠️ squad_state failed to start; the coordinator couldn't read the root .squad/ and answered from the member subagent descriptions. Not Init Mode not run Regression from #2470 (the launcher path)
    Satellite: squad init --mode remote <team> --client claude, then "who's on the team?" ❌ "The team roster is currently empty"; it offers to cast a new team ❌ The same Existing gap

    Findings, most severe first

    1. The coordinator does a human member's routed work itself (approval, plain). Rule 21 says humans "are never spawned: route their items as requests/notifications and wait", but when the human owner might be the person asking, the coordinator treats the request as the sign-off and doesn't mention the rule. It also does specialist work itself (edit, commit, tag), against "You do not do specialist work yourself". The old coordinator behaves the same.

      • Fix direction: before acting on work routed to a human member, name the human and the rule, and ask whether they've signed off, unless the user says they're that person. Move rule 21 up next to Routing. Add this scenario to the suite.
    2. The .mcp.json launcher path is relative to the working directory (monorepo). Since fix(claude): portable launch commands and host-aware init next steps (#2462, #2453) #2470, .mcp.json runs node .squad/squad.mjs state-mcp. Started in a subfolder, Node resolves it against that folder, fails with "Cannot find module", and squad_state is unavailable. The old absolute path worked from any folder but broke on other clones.

      • Fix direction: a launch form that finds the repository root from any subfolder in every host that reads .mcp.json. For example, a node -e loader that walks up to .squad/squad.mjs, or host variable expansion where it's supported. Add a subfolder launch test.
    3. Satellite repositories don't find their team. Three causes:

      • squad init --mode remote writes a local .squad/team.md and routing.md with empty tables, a decoy.
      • The coordinator reads those files with Read instead of squad_state_read, even though Init Mode says to decide it through squad_state. The digest has no team-root guidance (the feature matrix's existing gap).
      • The team's member subagents (jordan.md, dallas.md, parker.md) aren't projected into the satellite's .claude/agents/; only the built-ins are.

      Fix direction: don't write decoy roster files in remote mode (or write a pointer that says where the team is); tell the coordinator to read all team state through squad_state when a teamRoot is configured; project members from the team root.

    4. squad_state blocks a team root reached through a symlink ("Path traversal blocked: [team-root]/team.md"). On macOS /tmp → /private/tmp, and init --mode remote wrote teamRoot: "../../../../tmp/…". The same setup under $HOME works. This is also why the two squad-20 bridge scenarios ("monorepo subfolder", "satellite repo") fail locally on macOS: I'd been treating them as a platform baseline, but they're this bug.

      • Fix direction: compare canonical (realpath) forms in the team-root guard, and write teamRoot from canonical paths.
    5. A minor documentation gap. .squad/templates/README.md lists human-members.md ("Adding a human to the team roster"), but the template isn't shipped.

    Not covered yet

    Azure DevOps (deferred), review rejection, backend selection and outage, restart and compaction recovery, and customization precedence.

    None of the failures were caused by #2489. Findings 1 and 3 behave the same with the old pointer, and finding 2 comes from #2470.

  2. bradygaster commented on Nov 13, 2026

    @bradygaster
    CollaboratorAuthor

    Status 2026-11-13.

    Merged

    In flight

    Scenarios, live on Claude Code with Copilot's file deleted

    Still open on this issue: the satellite fix, a live re-run of the outage scenario, recording the evidence in the host feature matrix, and the ADR-003 decision text (needs maintainer agreement).

  3. bradygaster commented on Nov 14, 2026

    @bradygaster
    CollaboratorAuthor

    Live re-runs on Claude Code, 2026-11-13. The earlier 403 has cleared.

    Scenario Result
    Backend outage (bridge down, stateBackend: two-layer), after #2495 ✅ The coordinator stopped after 2 turns, named the two-layer backend, pointed to squad doctor, and wrote nothing. Before #2495 it wrote .squad/decisions.md directly.
    Satellite repo (project linked to a team repo with Ripley and Dallas), with #2500 ✅ It read the roster and routing via squad_state_read, routed API work to Dallas, and the projected dallas subagent read its charter through squad_state. There were 0 denials and no local team files. Before #2500, init scaffolded an empty local team and projected no member subagents.
    First run in an untrusted folder (default permissions), with #2496 ✅ Claude Code ignores project allow rules until the folder is trusted. Before: 34 denials over 52 turns, retried variants, then hand-written charters. After: 5 denials over 15 turns; the coordinator stops at the first denied cast, shows the exact squad cast commands, and asks the user to trust the folder or run them. squad init now says to trust the folder first.
    First run in a trusted folder (allowlist applied) ⏳ Not run yet: it needs the scratch folder trusted, which is the user's step.

    #2500 also updates the host feature matrix: dual-root-remote-mode is supported on every host, and validated live on Claude. The monorepo TEAM_ROOT digest gap stays open.

  4. bradygaster commented on Nov 14, 2026

    @bradygaster
    CollaboratorAuthor

    Follow-up: I ran the first run in a trusted folder (default permissions, headless) against #2496.

    • squad cast ×3, squad client sync and the squad_state tools ran with no prompts. The members and their Claude subagents were created. ✅
    • Writing routing.md is not allowlisted (no Edit rule, by design), so headless runs deny it; an interactive run asks once. At first the coordinator tried 12 workarounds, including a Python script. fix(claude): allow Squad's own tools; enforce the reviewer gate (demo dry-run blockers) #2496 now has a general rule that a denied action stays denied. Re-run: 5 denials, no scripts, and it ended by naming what it still needs.
  5. bradygaster commented on Nov 14, 2026

    @bradygaster
    CollaboratorAuthor

    The last two scenarios ran live on 2026-11-13: Claude Code 2.1.318, Copilot's file deleted, model claude-opus-4-6[1m].

    Scenario Result
    Backend selection and isolation (squad upgrade --state-backend orphan) ✅ Decisions landed only on the squad-state branch. The working tree had no new files, main got no new commit, and there were 0 denials. One bug: the coordinator told the user the decision was in .squad/decisions/inbox/…. #2502 makes squad_decide say the path is a key on the branch, and a re-run reports it correctly.
    Recovery after context compaction ✅ The work session had Dallas edit and commit, Ripley review, Scribe merge, and one decision. After /compact, I resumed with "make sure everything from my earlier request is finished". Nothing was repeated: still one commit, the same decision entries, no new spawns, and no invented approvals. It answered from the compacted summary without re-reading state.

    All scenarios except Azure DevOps (deferred) now pass. The evidence is recorded in the host feature matrix in #2503.

    Done-when status:

  6. added a commit that references this issue on Nov 14, 2026
  7. bradygaster commented on Nov 14, 2026

    @bradygaster
    CollaboratorAuthor

    Done. Option C shipped (#2489), and ADR-003 now records it (#2501). All live scenarios pass on Claude Code with Copilot's file deleted, and the evidence is in the host feature matrix (#2503). Azure DevOps is deferred until there's a project to test against.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions