Skip to content

Feature: Tiered Squad Deployment — Hub Repos, Companion Repos, and Meta-Hub for Clean Production Repos #242

@dfberry

Description

@dfberry

Overview

Squad currently assumes .squad/ is co-located with the repository you're actively working in. This works well for single-repo setups, but it fundamentally breaks down for users who want clean production repos — and breaks silently, with no error message, making it look like a fresh install every time.

This issue proposes a first-class tiered Squad deployment model that spans from a single repo all the way to a personal meta-hub that monitors every squad you operate, across personal accounts and org accounts alike. Each tier supports GitHub Discussions as a communication layer — surfacing agent activity, decisions, and escalations to humans without polluting source repo backlogs.


The Core Problem

Squad resolves its team root by running git rev-parse --show-toplevel from the current working directory, then checking for .squad/ there. If nothing is found, it walks git worktree list looking for a main checkout. If that also fails, it enters Init Mode — silently, indistinguishably from a fresh install.

This means: if you open a terminal in a target repo that has no .squad/, Squad sees no team at all. The user must remember to always start sessions from the hub directory — easy to forget, not documented as required, no error when forgotten.

This cannot be fixed with documentation alone. The failure mode gives no signal that a hub exists elsewhere.


The Tiered Deployment Model

Squad should recognize and support three tiers of team state storage, each building on the one before. Every tier supports GitHub Discussions as an optional communication surface.

Tier 1 — Team (single repo)

The current default. .squad/ lives inside the source repo, or in a dedicated companion branch or companion repo for that project.

my-project/               ← source repo
  .squad/                 ← Squad state (current behavior)

— OR —

my-project/               ← source repo (Squad-clean)
my-project-squad/         ← private companion repo, Squad state only

Discussions at Tier 1:

my-project-squad (Discussions)
  📋 Standup          ← Scribe: session summaries after significant work
  🚦 Decisions        ← decisions posted for async human review
  ⚠️  Blocked         ← issues an agent couldn't resolve alone

When to use: Solo projects, small teams, repos where Squad noise in git history is acceptable.


Tier 2 — Hub (domain-level, multiple repos)

A private hub repo owns Squad state for an entire domain of related repos. Target repos are completely Squad-clean — no .squad/, no agent history, nothing.

geraldinefberry-content-squad/    ← private hub: Azure content writing
  .squad/
    team.md, agents/, decisions.md, ...

geraldinefberry-samples-squad/    ← private hub: azure-samples code
  .squad/
    team.md, agents/, decisions.md, ...

(target repos — Squad-free, collaborators see nothing)
azure-docs-pr/
azure-samples/
sample-repo-1/
sample-repo-2/

Discussions at Tier 2:

geraldinefberry-content-squad (Discussions)
  📋 Standup          ← Scribe: daily summary of agent activity across all target repos
  🚦 Decisions        ← decisions that affect the domain (e.g., template changes)
  ⚠️  Blocked         ← agent blocked on ambiguous requirements, needs human input

geraldinefberry-samples-squad (Discussions)
  📋 Standup
  🚦 Decisions
  ⚠️  Blocked

When to use:

  • You manage many repos in a domain (e.g., all of azure-samples)
  • Repos are shared or public and Squad state would be noise for collaborators
  • You have distinct responsibility areas, each needing their own team and memory
  • You want Squad product upgrades (new files, renamed files, schema changes) to land only in the hub, never in production repos

Tier 3 — Meta-Hub (cross-org, all squads)

A single personal or org-level repo — named by convention {username}/.squad — that knows about every squad you operate across personal accounts, work accounts, and org accounts.

geraldinefberry/.squad            ← meta-hub: all squads
  cross-squad/
    registry.json                 ← index of all registered squad repos
    geraldinefberry-content-squad/
    geraldinefberry-samples-squad/
    microsoft-azure-docs-squad/

geraldinefberry/content-squad     ← hub for content domain
geraldinefberry/azure-samples-squad  ← hub for samples domain
microsoft/azure-docs-squad        ← org-level hub (if permitted)

The {username}/.squad naming mirrors GitHub's own {username}/.github convention — discoverable by design.

Discussions at Tier 3:

geraldinefberry/.squad (Discussions)
  📢 Announcements    ← meta Ralph: weekly board summary across all registered squads
  🔄 Cross-Squad      ← escalations that span multiple hubs
  🏗️ Architecture     ← decisions that affect multiple squads or all domains

What the meta-hub does:

Capability Detail
Registry cross-squad/registry.json maps every squad repo by owner, org, domain, and access level
Cross-squad Ralph Ralph runs at the meta level — monitors open issues and PRs across ALL registered squads
Health dashboard Summarizes board status across all squads: issues open, PRs pending, squads idle
Promotion routing Issues that span domains can be escalated to the meta Lead, who routes sub-tasks to domain squads
Credential scoping Knows which gh contexts (personal, MSFT SSO, etc.) to use per squad

When to use: You operate multiple squads across personal and org accounts and want one place to see the full picture.


Tier Summary

Tier Scope Repo naming Ralph scope Discussions categories
team One repo .squad/ in repo, or {repo}-squad companion That repo's board Standup, Decisions, Blocked
hub One domain (multiple repos) {domain}-squad or {username}-squad All repos in the domain Standup, Decisions, Blocked
meta All squads, all orgs {username}/.squad or {org}/.squad All registered squads Announcements, Cross-Squad, Architecture

Companion Repo vs. Companion Branch

At Tier 1 and Tier 2, Squad state can be stored in either a branch or a repo:

Concern Branch (--squad-branch) Companion Repo (--companion-repo)
Squad files visible in public repo file tree ❌ Yes (on that branch) ✅ Never — different repo
History pollution in source repo ❌ Orphan branch still in git log --all ✅ Completely separate history
Visibility control ❌ Inherits source repo visibility ✅ Can be private while source is public
Organizational clarity 🟡 Requires branch discipline ✅ Wrong repo = obvious mistake
Multi-repo span ❌ One branch per source repo ✅ Single hub repo spans many sources

Both should be first-class init options:

  • squad init --squad-branch squad → orphan branch in same repo
  • squad init --companion-repo → new private sibling repo (recommended for public repos)
  • squad init --hub → promote to Tier 2 hub, spanning multiple repos
  • squad init --meta → scaffold Tier 3 meta-hub with registry

Discovery: Finding the Team Root

For Squad to work from any directory regardless of tier, it needs a multi-step discovery mechanism:

Resolution Order

  1. SQUAD_TEAM_ROOT env var — if set and valid, use immediately. No file scanning.
  2. .squadrc pointer file — walk up from CWD (same as .git discovery). If found, team root = path in file.
  3. .squad/ at git rev-parse --show-toplevel — current behavior.
  4. .squad/ at main worktree — current fallback via git worktree list.
  5. Init Mode — but now with a hint: "No team found. If your team lives in a hub repo, set SQUAD_TEAM_ROOT or add a .squadrc file pointing to it."

.squadrc Format

# .squadrc (in target repo root, gitignored by default)
team_root = ~/repos/geraldinefberry-samples-squad

Discovered by walking up from CWD, so it works from any subdirectory of the target repo.

SQUAD_TEAM_ROOT env var

export SQUAD_TEAM_ROOT=~/repos/geraldinefberry-samples-squad

Zero repo changes. Set once in shell profile. Works from any directory.


Commit Targeting: Keeping Scribe on the Right Branch

Today Scribe commits .squad/ changes to whatever branch is checked out. For hub and meta repos this is fine, but for the --squad-branch pattern, Scribe needs to know to commit to the designated branch, not the current one.

Proposed: git.commit_branch in .squad/config.json

{
  "git": {
    "commit_branch": "squad"
  }
}

Scribe reads this field and commits to the specified branch. Field is optional — omitting it preserves current behavior.

Automated Setup via --squad-branch

squad init --squad-branch squad would:

  1. Create orphan squad branch (or use existing)
  2. Add it as a git worktree at ../{repo-name}-squad/
  3. Initialize .squad/ in that worktree
  4. Write .squadrc in the project root pointing at the worktree
  5. Set git.commit_branch: "squad" in .squad/config.json

GitHub Discussions as Communication Layer

Squad's file-based state (decisions.md, history.md, logs) is machine-readable and agent-friendly, but invisible to humans unless they know to look. GitHub Discussions provides a complementary surface: human-readable, notification-driven, threaded, and built into GitHub with no extra tooling.

This is additive — Discussions surface Squad activity to humans without replacing file-based state. Agents write decisions and logs as today; Scribe optionally mirrors significant events to Discussions for async human visibility.

Per-Tier Discussion Categories

Each tier uses a different category set reflecting its scope:

Tier Categories Purpose
Tier 1 (team) Standup, Decisions, Blocked Single-project agent activity and human check-ins
Tier 2 (hub) Standup, Decisions, Blocked Domain-wide agent activity across all target repos
Tier 3 (meta) Announcements, Cross-Squad, Architecture Cross-squad coordination and escalation

What Gets Posted Where

Signal Agent Tier Category Why
Session summary after significant work Scribe 1 or 2 Standup Async human catch-up
Decision that affects the domain Scribe 2 Decisions Human review before it propagates
Agent blocked on ambiguous requirements Any agent 1 or 2 Blocked Human input needed
Weekly board summary (issues closed, PRs merged) Ralph 3 (meta) Announcements Cross-squad visibility
Issue that spans two domains Lead (hub) 3 (meta) Cross-Squad Escalation with full context
Architectural decision affecting multiple squads Lead (meta) 3 (meta) Architecture Visibility before adoption

Inter-Squad Escalation Flow

Tier 2 Hub (content-squad)
  Scribe posts: "Decision: switching all Azure content to new template format"
        ↓
  Tier 3 Meta-hub Lead sees it in Cross-Squad feed
        ↓
  Meta Lead posts to meta Discussions: "This affects azure-samples-squad —
  @geraldinefberry review before samples squad adopts same template"
        ↓
  Human (geraldinefberry) responds in Discussion thread
        ↓
  Meta Scribe writes resolution back to .squad/decisions/inbox/

Why Discussions Over Issues

  • Issues are work items. A decision isn't a bug. An agent standup isn't a task. Using issues for communication pollutes the backlog.
  • Discussions support threading. A cross-squad architectural debate can have a main post (Scribe) with human responses, without creating noise in either source repo.
  • Discussions have categories. The structured categories map cleanly to signal types (decisions, standups, escalations).
  • Discussions are searchable across repos. GitHub's search indexes Discussion content — a human can find a decision across all squad repos from one place.

Configuration

// .squad/config.json
{
  "discussions": {
    "enabled": true,
    "standup_category": "Standup",
    "decisions_category": "Decisions",
    "blocked_category": "Blocked",
    "post_after_session": true,
    "post_decisions_threshold": "significant"
  }
}

squad init asks: "Enable GitHub Discussions for this squad? Scribe can post session summaries and decisions there. (yes/no)"


Cross-Org Considerations (Meta-Hub)

Squad repos inside Microsoft orgs (e.g., microsoft/azure-docs-squad) may not be readable from a personal meta-hub without token scoping. The meta-hub needs:

  • Multiple gh auth contexts (gh auth switch support)
  • Read-only mirroring of org squad state into cross-squad/ in the meta-hub
  • Respect for org visibility rules — private org squad state must not be written to a public meta-hub
  • Discussions at the meta tier must respect the same visibility rules — private org Discussions must not be cross-posted to a public meta-hub

Why This Is a Product Gap, Not a Docs Fix

The hub repo pattern is the natural conclusion of recommending that users keep Squad state out of shared repos. If Squad recommends clean repos but silently fails when that architecture is used, the product is sending users toward a cliff.

The failure mode is indistinguishable from a fresh install: no error, no hint, no recovery path. A correctly configured hub user hits Init Mode on day one.


Acceptance Criteria

Discovery

  • Squad resolves team root from SQUAD_TEAM_ROOT env var when set
  • Squad resolves team root from .squadrc pointer file when present in CWD ancestry
  • When no team is found, Init Mode prompt includes a hint about SQUAD_TEAM_ROOT and .squadrc
  • .squadrc is added to Squad's default .gitignore template (opt-in to commit)

Init flags

  • squad init --squad-branch <name> creates orphan branch + worktree + .squadrc + config.json automatically
  • squad init --companion-repo creates a private {repo}-squad sibling repo and writes .squadrc
  • squad init --hub scaffolds a domain hub repo spanning multiple registered target repos
  • squad init --meta scaffolds a {username}/.squad meta-hub with cross-squad/registry.json

Commit targeting

  • Scribe reads git.commit_branch from .squad/config.json and commits to that branch when set
  • squad init without flags behaves exactly as today (no regression)

Tiered naming convention

  • {username}/.squad recognized as a meta-hub by convention
  • squad register {owner/squad-repo} CLI command adds a squad to the meta-hub registry
  • Ralph at meta tier can aggregate board status across all registered squads

GitHub Discussions

  • Scribe can post to GitHub Discussions via gh api or MCP tools when discussions.enabled: true
  • Each tier uses its default category set: Tier 1/2 → Standup/Decisions/Blocked; Tier 3 → Announcements/Cross-Squad/Architecture
  • Meta-hub Ralph posts a weekly board summary to {username}/.squad Discussions > Announcements
  • Escalation routing: when a hub agent is blocked, Scribe opens a Discussion thread in the hub repo and (if configured) cross-posts to the meta-hub's Cross-Squad category
  • squad init offers Discussions setup as an optional post-init step
  • Discussion post format is consistent: agent name, squad, date, concise summary, link to relevant .squad/ files
  • Discussions visibility follows org visibility rules — private org squad Discussions must not be cross-posted to a public meta-hub

Documentation

  • Hub repo pattern documented as a first-class deployment option
  • All three tiers documented with examples, tradeoff table, and Discussions category mapping
  • Cross-org auth (multiple gh contexts) documented in .squadrc reference
  • GitHub Discussions configuration documented per tier with setup instructions

Metadata

Metadata

Assignees

No one assigned

    Labels

    go:needs-researchNeeds investigationsquadSquad triage inbox — Lead will assign to a membersquad:hockneyAssigned to Hockney (Tester)squad:keatonAssigned to Keaton (Lead)status:backlogBacklog item

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions