From ce8ef7cc0806d0c6cbad0041274f3acbf802bd9c Mon Sep 17 00:00:00 2001 From: Brady Gaster Date: Sun, 2 Aug 2026 18:39:42 -0700 Subject: [PATCH] =?UTF-8?q?docs:=20issue=20truth=20fixes=20=E2=80=94=20PRD?= =?UTF-8?q?=20mode,=20state=20storage,=20ralph=20escape=20hatch=20(WS-C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rewrite features/prd-mode.md: ingestion/analysis only, removes 'write a PRD for me' framing - Rewrite scenarios/team-state-storage.md: replace .ai-team/ with .squad/, document local/orphan/two-layer backends and squad init --state-backend, remove outdated worktree/submodule strategies - Update features/state-backends.md with two-layer details, git-notes deprecation note, migration guidance - Document .squad/ralph-instructions.md escape hatch in features/ralph.md Related to #1314 (docs recovery — full template/TEMPLATE_MANIFEST work tracked separately in #1314) Closes #1186 Closes #1194 Supersedes part of #1426 Working as PAO (DevRel) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/src/content/docs/features/prd-mode.md | 137 ++----- docs/src/content/docs/features/ralph.md | 118 +----- .../content/docs/features/state-backends.md | 198 +--------- .../docs/scenarios/team-state-storage.md | 368 ++++++------------ 4 files changed, 171 insertions(+), 650 deletions(-) diff --git a/docs/src/content/docs/features/prd-mode.md b/docs/src/content/docs/features/prd-mode.md index 42417d95b..2a91fa245 100644 --- a/docs/src/content/docs/features/prd-mode.md +++ b/docs/src/content/docs/features/prd-mode.md @@ -1,124 +1,73 @@ # PRD Mode - -> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. - - -**Try this to generate a requirements document:** +**Try this to ingest an existing PRD:** ``` -Write a PRD for a user authentication system with OAuth support +Read the PRD at docs/product-spec.md and summarize the key requirements ``` - -**Try this to break down product specs into work items:** +**Try this to turn an existing spec into next steps:** ``` -Read the PRD at docs/product-spec.md and break it into work items +Read docs/product-spec.md and suggest implementation slices or GitHub issues ``` - -Give Squad a product requirements document and the Lead breaks it into prioritized work items, assigns them to the team, and tracks progress with dependency management. +Squad can **read an existing PRD or spec** and help you reason about it in chat. Today, this is an ingestion workflow — not a dedicated PRD authoring system or a built-in persistent work-item tracker. --- - -## How to Use - -Give Squad a product requirements document. The Lead agent breaks it into work items, assigns them to the team, and tracks progress. +## What works today +Squad can: +- Read a PRD you paste into chat +- Read a PRD from a file path you provide +- Summarize requirements, risks, and open questions +- Suggest implementation slices, milestones, or issue breakdowns +There is **no special CLI command or router for PRD mode** in the current implementation. The feature works because you can point Squad at an existing document and ask for analysis. --- - -## How to Use - -Paste a PRD directly into the chat: - +## How to use it +Paste the PRD directly into chat: ``` -> Here's what we're building: +> Here's the PRD for the new onboarding flow: > -> [paste your PRD or spec] +> [paste the document] ``` - -Or reference a file: - +Or reference a file that already exists in the repo: ``` -> Read the PRD at docs/product-spec.md and break it into work items +> Read the PRD at docs/product-spec.md and call out the main requirements ``` --- - -## How It Works - -1. **Lead decomposes the spec** into discrete work items (WI-1, WI-2, WI-3, etc.) -2. **Each work item gets a priority**: P0 (must-have), P1 (important), P2 (nice-to-have) -3. **Work items are assigned** to agents based on domain expertise -4. **Dependencies are tracked** — Squad won't start WI-4 if it depends on WI-2 finishing first -5. **Parallel work** happens where possible — independent work items run simultaneously - -### Example output - -``` -📋 PRD decomposed into 8 work items: - -WI-1 [P0] Set up project structure → Kane (Backend) -WI-2 [P0] Design database schema → Kane (Backend) -WI-3 [P0] Build component library → Dallas (Frontend) -WI-4 [P1] Implement auth endpoints → Kane (Backend) [depends: WI-2] -WI-5 [P1] Build login/signup UI → Dallas (Frontend) [depends: WI-3] -WI-6 [P1] Write auth integration tests → Lambert (Tester) [depends: WI-4] -WI-7 [P2] Add social login → Kane (Backend) [depends: WI-4] -WI-8 [P2] Build user profile page → Dallas (Frontend) [depends: WI-5] -``` +## What to expect +A good PRD-ingestion session usually produces: +1. A summary of the product goals and constraints +2. A list of ambiguities or missing decisions +3. A suggested implementation breakdown you can turn into issues or milestones +4. Follow-up prompts for the lead, reviewers, or implementers +This is best thought of as **spec reading and decomposition assistance**. --- - -## Mid-Project PRD Updates - -Requirements change. When they do, give Squad the updated PRD: - -``` -> The PRD has been updated — re-read docs/product-spec.md -``` - -The Lead agent: - -1. Re-reads the PRD -2. Diffs against the existing work items -3. Adjusts the backlog — adds new items, re-prioritizes, or marks items as obsolete - -Work already completed isn't undone. Only the remaining backlog changes. +## What it does not do today +Be careful not to over-read the feature: +- It does **not** provide a dedicated “write a PRD for me” product mode +- It does **not** maintain a first-class backlog with stored WI-1 / WI-2 style identifiers +- It does **not** persist dependency graphs or a PRD-specific board you can query later with commands like “show me the work items” +If you want durable tracking, use the decomposition output to create GitHub issues, project-board items, or other repo artifacts explicitly. --- - ## Tips - -- P0 work items are tackled first. Use priority levels to control sequencing. -- The Lead handles decomposition — you don't need to break down the spec yourself. -- Dependencies are respected automatically. You won't see an agent start on a dependent task before its prerequisite is done. -- Combine with [GitHub Issues Mode](github-issues.md) to create GitHub issues from work items. - +- Start from a real document — a PRD, spec, RFC, or issue write-up already in the repo. +- Ask for gaps and risks, not just summaries. +- After Squad suggests slices, promote the useful ones into GitHub issues or project-board items. +- Combine with [GitHub Issues Mode](github-issues.md) when you want the decomposition turned into tracked work. ## Sample Prompts - ``` -read the PRD at docs/product-spec.md and break it into work items +read the PRD at docs/product-spec.md and summarize the key requirements ``` - -Ingests a product requirements document and creates a prioritized, dependency-tracked backlog. - -``` -show me the work items -``` - -Displays the current backlog with priorities, assignments, and dependencies. - +Ingests an existing PRD and produces a concise requirements summary. ``` -the PRD has been updated — re-read docs/product-spec.md +read docs/product-spec.md and list open questions, risks, and assumptions ``` - -Re-ingests the PRD and adjusts the backlog based on changes without undoing completed work. - +Finds the places where the spec is incomplete or needs decisions. ``` -start working on approved P0 items +read docs/product-spec.md and suggest an implementation breakdown ``` - -Begins parallel execution of all high-priority work items with no blockers. - +Proposes slices you can convert into issues, milestones, or workstreams. ``` -which work items are blocked right now? +the PRD changed — re-read docs/product-spec.md and tell me what changed materially ``` - -Shows which tasks are waiting on dependencies or other blocking conditions. +Re-ingests the updated document and highlights changes in the requirements. diff --git a/docs/src/content/docs/features/ralph.md b/docs/src/content/docs/features/ralph.md index 3aefacca2..00e16219d 100644 --- a/docs/src/content/docs/features/ralph.md +++ b/docs/src/content/docs/features/ralph.md @@ -1,53 +1,36 @@ # Ralph — Work Monitor - -> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. - - **Try this to see active work:** ``` Ralph, show me what everyone is working on ``` - **Try this to identify blockers:** ``` Ralph, what's blocking progress on issue #42? ``` - **Try this to auto-assign work:** ``` Ralph, assign the next high-priority issue ``` - Ralph tracks the work queue, monitors CI status, and ensures the team never sits idle when there's work to do. He's always on the roster and requires GitHub CLI access. --- - ## What Ralph Does - Ralph is a built-in squad member whose job is keeping tabs on work. Like Scribe tracks decisions, **Ralph tracks and drives the work queue**. He's always on the roster — not cast from a universe — and has one job: make sure the team never sits idle when there's work to do. - Ralph uses intelligent routing to match work to the right agent. Rather than simple keyword matching against role titles, Ralph reads `.squad/routing.md` — your team's work-type definitions and module ownership — to make smart triage and dispatch decisions. This is the same intelligence the in-session coordinator uses. - ## Prerequisites - Ralph requires access to GitHub Issues and Pull Requests via the `gh` CLI. **A GitHub PAT (Personal Access Token) with Classic scope is required.** - ### Why PAT Classic? - The default `GITHUB_TOKEN` provided by Copilot does not have sufficient scopes to read and write GitHub Issues and PRs. Ralph needs to: - List and read issues - Create and update issue labels and assignments - Read and interact with pull requests - Report on CI status - ### Setup - 1. **Create a PAT Classic token:** - Go to https://github.com/settings/tokens - Click "Generate new token (classic)" - Select scopes: `repo` and `project` (full access to repositories and projects) - Copy the token - 2. **Authenticate with `gh`:** ```bash gh auth login @@ -56,69 +39,48 @@ The default `GITHUB_TOKEN` provided by Copilot does not have sufficient scopes t - Select "HTTPS" for protocol - When asked "Authenticate Git with your GitHub credentials?", answer "Yes" - Choose "Paste an authentication token" and paste your PAT Classic token - 3. **Verify authentication:** ```bash gh auth status ``` - Once authenticated, Ralph can monitor your repository's issues and PRs. - ## How It Works - -Once activated, Ralph continuously checks for pending work — open issues, draft PRs, review feedback, CI failures — and keeps the squad moving through the backlog without manual nudges. Ralph's behavior is built on three layers: in-session coordinator, watch mode for local polling, and cloud heartbeat for fully unattended monitoring. - +Once activated, Ralph continuously checks for pending work — open issues, draft PRs, review feedback, CI failures — and keeps the squad moving through the backlog without manual nudges. Ralph's behavior is built on three layers: in-session coordinator, watch mode for local polling, and cloud heartbeat for event-driven monitoring. ### Routing-Aware Triage - Ralph doesn't rely on dumb keyword matching. He reads your `.squad/routing.md` file to understand: - **Work types** — categories like "Core runtime", "Docs & messaging", "Tests & quality" - **Agent assignments** — which agent owns each domain - **Module ownership** — which files belong to which agent (e.g., `src/hooks/` → Baer) - When triaging an issue, Ralph uses this priority order: 1. **Module path match** — If the issue mentions a file in `src/hooks/`, it routes to Baer (primary owner) 2. **Routing rule keywords** — If the issue mentions "docs" or "messaging", Ralph looks up those work types and assigns the matching agent (McManus for "Docs & messaging") 3. **Role keywords** — If no module or routing rule matches, Ralph scans the issue for role titles ("test", "security", "performance") 4. **Lead fallback** — If still no match, escalate to the team Lead for manual review This ensures Ralph makes intelligent decisions based on your team's actual structure, not generic heuristics. - ### In-Session (Copilot Chat) - When you're in a Copilot session, Ralph self-chains the coordinator's work loop: - 1. Agents complete a batch of work 2. Ralph checks GitHub for more: untriaged issues, assigned-but-unstarted items, draft PRs, failing CI 3. Work found → triage, assign, spawn agents 4. Results collected → Ralph checks again **immediately** — no pause, no asking permission 5. Board clear → Ralph idles (use `squad watch` for persistent polling) - -**Ralph never stops on his own while work remains.** He keeps cycling through the backlog until every issue is closed, every PR is merged, and CI is green. When the board clears, Ralph idles — run `squad watch` in a separate terminal for persistent polling, or use the cloud heartbeat for fully unattended monitoring. The only things that stop Ralph's active loop: the board is clear, you say "idle"/"stop", or the session ends. - +**Ralph never stops on his own while work remains.** He keeps cycling through the backlog until every issue is closed, every PR is merged, and CI is green. When the board clears, Ralph idles — run `squad watch` in a separate terminal for persistent polling, or use the cloud heartbeat for event-driven monitoring. The only things that stop Ralph's active loop: the board is clear, you say "idle"/"stop", or the session ends. ### Between Sessions (GitHub Actions Heartbeat) - When no one is at the keyboard, the `squad-heartbeat.yml` workflow runs on event-based triggers (issue close, PR merge, manual dispatch). It: - - Finds untriaged `squad`-labeled issues - Auto-triages based on your routing.md — matching issues to the right agent by work type and module ownership - Assigns `squad:{member}` labels -- For `@copilot` (if enabled with auto-assign): assigns `copilot-swe-agent[bot]` so the coding agent picks up work autonomously - -This creates a fully autonomous loop for `@copilot` — heartbeat triages → assigns → agent works → issue closed → heartbeat finds next issue → repeat. For continuous periodic monitoring, use `squad watch` locally. - +- For `@copilot` (if enabled with auto-assign): assigns `copilot-swe-agent[bot]` so the coding agent picks up work in the background +This creates a background loop for `@copilot` — heartbeat triages → assigns → agent works → issue closed → heartbeat finds next issue → repeat. For continuous periodic monitoring, use `squad watch` locally. ### Work-in-Progress Monitoring - Ralph doesn't just dispatch work and forget about it. Once an issue is assigned or a PR is created, Ralph **watches the work** — tracking its lifecycle from assigned → PR created → review requested → CI running → approved → merged. Each completed step triggers a re-scan: - - **Assigned but no PR**: Ralph checks if the assigned agent has started work - **PR created**: Ralph monitors for review feedback and CI status - **Changes requested**: Ralph routes the feedback back to the author agent - **CI passing**: Ralph marks as ready to merge - **PR merged**: Ralph closes the corresponding issue and picks up the next work item - This continuous watch prevents work from getting stuck in intermediate states — Ralph catches stalled PRs, failed CI, and review bottlenecks automatically. - ### Board State - Ralph maintains an internal view of the work board. Work items flow through these categories: | Category | Meaning | Label(s) | @@ -135,25 +97,19 @@ Ralph maintains an internal view of the work board. Work items flow through thes Ralph uses these categories internally to decide what action to take next. When you ask for status, Ralph reports the current board state across all these categories. ### What Wakes Ralph Up - Ralph monitors work at three different layers, each with different wake-up triggers: - **In-Session (Copilot Chat):** - Agent completes work → Ralph immediately checks for next item (no delay) - You say "Ralph, go" or "Ralph, status" → Ralph starts active loop - You say "Ralph, idle" → Ralph stops checking - **Watch Mode (`squad watch` CLI):** - Poll interval expires (default 10 min) → Ralph checks GitHub - You press Ctrl+C → Ralph stops - **Cloud Heartbeat (GitHub Actions events):** - Issue close event → Ralph checks for next item - PR merge event → Ralph checks for next item - Manual dispatch via GitHub Actions UI → Ralph checks GitHub - In all three layers, when Ralph wakes up, he scans the board, triages any untriaged items using routing.md, dispatches work to the right agent, watches in-flight items for progress, and reports results. - ## Talking to Ralph | What you say | What happens | @@ -176,52 +132,44 @@ In all three layers, when Ralph wakes up, he scans the board, triages any untria | **Approved PRs** | Ready to merge | Merge and close issue | ## Periodic Check-In - Ralph doesn't run silently forever. Every 3-5 rounds, Ralph reports and **keeps going**: - ``` 🔄 Ralph: Round 3 complete. ✅ 2 issues closed, 1 PR merged 📋 3 items remaining: #42, #45, PR #12 Continuing... (say "Ralph, idle" to stop) ``` - Ralph does **not** ask permission to continue — he keeps working. The only things that stop Ralph: the board is clear, you say "idle"/"stop", or the session ends. - ## Watch Mode (`squad watch`) - Ralph's in-session loop processes work while it exists, then idles. For **persistent polling** when you're away from the keyboard, run the `squad watch` command in a separate terminal. - ### Triage Mode (Default) - Basic usage — triage only, no execution: - ```bash squad watch # polls every 10 minutes (default) squad watch --interval 5 # polls every 5 minutes squad watch --interval 30 # polls every 30 minutes ``` - This runs as a standalone local process (not inside Copilot) that: - Checks GitHub every N minutes for untriaged squad work - Auto-triages issues based on team roles and keywords - Assigns @copilot to `squad:copilot` issues (if auto-assign is enabled) - Runs until Ctrl+C - ### Full Work Monitor Mode (`--execute`) - -Add `--execute` to transform Ralph from a triage bot into a full work monitor that spawns Copilot sessions and actually does the work: - +Add `--execute` to transform Ralph from a triage bot into a full work monitor that spawns Copilot sessions and moves approved work forward: ```bash squad watch --execute # basic work monitor squad watch --execute --interval 15 # check every 15 minutes squad watch --execute --max-concurrent 2 # work on 2 issues in parallel ``` - When `--execute` is enabled, Ralph spawns Copilot CLI sessions for actionable issues (assigned to a squad member, not blocked, not already assigned to a human). Squad automatically injects `--yolo --additional-mcp-config @.mcp.json` into every spawned Copilot invocation so that MCP tools are available in non-interactive (`-p`) mode — see [Copilot CLI MCP Trust Gate](./copilot-mcp-trust.md) for details. - +#### Custom instructions with `.squad/ralph-instructions.md` +If `.squad/ralph-instructions.md` exists, `squad watch --execute` tells the spawned Ralph session to read that file first and follow **all** of its sections. This is the escape hatch for repo-specific Ralph behavior. +Use it when you want Ralph to consistently apply local operating rules such as: +- preferred execution order or escalation rules +- custom branch/PR conventions +- repo-specific definitions of “blocked” or “actionable” +If the file is missing, Ralph falls back to the built-in execution prompt. **Example execution output:** - ``` 🔄 Ralph — Round 1 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -234,11 +182,8 @@ When `--execute` is enabled, Ralph spawns Copilot CLI sessions for actionable is ▶ [14:25:44] Executing #45 "Add retry logic" → gh copilot --message "Work on issue #45..." ✓ [14:28:20] #45 completed ``` - ### All Watch Flags - All new features are **opt-in** and disabled by default. Existing `squad watch` behavior is unchanged. - #### Execution Control | Flag | Description | Example | @@ -280,31 +225,24 @@ All new features are **opt-in** and disabled by default. Existing `squad watch` | `--channel-routing` | Route notifications to specific Teams channels (requires `.squad/teams-channels.json`) | `squad watch --channel-routing` | ### Common Workflows - **Basic triage + work execution:** ```bash squad watch --execute --interval 10 ``` - **Full monitor with all features:** ```bash squad watch --execute --board --two-pass --monitor-teams --retro --decision-hygiene --max-concurrent 2 --interval 15 ``` - **Cost-conscious (two-pass, lower concurrency):** ```bash squad watch --execute --two-pass --max-concurrent 1 --timeout 20 ``` - **Teams + email bridge only (no issue execution):** ```bash squad watch --monitor-teams --monitor-email --interval 5 ``` - ### Round Cycle (Full Monitor) - When all features are enabled, each round follows this cycle: - 1. **Self-pull**: `git fetch && git pull --ff-only` to stay current 2. **Scan**: Fetch open issues (two-pass if enabled) 3. **Triage**: Label untriaged issues based on routing rules @@ -313,23 +251,15 @@ When all features are enabled, each round follows this cycle: 6. **Monitor**: Scan Teams/email for new actionable items 7. **Housekeep**: Check for retro, merge decision inbox if needed 8. **Report**: Log round summary, sleep until next interval - ### Advanced: `--agent-cmd` (Hidden Flag) - For advanced users who know what they're doing: - ```bash squad watch --execute --agent-cmd "custom-agent-wrapper" ``` - This fully overrides the agent command. The default is `gh copilot --message ""` plus any `--copilot-flags`. Use this to plug in custom agent wrappers or alternative Copilot entry points. - ### Azure DevOps Support - Ralph supports Azure DevOps repos and work items via the SDK's PlatformAdapter. When your git remote points to `dev.azure.com` or `visualstudio.com`, Ralph auto-detects ADO — no flag needed. - **Setup:** - 1. Install Azure CLI: `az extension add --name azure-devops` 2. Authenticate: `az login` 3. Add ADO config to `.squad/config.json`: @@ -342,31 +272,26 @@ Ralph supports Azure DevOps repos and work items via the SDK's PlatformAdapter. } } ``` - **Usage:** ```bash squad watch # auto-detects from git remote squad watch --execute # full work monitor (auto-detects platform) ``` - **Key differences from GitHub:** - ADO uses **tags** instead of labels — `squad:data` becomes a tag on the work item - ADO uses `az boards` CLI instead of `gh` — Ralph checks `az` availability - ADO rate limiting is handled differently — the circuit breaker skips quota checks - ADO PRs don't expose `statusCheckRollup` — CI status columns may be empty - ### Three layers of Ralph | Layer | When | How | |-------|------|-----| | **In-session** | You're at the keyboard | "Ralph, go" — active loop while work exists | | **Local watchdog** | You're away but machine is on | `squad watch --interval 10` (triage) or `squad watch --execute` (full monitor) | -| **Cloud heartbeat** | Fully unattended | `squad-heartbeat.yml` GitHub Actions events (issue close, PR merge, manual dispatch) | +| **Cloud heartbeat** | Event-driven | `squad-heartbeat.yml` GitHub Actions events (issue close, PR merge, manual dispatch) | ## Ralph's Board View - When you ask for status: - ``` 🔄 Ralph — Work Monitor ━━━━━━━━━━━━━━━━━━━━━━ @@ -376,52 +301,35 @@ When you ask for status: 🟢 Ready: 1 PR approved, awaiting merge ✅ Done: 5 issues closed this session ``` - ## Heartbeat Workflow Setup - The heartbeat workflow (`squad-heartbeat.yml`) is automatically installed during `init` or `upgrade`. It runs: - - **On issue close**: Checks for next item in backlog - **On PR merge**: Checks for follow-up work - **On manual dispatch**: Trigger via GitHub Actions UI - For persistent polling when you're away, use `squad watch` locally — it polls at your chosen interval without consuming GitHub Actions minutes. - ## Notes - - Ralph is session-scoped — his state (active/idle, round count, stats) resets each session - Ralph appears on the roster like Scribe: `| Ralph | Work Monitor | — | 🔄 Monitor |` - Ralph is exempt from universe casting — always "Ralph" - The heartbeat workflow is the between-session complement to in-session Ralph - ## Sample Prompts - ``` Ralph, go — start monitoring and process the backlog until it's clear ``` - Activates Ralph's self-chaining work loop to continuously process all pending work. - ``` Ralph, status ``` - Runs a single check cycle and shows the current board state without activating the work loop. - ``` squad watch --interval 5 ``` - Starts persistent local polling — checks GitHub every 5 minutes for new squad work and triages automatically. - ``` Ralph, scope: just issues ``` - Configures Ralph to monitor only issues and skip PRs and CI status checks. - ``` Ralph, idle ``` - Fully stops Ralph's work loop and idle-watch polling until manually reactivated. diff --git a/docs/src/content/docs/features/state-backends.md b/docs/src/content/docs/features/state-backends.md index ee621ed38..d26f81637 100644 --- a/docs/src/content/docs/features/state-backends.md +++ b/docs/src/content/docs/features/state-backends.md @@ -1,58 +1,38 @@ # State Backends - -> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. - Squad supports multiple **state backends** for storing `.squad/` state (decisions, agent memories, session logs, skills). Each backend determines _where_ and _how_ this data is persisted — without changing how agents interact with it. Once configured, everything is automatic. --- - ## The Problem - By default, Squad stores `.squad/` state as regular files in your working tree. This works for solo workflows but has real trade-offs for teams: - - **Branch pollution:** `.squad/` files appear in diffs and PRs - **Branch-switch loss:** State can be lost when switching branches (if not committed) - **Merge conflicts:** Multiple team members modifying `.squad/` files creates frequent conflicts - State backends solve this by moving `.squad/` data into Git-native structures that live outside the working tree — keeping your PRs clean and your state safe across branches. --- - ## Getting Started - ### New project — choose a backend during init - ```bash # Default (local — files in .squad/, same as always) squad init - # Orphan branch (state on a dedicated squad-state branch) squad init --state-backend orphan - # Two-layer (recommended for teams — orphan branch + git notes) squad init --state-backend two-layer ``` - > **Default backend:** if you don't pass `--state-backend`, Squad uses the > `local` backend (regular `.squad/` files in your working tree). The > `orphan` and `two-layer` backends are opt-in — you must pass the explicit > flag during `squad init` or `squad upgrade` to activate them. - The backend is stored in `.squad/config.json` — you never need to pass it again. All subsequent commands (`squad watch`, interactive sessions, etc.) read from config automatically. - ### Existing project — migrate with upgrade - ```bash # Migrate from local to orphan or two-layer squad upgrade --state-backend two-layer - # Or: squad upgrade --state-backend orphan ``` - This migrates existing state, creates the orphan branch, and installs git hooks for automatic multi-user sync. - ### What gets installed automatically - When you choose `orphan` or `two-layer`: - **Git hooks** (pre-push, post-merge, post-checkout, post-rewrite, pre-commit, post-commit) are installed in `.git/hooks/` - The sync hooks (pre-push, post-merge, post-checkout, post-rewrite) keep the `squad-state` branch in sync automatically when you push/pull @@ -61,28 +41,20 @@ When you choose `orphan` or `two-layer`: - Hooks chain with existing hooks (husky, etc.) — nothing is overwritten --- - ## Available Backends - ### Local (default) - State lives as regular files in `.squad/` inside the working tree. This is the standard behavior — what you get out of the box. - **Pros:** - Simple and familiar — files on disk - Easy to inspect, edit, and commit - Works with all Git tools and IDEs - **Cons:** - Files appear in `git status` and diffs - Branch switches can lose uncommitted state - **Best for:** Most projects, especially when you want squad state committed alongside code. --- - ### Git Notes (Deprecated → Two-Layer) - > ⚠️ **Deprecated:** The standalone `git-notes` backend has been removed as a user-facing option. If your config still references `git-notes`, it will be **automatically migrated to `two-layer`** at runtime. > > **Why:** Standalone git-notes stores all state as a single JSON blob on the root commit. This fundamentally cannot handle concurrent writes from multiple team members — `git notes merge` cannot merge opaque JSON, causing silent data loss. @@ -90,36 +62,27 @@ State lives as regular files in `.squad/` inside the working tree. This is the s > **Replacement:** The `two-layer` backend uses git notes as best-effort commit annotations (the "why" layer) while storing durable state on an orphan branch with per-file granularity (the "state" layer). This gives you the clean working tree of git-notes with the team-safe mergeability of the orphan approach. --- - ### Orphan Branch - State lives on a dedicated orphan branch (`squad-state` by default). The branch has no common history with your main branches — it's a completely separate tree used only for squad data. - **How it works:** - An orphan branch `squad-state` is created automatically on first write - Each state file is stored as a blob in the branch's tree - Reads use `git show squad-state:`, writes create new commits on the branch - The branch is never checked out — all operations use Git plumbing commands - **Pros:** - Working tree stays clean - State is versioned with full Git history - Easy to inspect: `git log squad-state`, `git show squad-state:decisions.md` - Pushes/fetches with normal branch operations - **Cons:** - An extra branch in your repository - Slightly more complex than `local` for debugging - Concurrent writes to the branch can conflict (single-writer recommended) - **Best for:** Teams who want Git-versioned state without polluting the main branch history. --- - ## Configuration - The state backend is set once (during `squad init` or `squad upgrade`) and stored in `.squad/config.json`: - ```json { "version": 1, @@ -127,21 +90,15 @@ The state backend is set once (during `squad init` or `squad upgrade`) and store "stateBackend": "two-layer" } ``` - All squad commands read from this file automatically. You don't need to pass `--state-backend` on every invocation. - > **Note:** If no `stateBackend` field exists, the default is `local` (current behavior, no change). - ### Fallback Behavior - If a non-default backend fails to initialize (e.g., Git is not available, permissions issue), Squad automatically falls back to the **local** backend with a warning: - ``` Warning: State backend 'two-layer' failed: . Falling back to 'local'. ``` --- - ## Comparison | Feature | Local | Orphan Branch | Two-Layer | @@ -157,52 +114,38 @@ Warning: State backend 'two-layer' failed: . Falling back to 'local'. | Team-safe (multi-user) | ❌ (merge conflicts) | ⚠️ (needs coordination) | ✅ (designed for teams) | --- - ## Inspecting State - ### Local - ```bash cat .squad/decisions.md ls .copilot/skills/ ``` - ### Git Notes - ```bash # Show all state as JSON (anchored to root commit) git notes --ref=squad show $(git rev-list --max-parents=0 HEAD) - # Pretty-print git notes --ref=squad show $(git rev-list --max-parents=0 HEAD) | python -m json.tool ``` - ### Orphan Branch - ```bash # List all state files git ls-tree --name-only -r squad-state - # Read a specific file git show squad-state:decisions.md - # View commit history git log --oneline squad-state ``` --- - ## SDK Usage - The state backend is available programmatically via the Squad SDK: - ```typescript import { resolveSquadState, resolveStateBackend, type StateBackend, } from '@bradygaster/squad-sdk'; - // Option 1: Full context resolution (recommended) // Resolves paths + backend from config + CLI override in one call const ctx = resolveSquadState(process.cwd(), 'two-layer'); @@ -211,7 +154,6 @@ if (ctx) { ctx.backend.append('log.md', 'New entry\n'); ctx.backend.delete('inbox/processed.md'); } - // Option 2: Backend-only resolution const backend: StateBackend = resolveStateBackend( '.squad', // squadDir @@ -220,9 +162,7 @@ const backend: StateBackend = resolveStateBackend( ); backend.write('decisions.md', '# Decisions\n...'); ``` - All backends implement the same `StateBackend` interface: - ```typescript interface StateBackend { read(relativePath: string): string | undefined; @@ -236,119 +176,85 @@ interface StateBackend { ``` --- - ## Security - State backends include hardening against common injection attacks: - - **Path traversal:** `..` segments are rejected - **Null byte injection:** `\0` characters are rejected - **Newline injection:** `\n` and `\r` characters are rejected (prevents Git plumbing manipulation) - **Tab injection:** `\t` characters are rejected (prevents mktree format corruption) - **Empty segments:** Double slashes (`//`) are rejected - All validation is centralized in `validateStateKey()` and applied uniformly across all backends. --- - ## Content Fidelity - All backends preserve content exactly as written — including trailing newlines, leading whitespace, and empty lines. This is critical for append-only files like `history.md` and `decisions.md` where multiple agents append entries over time. - The orphan and two-layer backends use raw `execFileSync` for content reads (without trimming) to ensure faithful round-trips. Git plumbing helpers that trim output are only used for non-content operations like `rev-parse` and `ls-tree`. --- - ## Worktree Awareness - When running in a git worktree, `resolveSquadState()` uses `git rev-parse --show-toplevel` to determine the actual current worktree root — not the parent of `.squad/`. This ensures that git-native backends (orphan, two-layer) operate in the correct repository context, even when `.squad/` is resolved from the main checkout via the worktree fallback strategy. --- - ## Notes - - State backends are **opt-in** — the default is `local` (no behavior change) - All backends implement the same interface — agents don't know or care which backend is active - Empty directories are automatically pruned after the last file is deleted (orphan backend) -- The `external-stub` backend type is an unimplemented placeholder that falls back to `local` (the legacy name `external` is still accepted with a deprecation warning); for real external storage see [External State](./external-state) +- The `external` backend type exists as a stub for future external storage (see [External State](./external-state)) - State backends are available in the **insider** release channel (`@bradygaster/squad-cli@insider`) - 63 unit tests + 46 E2E tests cover all backends including security hardening, content fidelity, and directory pruning --- - ## Using with Copilot CLI Sessions - The SDK's `StateBackend` interface handles programmatic state for Squad internals, but Copilot agents also need a way to write commit-scoped context — decisions, research, reviews — without creating `.squad/` file changes that pollute PRs. - The solution: agents use **git notes CLI commands** directly for mutable, commit-scoped state. The `notes-protocol.md` template defines the contract. - ### How it works - 1. Each agent writes to its own namespace: `refs/notes/squad/{agent-name}` 2. Notes are JSON with required fields: `agent`, `timestamp`, `type`, `content` 3. Notes are invisible in PR diffs — they travel as git refs, not files 4. Ralph promotes notes with `"promote_to_permanent": true` to `decisions.md` after merge 5. If a PR is rejected, notes on those commits are NOT promoted (desired behavior) - ### Setup - When you enable `stateBackend: "two-layer"` or `stateBackend: "orphan"`, copy the notes protocol and helper scripts into your project: - ```bash # Copy from Squad's templates (after squad init) cp .squad/templates/notes-protocol.md .squad/notes-protocol.md cp -r .squad/templates/scripts/notes/ scripts/notes/ - # One-time git config for notes fetch ./scripts/notes/fetch.ps1 -Setup ``` - ### Copilot Instructions Integration - Add the following to your `.github/copilot-instructions.md` (or `.copilot/copilot-instructions.md`) to teach agents the notes protocol: - ````markdown ## Git Notes — State Protocol - **Every agent uses git notes for commit-scoped state.** Do not write to `.squad/decisions.md` or other `.squad/` files directly on feature branches. - ### On every work round - 1. **Start**: `git fetch origin 'refs/notes/*:refs/notes/*'` 2. **When making a decision**: Write it as a note on the relevant commit 3. **End**: `git push origin 'refs/notes/*:refs/notes/*'` - ### Write pattern - ```bash git notes --ref=squad/{your-agent} add \ -m '{"agent":"{Name}","timestamp":"{ISO8601}","type":"decision","content":"..."}' \ HEAD ``` - Use `git notes append` if a note already exists on the commit. - ### Key rules - - Write only to your own namespace (`refs/notes/squad/{your-name}`) - Notes MUST be valid JSON - Set `"promote_to_permanent": true` for decisions that should outlast the branch - Set `"archive_on_close": true` for research worth keeping even if the PR is rejected - Fetch before write, push after your round - See `.squad/notes-protocol.md` for the full contract. ```` - ### Example: Agent writes a decision, Ralph promotes it - 1. **Data** makes an architecture choice and writes a note: ```bash git notes --ref=squad/data add -m \ @@ -356,17 +262,13 @@ See `.squad/notes-protocol.md` for the full contract. HEAD git push origin 'refs/notes/*:refs/notes/*' ``` - 2. **PR merges** into the default branch. - 3. **Ralph** runs promotion on the next watch cycle: - Fetches all notes - Finds Data's note with `promote_to_permanent: true` on a merged commit - Appends the decision to `decisions.md` via the state backend - Notes on rejected PRs are silently ignored - ### Template files - When `stateBackend` is set to `two-layer` or `orphan`, the following templates are available: | Template | Purpose | @@ -376,7 +278,6 @@ When `stateBackend` is set to `two-layer` or `orphan`, the following templates a | `scripts/notes/write-note.ps1` | Agent helper — handles JSON, conflicts, push | ### Automatic Coordinator Integration - **You don't need to manually add copilot-instructions.md snippets.** When `stateBackend` is set in `.squad/config.json`, the Squad coordinator (`squad.agent.md`) automatically adapts its agent spawn prompts: | Backend | Agent reads | Agent writes | Scribe commits to | @@ -386,107 +287,71 @@ When `stateBackend` is set to `two-layer` or `orphan`, the following templates a | `two-layer` | Git notes + orphan branch | Git notes via `write-note.ps1` + orphan | Pushes note refs + orphan branch | **Config vs State distinction:** + - **Static config** (charters, team.md, routing.md, casting/) — always on disk, all backends - **Mutable state** (history.md, decisions/inbox/, logs, orchestration-log/) — backend-dependent - The coordinator passes `STATE_BACKEND` into every agent spawn prompt. Agents receive backend-specific instructions for reading and writing state. Scribe receives backend-specific commit instructions. This is fully automatic — no user configuration beyond setting `stateBackend` in config.json is needed. --- - ## Migrating an Existing Squad - Use `squad upgrade` to migrate — it handles everything: - ```bash squad upgrade --state-backend two-layer # or: squad upgrade --state-backend orphan ``` - This will: 1. Update `.squad/config.json` with the new backend 2. Create the `squad-state` orphan branch (if needed) 3. Install git hooks for automatic sync 4. Preserve all existing state - **What happens:** Existing `.squad/` files are migrated to the orphan branch and may be removed from the working tree on subsequent commits. New decisions and state writes go to the orphan branch (and git notes for two-layer). The pre-commit hook prevents you from accidentally re-committing mutable state files into the working tree. - ### Switching between orphan and two-layer - Change `stateBackend` in `.squad/config.json`. The coordinator adapts on the next session. Both use the `squad-state` orphan branch, so existing state is preserved. Two-layer additionally enables git notes for commit-scoped annotations. --- - ## Steady-state safety net - Once you've migrated to `orphan` or `two-layer`, two additional git hooks enforce the invariant that mutable state never lands in your working branch. - ### `pre-commit` — blocks state from entering the working tree - Before every commit, this hook scans the staged index for files that belong on the orphan branch: - - `.squad/decisions.md` - `.squad/agents/*/history.md` - `.squad/casting/` - `.squad/routing/` - If any of those paths are staged, the commit is refused with: - ``` ⚠ squad pre-commit: refusing to commit two-layer state into the working tree. Unstage the state files and let the post-commit hook sync them: git restore --staged .squad/decisions.md .squad/agents/*/history.md ``` - **Why files might reappear:** A tool, editor save, or agent code path that writes directly via `fs.writeFile` (bypassing `StateBackend`) will recreate the file on disk. Staging it and attempting a commit triggers this hook. - For the full recovery flow see [troubleshooting](#squad-pre-commit-refusing-to-commit-two-layer-state-into-the-working-tree). - ### `post-commit` — keeps the orphan branch current - After every successful commit, `squad sync --quiet` is called automatically. This pushes any pending state from the in-memory queue onto the `squad-state` branch, so the orphan branch stays up to date without manual intervention. - ### `SQUAD_SYNC_ACTIVE=1` bypass - Setting `SQUAD_SYNC_ACTIVE=1` in the environment causes both hooks to exit immediately without running. This is used **internally** by `squad sync` itself to avoid hook recursion. - > ⚠️ **Do not use `SQUAD_SYNC_ACTIVE=1` routinely.** Bypassing the pre-commit hook lets state files land in your working branch commits — exactly the situation `two-layer` is designed to prevent. Any PR created from that branch will carry squad state in the diff, defeating the clean-PR promise of the two-layer backend. Use the recovery flow instead. - ## Troubleshooting - ### "Pre-commit hook refused my commit" - **Cause:** You staged `.squad/` files that belong on the `squad-state` orphan branch (decisions.md, agent histories, casting/, routing/). The pre-commit hook blocks these to keep mutable state off your working-tree branches. - **Fix:** - ```bash # Unstage the offending paths git restore --staged .squad/decisions.md .squad/agents/*/history.md .squad/casting/ .squad/routing/ - # Then commit normally — only your code changes go through git commit ``` - **If you need to bypass** (e.g., during initial migration or manual repair): - ```bash SQUAD_SYNC_ACTIVE=1 git commit -m "manual state repair" ``` - > ⚠️ Only bypass when you understand why — the hook exists to prevent state from leaking into PRs. - ### "My state disappeared after switching branches" - **Cause:** You're using the default `local` backend. State files are branch-local. - **Fix:** Switch to `orphan` or `two-layer` backend. Both persist state across branches: - Orphan: state lives on a dedicated branch (accessible via `git show squad-state:`) - Two-layer: orphan branch + git notes for commit-scoped annotations - ### "State files are showing up in my PR" - **Cause:** Using `local` backend, or an agent accidentally committed state files on orphan/two-layer backend. - **Fix:** 1. If using local backend: switch to `orphan` or `two-layer` 2. If using orphan/two-layer: Scribe's State Leak Guard should catch this automatically. If it missed: @@ -494,11 +359,8 @@ SQUAD_SYNC_ACTIVE=1 git commit -m "manual state repair" git reset HEAD -- .squad/decisions.md .squad/agents/*/history.md .squad/log/ .squad/orchestration-log/ git checkout HEAD -- .squad/decisions.md .squad/agents/*/history.md ``` - ### "Orphan branch doesn't exist" - **Cause:** The `squad-state` branch hasn't been created yet. - **Fix:** Create it manually: ```bash git checkout --orphan squad-state @@ -507,45 +369,28 @@ mkdir .squad && echo "# Squad State" > .squad/README.md git add .squad/ && git commit -m "init: squad-state orphan branch" git checkout main ``` - Scribe will auto-create it on the next session if it doesn't exist (via git plumbing: `mktree`, `commit-tree`, `update-ref`). - ### "Git notes not found on root commit" - **Cause:** The agent wrote the note to HEAD instead of the root commit. - **Known issue:** Some agents write notes to the current HEAD instead of `$(git rev-list --max-parents=0 HEAD)`. The note still exists on the ref and is readable, but the root-commit anchor pattern isn't being followed precisely. - **Workaround:** The note is still accessible via `git notes --ref=squad/{agent} show {commit-sha}`. The ref itself (`refs/notes/squad/{agent}`) is visible from all branches regardless of which commit the note is on. - ### "Config.json doesn't have stateBackend" - **This is fine.** The default is `local` — the current behavior. No config change needed unless you want a different backend. --- - ## Multi-User Synchronization - When multiple team members work on the same repo with Squad, the state backend determines how state stays in sync. - ### Local backend - Each user has their own `.squad/` files in the working tree. If committed, they merge like any other files — which means **merge conflicts are common** when two people modify decisions or histories simultaneously. This is the main reason teams choose orphan or two-layer backends. - ### Orphan backend - The `squad-state` branch is a normal Git branch. Synchronization works like any other branch: - ```bash # Before a squad session — pull latest state git fetch origin squad-state:squad-state - # After a squad session — push your state changes git push origin squad-state ``` - **Conflict handling:** If two users push to `squad-state` simultaneously, the second push will be rejected (non-fast-forward). Resolution: - ```bash git fetch origin squad-state:squad-state git checkout squad-state @@ -553,110 +398,71 @@ git merge origin/squad-state # resolve conflicts, then: git push origin squad-state git checkout main ``` - In practice, Squad's watch loop handles this automatically — Scribe's commit logic retries on push failure. - > **Tip:** For teams, consider protecting the `squad-state` branch with GitHub branch protection rules that allow force-push from the CI bot but require linear history from humans. - ### Two-layer backend - Two-layer uses **both** the orphan branch (same as above) **and** git notes. Notes are per-commit annotations that travel as refs: - ```bash # Fetch notes from the remote git fetch origin 'refs/notes/*:refs/notes/*' - # Push notes to the remote git push origin 'refs/notes/*:refs/notes/*' ``` - **Why this is team-safe:** Notes are scoped to individual commits — there are no merge conflicts because each commit has its own annotation namespace. The orphan branch stores the aggregated permanent state, and Ralph promotes note data to it after PRs merge. - ### Automatic fetch in `squad watch` - When `squad watch` starts, it automatically: 1. Fetches the `squad-state` branch (if orphan or two-layer) 2. Fetches `refs/notes/*` (if two-layer) 3. On each watch cycle, pushes any state changes back - **No manual sync is needed** when using `squad watch`. Manual sync is only needed if you're running one-off squad commands outside of watch mode. - ### Git config for automatic notes fetch - To make `git pull` automatically fetch notes, add this to `.git/config` (or use the setup script): - ```bash # One-time setup per clone git config --add remote.origin.fetch '+refs/notes/*:refs/notes/*' ``` - After this, every `git fetch origin` includes notes automatically. --- - ## FAQ - ### What's the default state backend? - **`local`**. If you don't set `stateBackend` in `.squad/config.json`, Squad stores state as regular files in `.squad/` on your working branch. This is the simplest setup — no extra configuration needed. - ### When should I switch away from `local`? - Switch when any of these apply: - Your PRs are cluttered with `.squad/` file changes - You lose state when switching branches - Multiple team members are getting merge conflicts on `.squad/` files - You want squad state to be invisible in code reviews - ### Why would I choose `orphan` over `two-layer`? - **Choose `orphan` when you want simplicity.** It stores all state on a single dedicated branch. Easy to understand, inspect, and debug. One branch, one source of truth. - **Choose `two-layer` when you need commit-scoped context.** Two-layer adds git notes — annotations attached to specific commits. This means: - A decision made on commit `abc123` stays linked to that commit - Ralph can decide whether to promote or discard decisions based on whether the PR was merged or rejected - Research notes on a rejected PR are automatically ignored (not promoted) - **Bottom line:** `orphan` is for teams who just want clean PRs. `two-layer` is for teams who want intelligent state lifecycle management (decisions that survive or die with their PRs). - ### Can I use `orphan` and later upgrade to `two-layer`? - Yes. Both use the same `squad-state` orphan branch for permanent state. Switching from `orphan` to `two-layer` simply enables the additional git notes layer. Your existing state is fully preserved. - ### What happens if two people run Squad simultaneously? - - **Local backend:** File-level merge conflicts when both push (just like any Git merge conflict). - **Orphan backend:** The second push to `squad-state` fails with a non-fast-forward error. Squad's watch loop retries automatically. In the worst case, you manually merge the branch. - **Two-layer backend:** Notes are per-commit, so they never conflict. The orphan branch layer has the same retry behavior as the orphan backend. - ### Does the `squad-state` branch show up in my PRs? - No. The `squad-state` branch is an **orphan branch** — it has no common ancestor with your main branch. GitHub doesn't include it in PR diffs. It's completely invisible in code reviews. - ### How do I inspect state on the orphan branch? - ```bash # List all state files git ls-tree --name-only -r squad-state - # Read a specific file git show squad-state:decisions.md - # View state history git log --oneline squad-state ``` - ### Does this work with GitHub Actions / CI? - Yes. If your CI/CD workflow needs to read squad state: - **Orphan backend:** `git fetch origin squad-state && git show squad-state:` - **Two-layer:** Same as orphan, plus `git fetch origin 'refs/notes/*:refs/notes/*'` for notes - **Local backend:** State is on the working branch — just read `.squad/` files directly - ### What if I forget to push the `squad-state` branch? - State stays local to your machine. Other team members won't see your latest decisions or agent histories until you push. This is no different from forgetting to push any other branch — Git is distributed, and state only syncs when you push/fetch. - ### Can the `squad-state` branch be deleted safely? - **No.** Deleting it loses all permanent squad state (decisions, agent histories, logs). Treat it like your main branch — push it to the remote and don't delete it. You can recover from a local deletion by re-fetching from the remote: `git fetch origin squad-state:squad-state`. diff --git a/docs/src/content/docs/scenarios/team-state-storage.md b/docs/src/content/docs/scenarios/team-state-storage.md index a2dbe4057..21aae3d59 100644 --- a/docs/src/content/docs/scenarios/team-state-storage.md +++ b/docs/src/content/docs/scenarios/team-state-storage.md @@ -1,329 +1,187 @@ -# Keeping Your Squad Where You Want It +# Keeping Your Squad State Where You Want It -Your `.ai-team/` directory contains everything—team rosters, skills, decisions, agent histories. The question isn't whether to track it, but *how* and *where* to track it. Here are the real options, with honest tradeoffs. +Your `.squad/` directory contains everything — team rosters, skills, decisions, agent histories. The question isn't whether to track it, but *where* to track it. Squad gives you three storage backends, each suited to different team sizes and workflows. --- -## 1. Committed to Main (The Default) +## Quick Decision -**What it is:** `.ai-team/` is tracked in git, committed alongside your code. Anyone who clones the repo gets the full team with all accumulated knowledge. +| Situation | Recommended backend | +|---|---| +| Solo dev or small team, want simple setup | **local** (default) | +| Want a clean working tree, no PR noise | **orphan** | +| Team with concurrent writers, want full safety | **two-layer** | -```bash -git add .ai-team/ -git commit -m "Add Squad team" -``` - -### Pros - -- **Simplest setup.** No configuration, no branching strategy. -- **Portable.** Clone the repo anywhere, and the team knowledge travels with it. -- **Shared context.** Every collaborator sees the same team definitions, skills, and decisions. -- **Git history.** You can trace how decisions evolved, view old skills, recover deleted files. -- **GitHub Actions work out of the box.** Workflows (heartbeat, triage, label sync) access `.ai-team/` immediately. - -### Cons - -- **PR noise.** Every team change—new skill, updated decision, agent history—shows up in PR diffs. Some people find this distracting. -- **`decisions.md` grows.** Over time, your decisions file accumulates hundreds of entries. Git history is there, but the current file gets long. -- **Some orgs don't allow it.** Enterprise policies sometimes forbid AI artifacts in source repositories. Check before you commit. - -### When to Use This - -- Solo dev or small team, private repo. -- Open source project—your contributors should see how the team works. -- You want maximum portability and zero configuration. - ---- - -## 2. Gitignored (Local-Only) - -**What it is:** Add `.ai-team/` to `.gitignore`. Team state lives locally on each dev machine, never committed. - -```bash -echo ".ai-team/" >> .gitignore -git add .gitignore -git commit -m "Gitignore squad team state" -``` - -### Pros - -- **Zero repo noise.** No PR diffs, no git history clutter. -- **No policy concerns.** Enterprise orgs with AI artifact policies sleep easy. -- **Clean main branch.** Code and team are completely separated. - -### Cons - -- **Team knowledge is not portable.** If you delete `.ai-team/`, it's gone. No git history to recover it. -- **Collaborators don't share state.** Your teammate clones the repo and gets a fresh, empty `.ai-team/`. Their team doesn't match yours. -- **No git history for recovery.** You can't `git log` to find an old decision or see when a skill was added. -- **⚠️ GitHub Actions workflows can't access `.ai-team/`.** Actions only see committed files. Triage routing rules in `team.md` won't work in CI/CD. (Label sync and other API-based workflows still function, but the team-based routing logic is silent.) - -### When to Use This - -- Team doesn't need shared state (unlikely for Squad). -- Enterprise policy strictly forbids AI artifacts in repos. -- You're experimenting and don't want to commit yet. +Configure your backend once during `squad init` or `squad upgrade`. The choice is stored in `.squad/config.json` — you never pass it again. --- -## 3. Separate Branch (e.g., `squad-state`) - -**What it is:** Keep `.ai-team/` on a dedicated branch (`squad-state`, `team-config`, etc.), not on `main`. Use `git worktree` to mount it locally. +## Setting Your Backend -### Setup +### New project ```bash -# Create and push the squad-state branch (if it doesn't exist) -git checkout --orphan squad-state -git rm -rf . -echo "# Squad State Branch\nThis branch tracks .ai-team/ configuration." > README.md -git add README.md -git commit -m "Initial squad-state branch" -git push origin squad-state +# Default — state files in .squad/ in the working tree +squad init -# Back on main -git checkout main +# Orphan branch — state on a dedicated squad-state branch +squad init --state-backend orphan -# Mount squad-state in a worktree -git worktree add .ai-team-worktree squad-state -ln -s .ai-team-worktree/.ai-team .ai-team -git add .gitignore -echo ".ai-team-worktree/" >> .gitignore -git commit -m "Add squad worktree" +# Two-layer — recommended for teams with concurrent writers +squad init --state-backend two-layer ``` -On Windows: +### Existing project ```bash -# Use mklink instead of ln -s (requires admin or Developer Mode) -git worktree add .ai-team-worktree squad-state -mklink /D .ai-team .ai-team-worktree\.ai-team +# Migrate from local to a different backend +squad upgrade --state-backend two-layer +# or: squad upgrade --state-backend orphan ``` -### Pros - -- **Clean main branch.** `.ai-team/` never appears in `main` or in PR diffs. -- **Full git history.** The `squad-state` branch has complete history of all team changes. -- **Shareable with collaborators.** They can check out `squad-state` and pull your team setup. -- **GitHub Actions can access it.** Workflows can check out both `main` and `squad-state` if needed. - -### Cons - -- **Complex setup.** Requires knowledge of `git worktree` and branch management. -- **Merge conflicts.** If multiple people work on `squad-state` simultaneously, conflicts happen. -- **Worktree management overhead.** You need to remember to update the worktree, and it can get stale. -- **Collaborators must set up the worktree.** They can't just clone; they need to run the setup commands. - -### When to Use This - -- Team that values clean main branch but wants shared team state. -- You want full git history of team evolution but don't want PR noise. -- You're already comfortable with `git worktree` or branching strategies. +The migration moves your existing state, creates the orphan branch (if applicable), and installs git hooks automatically. --- -## 4. Git Submodule +## The Three Backends -**What it is:** `.ai-team/` as a separate Git repository, added as a submodule to your main repo. +### Local (default) -### Setup - -```bash -# Create a separate repository for your squad (e.g., on GitHub) -# Then add it as a submodule -git submodule add https://github.com/you/my-squad-state .ai-team -git commit -m "Add squad state as submodule" -git push -``` +State lives as regular files in `.squad/` inside your working tree — the same directory structure you see when you `ls`. -Collaborators clone with: +**Pros:** +- Simple and familiar — files on disk +- Easy to inspect, edit, and commit +- Works with all Git tools and IDEs -```bash -git clone --recurse-submodules https://github.com/you/my-project -``` +**Cons:** +- Files show up in `git status` and PR diffs +- Branch switches can lose uncommitted state +- Multiple team members modifying `.squad/` causes merge conflicts -Or after cloning normally: +**Best for:** Most solo projects, especially when you want squad state committed alongside your code for portability. -```bash -git submodule init -git submodule update +```json +// .squad/config.json +{ + "stateBackend": "local" +} ``` -### Pros - -- **Completely separate history.** The submodule repo has its own git log, independent of your main project. -- **Shareable across repos.** Use the same submodule in multiple projects. -- **Clean main branch.** `.ai-team/` is external; no PR diffs in your project. -- **Full git features.** Submodule repo has branches, tags, and full history. - -### Cons - -- **Submodules are complex.** Widely disliked by the git community. Conflicts, merge issues, and confusion are common. -- **Everyone must remember `--recurse-submodules`.** Collaborators who forget get an empty `.ai-team/` directory. -- **CI/CD needs extra setup.** Your workflows must initialize submodules explicitly: - ```bash - git submodule init && git submodule update - ``` -- **Updating the submodule can cause conflicts.** If two people push to the submodule simultaneously, merging back is painful. - -### When to Use This - -- You're already using submodules elsewhere in your org (they're familiar with the pain). -- You want to share the same squad configuration across 3+ repositories. -- Your team is comfortable with advanced git workflows. - -**Honest take:** Submodules work, but the git community almost universally dislikes them. They're powerful tools for specific use cases, but most teams regret using them. Only reach for submodules if you truly need them. - --- -## 5. Symlink to External Directory +### Orphan Branch -**What it is:** Keep `.ai-team/` somewhere else on your filesystem (e.g., `~/my-squads/my-project-squad/`), then symlink it into your repo. +State lives on a dedicated orphan branch (`squad-state` by default). This branch has no common history with your main branches — it's a separate tree used only for squad data. -### Setup +**How it works:** +- An orphan branch `squad-state` is created automatically on first write +- All state reads use `git show squad-state:`; writes create new commits on the branch +- The branch is never checked out — all operations use Git plumbing commands +- Git hooks (installed automatically) keep the branch in sync when you push/pull -On macOS/Linux: +**Pros:** +- Working tree stays clean — no `.squad/` clutter in your diffs +- State is versioned with full Git history +- Easy to inspect: `git log squad-state` or `git show squad-state:decisions.md` -```bash -mkdir -p ~/my-squads/my-project-squad -ln -s ~/my-squads/my-project-squad .ai-team -``` +**Cons:** +- An extra branch in the repository +- Concurrent writes from multiple people can conflict (single-writer workflows work best) -On Windows (requires admin or Developer Mode): +**Best for:** Solo or small-team projects where you want a clean working tree. -```bash -mkdir C:\Users\you\my-squads\my-project-squad -mklink /D .ai-team C:\Users\you\my-squads\my-project-squad -``` - -Add `.ai-team` to `.gitignore`: - -```bash -echo ".ai-team" >> .gitignore +```json +// .squad/config.json +{ + "stateBackend": "orphan" +} ``` -### Pros - -- **Share state across repos.** Point multiple projects to the same squad directory. -- **No git noise.** The symlink itself isn't tracked; `.ai-team/` is ignored. -- **Maximum flexibility.** You can move the squad, reorganize it, or swap it out. - -### Cons - -- **Not portable.** Symlinks are machine-specific. Collaborators need the exact same filesystem layout or the symlink breaks. -- **Windows compatibility is fragile.** Symlinks on Windows require admin privileges or Developer Mode; many orgs disable this. -- **Easy to break.** If the external directory is deleted, the symlink points to nothing. -- **No git history.** Team state changes aren't tracked in your project repo; you're on your own for backups. - -### When to Use This - -- You maintain multiple repositories with the same squad. -- Everyone on your team has the same filesystem layout (rare in practice). -- You're on macOS/Linux and control your development environment. - -**Caveat:** This breaks for most teams sharing code. Collaborators' symlinks will be broken, external contractors can't participate, and CI/CD usually fails. - --- -## 6. Dev Branch Only (The Squad Project's Own Approach) +### Two-Layer (recommended for teams) -**What it is:** `.ai-team/` is committed, but *only* on dev/feature branches. On `main`, it's gitignored. When you create a feature branch, you remove `.ai-team/` from `.gitignore` so the team travels with your work. +Combines an orphan branch for durable file-per-state storage with git notes for best-effort commit annotations. This is the team-safe option. -### Setup +**How it works:** +- Durable squad state (decisions, agent histories, casting, routing) lives on the orphan branch with per-file granularity — fully mergeable by Git +- Git notes on commits provide lightweight "why this commit" annotations +- Git hooks prevent accidentally staging squad state into a working-tree commit and flush pending state after each commit -On `main`: +**Pros:** +- Working tree stays clean +- Concurrent writers are safe — per-file granularity means Git can merge state from multiple contributors +- Automatic sync via hooks: push/pull keeps the orphan branch in sync -```bash -echo ".ai-team/" >> .gitignore -git add .gitignore -git commit -m "Ignore squad team on main" -``` +**Cons:** +- Slightly more complex to debug +- Requires the orphan branch to be pushed to the remote for team sharing -When you start a feature branch: +**Best for:** Teams with multiple developers working simultaneously on the same repo. -```bash -git checkout -b feature/my-feature -git rm .ai-team/ # if it exists from a previous branch -# Remove .ai-team/ from .gitignore -git edit .gitignore -# (remove the .ai-team/ line) -git add .gitignore -git commit -m "Track squad team on this branch" +```json +// .squad/config.json +{ + "stateBackend": "two-layer" +} ``` -Agents work with the full `.ai-team/` context while you develop. When you merge back to `main`, the PR shows the `.ai-team/` changes, but `main` stays clean. +> **Note on the deprecated `git-notes` backend:** If your config still references `git-notes`, Squad automatically migrates it to `two-layer` at runtime. The standalone git-notes backend stored all state as a single JSON blob and could not handle concurrent writes; the two-layer backend replaces it. -### Pros - -- **Clean main branch.** `main` is pure code, no squad artifacts. -- **Full context on feature branches.** Agents have the team history while you work. -- **Git history preserved.** Team changes are committed on feature branches and visible in git log. -- **Collaborators get team state.** Anyone checking out your feature branch gets `.ai-team/`. -- **GitHub Actions can work both ways.** On `main`, workflows use GitHub API (label sync, heartbeat). On feature branches, they can use team-based routing if needed. +--- -### Cons +## What Gets Installed Automatically -- **Merge conflicts when syncing branches.** If `main` has `.ai-team/` gitignored but your branch commits it, merging is messy. -- **Easy to forget the pattern.** Developers forget to remove `.ai-team/` from `.gitignore` when creating feature branches (or forget to add it back when switching back to `main`). -- **PR diffs include team changes.** PRs from feature branches show all `.ai-team/` modifications, which some teams find noisy. +When you choose `orphan` or `two-layer`, Squad installs Git hooks in `.git/hooks/`: -### When to Use This +| Hook | Purpose | +|---|---| +| `pre-push` | Syncs orphan branch state before push | +| `post-merge` | Pulls orphan branch state after merge | +| `post-checkout` | Refreshes state when you switch branches | +| `post-rewrite` | Syncs after rebase or amend | +| `pre-commit` | Guards against accidentally staging two-layer mutable state | +| `post-commit` | Flushes pending two-layer state to the orphan branch | -- Small team that's aware of the pattern. -- You want clean main but team context on feature branches. -- You're OK with remembering to toggle `.gitignore` per branch. +Hooks chain with existing hooks (husky, etc.) — nothing is overwritten. --- -## Decision Matrix +## Inspecting and Debugging Your State -| Scenario | Option | Why | -|----------|--------|-----| -| Solo dev, private repo | **1. Committed** | Simplest, portable, full history | -| Team, shared state, no PR concerns | **1. Committed** | Everyone gets same team | -| Team, clean main, no Actions workflows | **2. Gitignored** | No policy issues, no PR noise | -| Team, clean main, need Actions workflows | **3. Separate Branch** | Full history, shared state, Actions can access it | -| Multiple repos, same squad | **4. Submodule** or **5. Symlink** | Submodule if you need git; symlink if portable | -| Enterprise, AI artifact policy | **2. Gitignored** or **4. Submodule** | Keep AI stuff out of main repo | -| Open source | **1. Committed** | Contributors should see how the team works | +```bash +# See which backend you're using +cat .squad/config.json ---- +# Orphan or two-layer: inspect state branch +git log squad-state --oneline -10 +git show squad-state:decisions.md -## Tips +# Check installed hooks +ls .git/hooks/ -- **GitHub Actions and Gitignored `.ai-team/`:** If you choose option 2 (gitignore), remember that Actions workflows see committed files only. Label sync and heartbeat workflows (which use GitHub API) still work. But `squad.agent.md` triage rules won't see `.ai-team/decisions.md` during automated runs. Workaround: Copy critical decisions to a committed file or pass them as workflow env vars. -- **Merge conflicts on `decisions.md`:** If multiple people are committing to `.ai-team/` at the same time, `decisions.md` and agent histories conflict frequently. Use the `.gitattributes merge=union` rules that Squad sets up. Check the file after merge to ensure it looks reasonable. -- **Backup your team.** If you're gitignoring `.ai-team/`, make sure you have backups. A deleted `.ai-team/` directory with no git history is gone forever. -- **Communicate the pattern to your team.** Whatever you choose, document it. Add a line to your `CONTRIBUTING.md` or `README.md` explaining where the squad lives and how to interact with it. -- **Start simple, migrate later.** Commit `.ai-team/` initially (option 1). If PR noise becomes a real problem, migrate to option 2 or 3. Changing strategies later is possible but requires care. +# Migrate to a different backend +squad upgrade --state-backend two-layer +``` --- -## Sample Prompts - -Use these prompts with Squad to implement specific strategies: +## Sharing State with Your Team -- **"Keep .ai-team/ out of my main branch."** - - Directs you toward option 3 (separate branch) or option 6 (dev-only). +For `orphan` and `two-layer`, push the orphan branch to your remote so teammates can access it: -- **"I want to share my squad across three repos without duplicating the team state."** - - Points to option 4 (submodule) or option 5 (symlink). - -- **"Add .ai-team to .gitignore but make sure GitHub Actions can still route based on team.md."** - - Hybrid: gitignore but keep a committed `squad-routing.md` that Actions reads. +```bash +git push origin squad-state +``` -- **"My enterprise doesn't allow AI artifacts in the main repository."** - - Option 2 (gitignore) or option 4 (submodule in a separate org-controlled repo). +Team members who clone the repo or run `squad upgrade` will pull down the state branch and install hooks automatically. -- **"I deleted .ai-team by accident. How do I recover it?"** - - If committed: `git checkout HEAD~5 .ai-team/` (restore from history). - - If gitignored: No recovery from git. Restore from backup or rebuild the team. +For `local`, the standard approach is to commit `.squad/` alongside your code (or gitignore it for local-only state). There is no separate sync mechanism for the local backend. --- -## See Also +## Related -- **[Adding Squad to an Existing Repo](existing-repo.md)** — How to integrate Squad into a project with existing code. -- **[Squad for Solo Developers](solo-dev.md)** — Building alone? Here's how Squad becomes your team. -- **[Multiple Squads](multiple-squads.md)** — Managing more than one AI team. -- **[Team Portability](team-portability.md)** — Moving your squad to a new repo or machine. +- [State Backends](../features/state-backends.md) — full reference for backend configuration options +- [External State](../features/external-state.md) — cloud and remote storage options