Skip to content

PRD: Monorepo subfolder support — full story #974

Open

Description

@tamirdresher

Problem Statement

When users run squad init from a subfolder of a monorepo, the old behavior ran git init — creating a broken nested .git/ directory inside the subfolder. This corrupted the parent repo's view of the subfolder and caused cascading problems with workflows, upgrades, and agent resolution.

PR #944 fixes the init path (phase 1), but the full monorepo story has gaps:

  • Upgrade doesn't know about monorepo layouts — it re-stamps files at CWD instead of git root
  • Workflows conflict when multiple squads coexist in one repo
  • Shared agent file works today but has limitations if squads need different coordinator policies
  • Migration path is manual for users who already hit the bug

Related: #939 (original bug report), #944 (phase 1 fix)


Platform Constraint

Copilot custom agents (.github/agents/*.md) only resolve from the git root. This is a Copilot platform constraint — Squad cannot change it.

Implications:

Constraint Consequence
One shared squad.agent.md at git root All squads share the same coordinator template
Coordinator is stateless It reads team state from CWD (.squad/ directory)
CWD determines which squad is active cd team-alpha && copilot loads alpha's squad; cd team-beta && copilot loads beta's squad

This is actually a clean design — the coordinator is a router, and .squad/ is the state store. The constraint becomes a feature.


Phase 1 — Bug Fix ✅

Status: Done — PR #944

What shipped

  • squad init no longer runs git init in subfolders
  • Agent file (.github/agents/squad.agent.md) placed at git root, not CWD
  • .squad/ directory placed in the subfolder (CWD)
  • Workflows skipped in monorepo mode (with console warning explaining why)
  • CWD-first .squad/ resolution baked into the coordinator template

Validation

  • 20 unit tests passing
  • 7 E2E scenarios validated:
    1. Standalone repo (baseline, no regression)
    2. Monorepo subfolder — first squad
    3. Monorepo subfolder — second squad alongside first
    4. No-git directory (error path)
    5. Git root init (same as standalone)
    6. Re-init in same subfolder (idempotent)
    7. Agent file content verification (CWD resolution present)

Phase 2 — Upgrade Awareness

Status: Not started

Problem

squad upgrade currently assumes CWD == git root. In a monorepo, it re-stamps the agent file at the wrong location and doesn't detect pre-fix damage (nested .git/ directories).

Requirements

# Requirement Details
2.1 Monorepo detection in upgrade Detect when git root ≠ CWD; apply same path-splitting logic as init
2.2 Agent file re-stamp at git root upgrade writes updated squad.agent.md to <git-root>/.github/agents/, not CWD
2.3 Nested .git/ detection Warn if CWD contains a .git/ directory that isn't the repo root (pre-fix damage)
2.4 Migration helper squad migrate-monorepo command that automates the manual migration steps (see Migration Path below)

Acceptance criteria

  • squad upgrade from a monorepo subfolder updates agent file at git root
  • squad upgrade from a monorepo subfolder updates .squad/ in CWD
  • Warning emitted when nested .git/ detected
  • squad migrate-monorepo moves agent file, removes nested .git/, preserves .squad/
  • Existing standalone upgrade behavior is unchanged (no regression)

Phase 3 — Multi-Squad Workflows

Status: Not started — needs design spike

Problem

GitHub Actions only reads workflow files from <repo-root>/.github/workflows/. When multiple squads coexist in one monorepo, they can't each have independent heartbeat/triage workflows without conflicts.

Options to evaluate

Option Approach Pros Cons
A. Discovery workflow Single shared workflow discovers all .squad/ directories and iterates One workflow to maintain; scales automatically Complex workflow logic; all squads share schedule
B. Path-filtered workflows Heartbeat for team-alpha/**, separate for team-beta/** Clean separation; familiar pattern squad init must generate path-aware workflows; manual if squads added later
C. Per-squad naming squad-heartbeat-alpha.yml, squad-heartbeat-beta.yml Explicit; easy to debug squad init must namespace; doesn't scale to many squads

Additional considerations

  • Squad labels: Need namespacing? E.g., squad:alpha:member vs squad:beta:member — or do squad labels already scope by repo convention?
  • Workflow skip in phase 1: Currently squad init skips workflows entirely in monorepo mode and prints a warning. Phase 3 should remove this limitation.
  • Cron schedule: Should all squads share one heartbeat cadence, or can they differ?

Acceptance criteria

  • Design doc produced (pick one option or hybrid)
  • squad init in monorepo generates appropriate workflow(s)
  • Multiple squads in one repo can run independent heartbeats without conflict
  • Labels are properly namespaced (if needed)

Phase 4 — Per-Squad Coordinator Customization (If Needed)

Status: Future — may not be needed

Problem

Today one shared squad.agent.md means the coordinator behavior is identical for all squads. This works when squads only differ in team state (roster, decisions, history). It breaks if squads need:

  • Different casting policies (e.g., alpha uses strict casting, beta uses relaxed)
  • Different ceremony configs (e.g., alpha has weekly retros, beta doesn't)
  • Different routing rules or tool restrictions

Possible solution

A squad.config.json (or similar) in each subfolder's .squad/ directory with coordinator overrides:

{
  "casting": { "policy": "strict", "maxRetries": 2 },
  "ceremonies": { "retro": "weekly", "standup": "daily" },
  "routing": { "securityIssues": "worf", "docs": "seven" }
}

The coordinator template would read this config from CWD and adjust behavior accordingly.

Non-goal

Per-squad agent files (e.g., squad-alpha.agent.md) — blocked by Copilot platform constraint. Agent files resolve from .github/agents/ by name, not by CWD.


Non-Goals

Non-goal Reason
Named agent files per squad (squad-alpha.agent.md) Copilot doesn't support CWD-based agent selection; agent name is global
Cross-squad agent sharing Squads are independent teams, not a federation
Monorepo-wide orchestration No "meta-coordinator" that spans all squads
Polyrepo support Out of scope; Squad assumes one git repo per init

Migration Path

For users who already ran squad init in a monorepo subfolder before the fix in PR #944:

Manual steps (available now)

# 1. Navigate to the subfolder where squad was incorrectly initialized
cd my-monorepo/team-alpha

# 2. Remove the broken nested .git/
rm -rf .git

# 3. Move the agent file to git root
mkdir -p ../..github/agents
mv .github/agents/squad.agent.md ../../.github/agents/squad.agent.md
rm -rf .github  # if empty after move

# 4. Keep .squad/ in place — it's already correct
ls .squad/  # roster.md, decisions.md, etc. should be here

# 5. Verify
git status  # should show changes at repo root, not nested repo

Automated (phase 2)

cd my-monorepo/team-alpha
squad upgrade  # or: squad migrate-monorepo

Testing Strategy

Layer What to test Status
Unit Monorepo detection, git-root resolution, path normalization, workflow skip logic ✅ Phase 1
E2E Standalone, monorepo subfolder, second squad, no-git, git-root, re-init, agent content ✅ Phase 1
E2E Upgrade in monorepo, migrate-monorepo, nested .git/ detection ⬜ Phase 2
E2E Multi-squad workflow generation, label namespacing ⬜ Phase 3
Manual Clone PR branch → build → run squad init from various locations ✅ Phase 1

Links

Activity

  1. added
    type:epicParent issue that decomposes into sub-issues
    prdProduct Requirements Document
    enhancementNew feature or improvement
    on Apr 14, 2026
  2. added
    squadSquad triage inbox — Lead will assign to a member
    on Apr 17, 2026
  3. github-actions commented on Sep 10, 2026

    @github-actions
    Contributor

    🔄 Ralph — Auto-Triage

    Assigned to: Booster
    Reason: Matched routing keyword(s): cd, GitHub Actions
    Source: routing-rule

    Ralph auto-triaged this issue using routing rules.
    To reassign, swap the squad:* label.

  4. github-actions commented on Sep 10, 2026

    @github-actions
    Contributor

    🔄 Ralph — Auto-Triage

    Assigned to: Booster
    Reason: Matched routing keyword(s): cd, GitHub Actions
    Source: routing-rule

    Ralph auto-triaged this issue using routing rules.
    To reassign, swap the squad:* label.

  5. github-actions commented on Sep 11, 2026

    @github-actions
    Contributor

    🔄 Ralph — Auto-Triage

    Assigned to: Booster
    Reason: Matched routing keyword(s): cd, GitHub Actions
    Source: routing-rule

    Ralph auto-triaged this issue using routing rules.
    To reassign, swap the squad:* label.

  6. github-actions commented on Sep 11, 2026

    @github-actions
    Contributor

    🔄 Ralph — Auto-Triage

    Assigned to: Booster
    Reason: Matched routing keyword(s): cd, GitHub Actions
    Source: routing-rule

    Ralph auto-triaged this issue using routing rules.
    To reassign, swap the squad:* label.

  7. github-actions commented on Sep 11, 2026

    @github-actions
    Contributor

    🔄 Ralph — Auto-Triage

    Assigned to: Booster
    Reason: Matched routing keyword(s): cd, GitHub Actions
    Source: routing-rule

    Ralph auto-triaged this issue using routing rules.
    To reassign, swap the squad:* label.

  8. github-actions commented on Sep 11, 2026

    @github-actions
    Contributor

    🔄 Ralph — Auto-Triage

    Assigned to: Booster
    Reason: Matched routing keyword(s): cd, GitHub Actions
    Source: routing-rule

    Ralph auto-triaged this issue using routing rules.
    To reassign, swap the squad:* label.

  9. github-actions commented on Sep 13, 2026

    @github-actions
    Contributor

    🔄 Ralph — Auto-Triage

    Assigned to: architect
    Reason: No module, routing, or role keyword match — routed to Lead/Architect
    Source: lead-fallback

    Ralph auto-triaged this issue using routing rules.
    To reassign, swap the squad:* label.

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

    enhancementNew feature or improvementprdProduct Requirements DocumentsquadSquad triage inbox — Lead will assign to a membersquad:architectAssigned to architect (Lead Architect)triagedIssue has been triagedtype:epicParent issue that decomposes into sub-issueswave: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