Skip to content

Coordinator TEAM_ROOT has inconsistent semantics between State & Team Root Resolution and Worktree Awareness #1163

Open

Description

@ralarcon

Summary

PR #1132 (merged 2026-05-19) introduced the State & Team Root Resolution and Mode-Switch Check sections to squad.agent.md to fix coordinator awareness of .squad/config.json.teamRoot (closes #1127). The fix correctly resolves the satellite/external-state cases, but it introduces three inconsistencies with the pre-existing Worktree Awareness section that can cause false Init Mode entry in legitimate worktree scenarios and ambiguous behavior in others.

These findings were surfaced during downstream review of the same patch backported into a private dual-root pilot (Inditex). All three are in the upstream prompt as-merged.

Affected file

.github/agents/squad.agent.md (also .squad/templates/squad.agent.md)

Finding 1 — TEAM_ROOT has two contradictory definitions

Where:

  • State & Team Root Resolution step 4 (default branch): "team root is the local .squad/ directory" → TEAM_ROOT = <repo>/.squad/.
  • Worktree Awareness step 2 (later in the file): "Team root = current worktree root" → TEAM_ROOT = <repo>/.

The same identifier resolves to two paths one level apart depending on which section is consulted. The Mode-Switch Check then probes {TEAM_ROOT}/team.md, which:

  • Under section A semantics: looks at .squad/team.md ✅
  • Under section B semantics: looks at <repo>/team.md ❌ → triggers Init Mode incorrectly.

Impact: In sessions where the LLM resolves the team root via Worktree Awareness (which Team Mode explicitly tells it to do "on every session start") before the Mode-Switch is consulted, a fully configured repo enters Init Mode. The risk is highest in worktrees without .squad/ committed in the branch — the main scenario Worktree Awareness was originally written to support — because there the State & Team Root step 4 default fails (no local .squad/) and the only correct fallback (discover the main checkout) lives in the section that arrives too late.

Suggested fix: Either

  • Make Mode-Switch Check probe {TEAM_ROOT}/.squad/team.md and define TEAM_ROOT as the repo/worktree root throughout (preferred — aligns with charter/history path conventions used elsewhere); or
  • Introduce a separate STATE_ROOT that always points at the .squad/ directory, and reserve TEAM_ROOT for the repo root.

Finding 2 — teamRoot path semantics over-restrictive

Where: State & Team Root Resolution step 3.

Current wording: "The team root is the value of teamRoot (absolute path to another .squad/ directory)."

Real-world usage: In our dual-root pilot (and the natural setup for linked repos in the same workspace) teamRoot is a relative path (../cac-vniotsquad) pointing at the repo root that contains its own .squad/ — not at a .squad/ directory directly. The current wording marks portable relative paths as out-of-spec and is ambiguous about which level the path targets.

The note in parentheses "(or {teamRoot}/team.md if teamRoot already points inside .squad/)" acknowledges both shapes exist but does not document how to detect which. Tooling (squad link, squad init --mode remote) accepts both forms.

Suggested fix: Replace step 3 wording with:

  • teamRoot MAY be absolute or relative (relative resolves against the directory containing config.json).
  • teamRoot MAY point at a repo root (containing .squad/) OR directly at a .squad/ directory.
  • Resolution: if {teamRoot}/.squad/team.md exists, use it; else if {teamRoot}/team.md exists, use it; else error.

Finding 3 — Worktree Awareness step 0 lookup is ambiguous

Where: Worktree Awareness step 0, "Check config.json overrides first — read .squad/config.json in the current directory (or at the git root)".

Problem: Step 1 is where git rev-parse --show-toplevel is invoked, so step 0 cannot yet rely on the git root having been resolved. From a subdirectory the lookup is undefined: do we walk up looking for .squad/config.json? Try CWD first then fall back? Try git root first?

Suggested fix: Specify a deterministic order, e.g.:

  1. Run git rev-parse --show-toplevel first (move step 1 ahead of step 0, or duplicate the call).
  2. Check {gitRoot}/.squad/config.json.
  3. If absent, optionally check ancestors of CWD up to gitRoot (only if multi-.squad/ repos are supported — likely not).

Reproduction

The scenarios where this matters in practice:

Setup State & Team Root default applies? Mode-Switch outcome
Normal repo, .squad/ at root, no worktree Yes — step 4 returns .squad/ dir ✅ Passes
Repo with .squad/config.json.teamRoot set (satellite) Step 3 hit — STOP ✅ Passes
Worktree of branch where .squad/ IS committed Step 4 returns .squad/, but Worktree Awareness later reassigns ✅ Passes (latent inconsistency)
Worktree of branch where .squad/ is NOT committed Step 4 fails (no .squad/), Worktree Awareness fallback to main checkout would fix but runs too late ❌ False Init Mode

Why we're filing rather than patching downstream

We backported #1132 into our dual-root pilot verbatim (per our policy: "verbatim mirror, replace on upgrade to v0.9.5+"). Our specific configuration uses teamRoot (step 3, not affected). We're shipping the backport as-is and would prefer the fix to land upstream so we can drop our local mirror cleanly on the next upgrade.

Happy to open a PR if helpful — let us know which fix shape you'd prefer for finding 1 (collapse TEAM_ROOT to repo root, or split into TEAM_ROOT + STATE_ROOT).

Cross-references: #1127, #1132, #521, #525, #643.

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

    triagedIssue has been triagedwave:2-soonWave 2 — next 2-4 weeks

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions