@bradygaster/squad-cli 0.9.5-insider.2 → 0.9.6-insider.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/cli/commands/install-hooks.d.ts +26 -0
  2. package/dist/cli/commands/install-hooks.d.ts.map +1 -0
  3. package/dist/cli/commands/install-hooks.js +244 -0
  4. package/dist/cli/commands/install-hooks.js.map +1 -0
  5. package/dist/cli/commands/migrate-backend.d.ts +16 -0
  6. package/dist/cli/commands/migrate-backend.d.ts.map +1 -0
  7. package/dist/cli/commands/migrate-backend.js +87 -0
  8. package/dist/cli/commands/migrate-backend.js.map +1 -0
  9. package/dist/cli/commands/sync.d.ts +24 -0
  10. package/dist/cli/commands/sync.d.ts.map +1 -0
  11. package/dist/cli/commands/sync.js +257 -0
  12. package/dist/cli/commands/sync.js.map +1 -0
  13. package/dist/cli/commands/watch/config.d.ts +4 -1
  14. package/dist/cli/commands/watch/config.d.ts.map +1 -1
  15. package/dist/cli/commands/watch/config.js +2 -1
  16. package/dist/cli/commands/watch/config.js.map +1 -1
  17. package/dist/cli/core/init.d.ts +2 -0
  18. package/dist/cli/core/init.d.ts.map +1 -1
  19. package/dist/cli/core/init.js +63 -3
  20. package/dist/cli/core/init.js.map +1 -1
  21. package/dist/cli-entry.js +27 -5
  22. package/dist/cli-entry.js.map +1 -1
  23. package/package.json +6 -5
  24. package/templates/notes-protocol.md +202 -0
  25. package/templates/scribe-charter.md +95 -1
  26. package/templates/scripts/notes/fetch.ps1 +88 -0
  27. package/templates/scripts/notes/write-note.ps1 +126 -0
  28. package/templates/skills/cross-machine-coordination/SKILL.md +8 -0
  29. package/templates/skills/init-mode/SKILL.md +3 -3
  30. package/templates/skills/model-selection/SKILL.md +8 -0
  31. package/templates/skills/nap/SKILL.md +8 -0
  32. package/templates/skills/personal-squad/SKILL.md +8 -0
  33. package/templates/skills/ralph-two-pass-scan/SKILL.md +8 -0
  34. package/templates/skills/release-process/SKILL.md +91 -5
  35. package/templates/squad.agent.md.template +137 -14
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env pwsh
2
+ # scripts/notes/write-note.ps1
3
+ # ─────────────────────────────────────────────────────────────────────────────
4
+ # Helper for agents to write notes without wrestling with JSON escaping.
5
+ # Validates namespace ownership, handles conflicts, pushes automatically.
6
+ #
7
+ # Usage:
8
+ # ./scripts/notes/write-note.ps1 -Agent data -Type decision \
9
+ # -Content '{"decision":"Use JWT","reasoning":"..."}' \
10
+ # [-Commit HEAD] [-Promote] [-Archive]
11
+ # ─────────────────────────────────────────────────────────────────────────────
12
+
13
+ [CmdletBinding()]
14
+ param(
15
+ [Parameter(Mandatory)][string]$Agent,
16
+
17
+ [Parameter(Mandatory)]
18
+ [ValidateSet("decision","research","review","security-review","progress",
19
+ "api-contract","risk-assessment","routing-discovery","counter-argument")]
20
+ [string]$Type,
21
+
22
+ [Parameter(Mandatory)]
23
+ [string]$Content, # JSON object with type-specific fields
24
+
25
+ [string]$Commit = "HEAD",
26
+ [string]$RepoPath = ".",
27
+ [string]$Remote = "origin",
28
+ [switch]$Promote, # set promote_to_permanent: true
29
+ [switch]$Archive, # set archive_on_close: true
30
+ [switch]$NoPush, # skip auto-push
31
+ [switch]$Quiet
32
+ )
33
+
34
+ function Log ([string]$msg, [string]$color = "White") {
35
+ if (-not $Quiet) { Write-Host "[notes/write] $msg" -ForegroundColor $color }
36
+ }
37
+
38
+ $repo = Resolve-Path $RepoPath
39
+ $namespace = "squad/$($Agent.ToLower())"
40
+
41
+ # ── Validate JSON content ────────────────────────────────────────────────────
42
+ try {
43
+ $parsed = $Content | ConvertFrom-Json -ErrorAction Stop
44
+ } catch {
45
+ Write-Error "Content must be valid JSON. Got: $Content"
46
+ exit 1
47
+ }
48
+
49
+ # ── Build full note object ────────────────────────────────────────────────────
50
+ $note = [ordered]@{
51
+ agent = (Get-Culture).TextInfo.ToTitleCase($Agent.ToLower())
52
+ timestamp = [System.DateTime]::UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")
53
+ type = $Type
54
+ }
55
+
56
+ # Merge content fields into note
57
+ $parsed.PSObject.Properties | ForEach-Object { $note[$_.Name] = $_.Value }
58
+
59
+ # Add flag fields
60
+ if ($Promote) { $note["promote_to_permanent"] = $true }
61
+ if ($Archive) { $note["archive_on_close"] = $true }
62
+
63
+ $noteJson = $note | ConvertTo-Json -Compress -Depth 10
64
+
65
+ # ── Fetch first to avoid conflicts ───────────────────────────────────────────
66
+ Log "Fetching notes before write..."
67
+ git -C $repo fetch $Remote "refs/notes/*:refs/notes/*" 2>&1 | Out-Null
68
+
69
+ # ── Check if note already exists on this commit ─────────────────────────────
70
+ $existing = git -C $repo notes --ref=$namespace show $Commit 2>&1
71
+ $useAppend = ($LASTEXITCODE -eq 0)
72
+
73
+ if ($useAppend) {
74
+ Log "Note exists on $Commit — appending" DarkYellow
75
+ git -C $repo notes --ref=$namespace append -m $noteJson $Commit
76
+ } else {
77
+ git -C $repo notes --ref=$namespace add -m $noteJson $Commit
78
+ }
79
+
80
+ if ($LASTEXITCODE -ne 0) {
81
+ Write-Error "Failed to write note to refs/notes/$namespace on $Commit"
82
+ exit 1
83
+ }
84
+
85
+ Log "Note written to refs/notes/$namespace on $($Commit.Substring(0,[Math]::Min(8,$Commit.Length)))" Green
86
+
87
+ # ── Push with retry ──────────────────────────────────────────────────────────
88
+ if (-not $NoPush) {
89
+ $maxRetries = 5
90
+ $nsRef = "refs/notes/$namespace"
91
+
92
+ for ($i = 0; $i -lt $maxRetries; $i++) {
93
+ Log "Pushing notes (attempt $($i+1))..."
94
+ $pushOut = git -C $repo push $Remote "${nsRef}:${nsRef}" 2>&1
95
+ if ($LASTEXITCODE -eq 0) {
96
+ Log "Notes pushed successfully." Green
97
+ break
98
+ }
99
+
100
+ if ($pushOut -match "non-fast-forward|fetch first|rejected") {
101
+ Log "Push conflict — fetch-first retry..." DarkYellow
102
+
103
+ # Force-fetch: overwrite local ref with current remote state
104
+ git -C $repo fetch $Remote "${nsRef}:${nsRef}" 2>&1 | Out-Null
105
+
106
+ # Re-append our note on top of the now-current remote state
107
+ git -C $repo notes --ref=$namespace append -m $noteJson $Commit 2>&1 | Out-Null
108
+
109
+ $jitter = Get-Random -Minimum 0 -Maximum 1000
110
+ $sleep = [Math]::Pow(2, $i) + $jitter / 1000
111
+ Start-Sleep -Seconds $sleep
112
+
113
+ } else {
114
+ Log "Push error: $pushOut" Red
115
+ if ($i -eq $maxRetries - 1) {
116
+ Write-Warning "Failed after $maxRetries retries. Push manually: git push origin '${nsRef}:${nsRef}'"
117
+ }
118
+ }
119
+ }
120
+ }
121
+
122
+ # ── Show result ───────────────────────────────────────────────────────────────
123
+ if (-not $Quiet) {
124
+ Log "Note content:"
125
+ $note | ConvertTo-Json -Depth 5 | Write-Host -ForegroundColor DarkGray
126
+ }
@@ -1,3 +1,11 @@
1
+ ---
2
+ name: "cross-machine-coordination"
3
+ description: "Enables squad agents on different machines to share work via git-based task queuing"
4
+ domain: "orchestration"
5
+ confidence: "medium"
6
+ source: "manual"
7
+ ---
8
+
1
9
  # Skill: Cross-Machine Coordination Pattern
2
10
 
3
11
  **Skill ID:** `cross-machine-coordination`
@@ -20,7 +20,7 @@ Init Mode activates when `.squad/team.md` does not exist, or exists but has zero
20
20
 
21
21
  No team exists yet. Propose one — but **DO NOT create any files until the user confirms.**
22
22
 
23
- 1. **Identify the user.** Run `git config user.name` to learn who you're working with. Use their name in conversation (e.g., *"Hey Brady, what are you building?"*). Store their name (NOT email) in `team.md` under Project Context. **Never read or store `git config user.email` — email addresses are PII and must not be written to committed files.**
23
+ 1. **Identify the user.** Run `git config user.name` to learn who you're working with. Use their name in conversation (e.g., *"Hey {user}, what are you building?"*). Store their name (NOT email) in `team.md` under Project Context. **Never read or store `git config user.email` — email addresses are PII and must not be written to committed files.**
24
24
  2. Ask: *"What are you building? (language, stack, what it does)"*
25
25
  3. **Cast the team.** Before proposing names, run the Casting & Persistent Naming algorithm (see that section):
26
26
  - Determine team size (typically 4–5 + Scribe).
@@ -82,8 +82,8 @@ The `union` merge driver keeps all lines from both sides, which is correct for a
82
82
 
83
83
  **Example flow:**
84
84
  1. Coordinator detects no team.md → Init Mode
85
- 2. Runs `git config user.name` → "Brady"
86
- 3. Asks: *"Hey Brady, what are you building?"*
85
+ 2. Runs `git config user.name` → "{user}"
86
+ 3. Asks: *"Hey {user}, what are you building?"*
87
87
  4. User: *"TypeScript CLI tool with GitHub API integration"*
88
88
  5. Coordinator runs casting algorithm → selects "The Usual Suspects" universe
89
89
  6. Proposes: Keaton (Lead), Verbal (Prompt), Fenster (Backend), Hockney (Tester), Scribe, Ralph
@@ -1,3 +1,11 @@
1
+ ---
2
+ name: "model-selection"
3
+ description: "Determines which LLM model to use for each agent spawn"
4
+ domain: "orchestration"
5
+ confidence: "medium"
6
+ source: "extracted"
7
+ ---
8
+
1
9
  # Model Selection
2
10
 
3
11
  > Determines which LLM model to use for each agent spawn.
@@ -1,3 +1,11 @@
1
+ ---
2
+ name: "nap"
3
+ description: "Context hygiene — compress, prune, archive .squad/ state"
4
+ domain: "maintenance"
5
+ confidence: "medium"
6
+ source: "extracted"
7
+ ---
8
+
1
9
  # Skill: nap
2
10
 
3
11
  > Context hygiene — compress, prune, archive .squad/ state
@@ -1,3 +1,11 @@
1
+ ---
2
+ name: "personal-squad"
3
+ description: "User-level AI agents that travel with you across projects"
4
+ domain: "configuration"
5
+ confidence: "medium"
6
+ source: "manual"
7
+ ---
8
+
1
9
  # Personal Squad — Skill Document
2
10
 
3
11
  ## What is a Personal Squad?
@@ -1,3 +1,11 @@
1
+ ---
2
+ name: "ralph-two-pass-scan"
3
+ description: "Cuts GitHub API calls by separating lightweight list scanning from full hydration"
4
+ domain: "work-monitoring"
5
+ confidence: "high"
6
+ source: "extracted"
7
+ ---
8
+
1
9
  # Skill: Ralph — Two-Pass Issue Scanning
2
10
  **Confidence:** high
3
11
  **Domain:** work-monitoring
@@ -1,6 +1,15 @@
1
+ ---
2
+ name: "release-process"
3
+ description: "Pre-release validation, npm publish procedures, and post-publish verification"
4
+ domain: "release"
5
+ confidence: "high"
6
+ source: "earned"
7
+ ---
8
+
1
9
  # Release Process
2
10
 
3
- > Earned knowledge from the v0.9.0→v0.9.1 incident. Every agent involved in releases MUST read this before starting release work.
11
+ > Earned knowledge from the v0.9.0→v0.9.1 and v0.9.4 incidents. Every agent involved in releases MUST read this before starting release work.
12
+ > See also: `.copilot/skills/release-process/SKILL.md` for the Copilot-facing runbook.
4
13
 
5
14
  ## SCOPE
6
15
 
@@ -17,7 +26,7 @@
17
26
 
18
27
  ## Confidence: high
19
28
 
20
- Established through the v0.9.1 incident (8-hour recovery). Every rule below is battle-tested.
29
+ Established through the v0.9.1 incident (8-hour recovery) and reinforced by the v0.9.4 release delay (PRs #1042, #1043, #1044). Every rule below is battle-tested.
21
30
 
22
31
  ## Context
23
32
 
@@ -92,8 +101,9 @@ Set this environment variable in all CI build steps to prevent the build script
92
101
  ```
93
102
  □ All tests passing on dev
94
103
  □ No file:/link: references in packages/*/package.json
95
- CHANGELOG.md updated
96
- Version bumps committed (node -e script)
104
+ Root package.json version matches sub-packages (v0.9.4 lesson — PR #1043)
105
+ CHANGELOG.md has ## [$VERSION] section (not just [Unreleased]) (v0.9.4 lesson — PR #1042)
106
+ □ Version bumps committed: npm version $VERSION --workspaces --include-workspace-root --no-git-tag-version
97
107
  □ npm auth verified (Automation token)
98
108
  □ No draft GitHub Releases pending
99
109
  □ Local build + test: npm run build && npx vitest run
@@ -102,7 +112,8 @@ Set this environment variable in all CI build steps to prevent the build script
102
112
  □ Preview CI green (squad-preview validates)
103
113
  □ Promote preview → main
104
114
  □ squad-release auto-creates GitHub Release
105
- □ squad-npm-publish auto-triggers
115
+ □ squad-npm-publish auto-triggers (⚠️ may be BLOCKED — see GITHUB_TOKEN limitation below)
116
+ □ If publish didn't trigger: gh workflow run squad-npm-publish.yml --ref main -f version=X.Y.Z
106
117
  □ Monitor publish workflow
107
118
  □ Post-publish smoke test
108
119
  ```
@@ -116,6 +127,79 @@ Set this environment variable in all CI build steps to prevent the build script
116
127
  | `npm -w publish` hangs with 2FA | Silent hang, no error | Never use `-w` for publish |
117
128
  | Draft GitHub Releases | npm publish workflow doesn't trigger | Never create drafts |
118
129
  | User npm tokens with 2FA | EOTP errors in CI | Use Automation token type |
130
+ | Root package.json version drift (v0.9.4) | squad-release.yml fails CHANGELOG check | Always bump all 3 package.json files together (PR #1043) |
131
+ | CHANGELOG.md missing `## [$VERSION]` (v0.9.4) | squad-release.yml exits with error | Convert `[Unreleased]` → `[$VERSION] - YYYY-MM-DD` before promoting to main (PR #1042) |
132
+ | GITHUB_TOKEN can't trigger downstream workflows (v0.9.4) | squad-npm-publish.yml never fires | Manual `gh workflow run` or use PAT/GitHub App token (see below) |
133
+ | Lockfile integrity check rejects workspace packages (v0.9.4) | False failures in squad-npm-publish.yml | Only validate packages resolved from npm registry (`startsWith('https://')`) (PR #1044) |
134
+ | `prebuild` version bump breaks workspace linking (v0.9.4) | Local builds fail after bump-build.mjs runs | `git checkout -- package.json packages/*/package.json` then fresh install |
135
+
136
+ ## v0.9.4 Incident Learnings
137
+
138
+ > Source: v0.9.4 release session. PRs #1042, #1043, #1044.
139
+
140
+ ### Root Package.json Version Must Match Sub-Packages
141
+
142
+ `squad-release.yml` reads version from ROOT `package.json` (lines 31-35):
143
+ ```bash
144
+ VERSION=$(node -e "console.log(require('./package.json').version)")
145
+ if ! grep -q "## \[$VERSION\]" CHANGELOG.md; then
146
+ echo "::error::Version $VERSION not found in CHANGELOG.md"
147
+ exit 1
148
+ fi
149
+ ```
150
+ If root package.json is behind (e.g., 0.9.1 while sub-packages are 0.9.4), the release workflow FAILS. This was the root cause of the v0.9.4 release delay — PR #1043 fixed it.
151
+
152
+ **Rule:** When bumping versions, ALWAYS bump all 3 package.json files together:
153
+ ```bash
154
+ npm version $VERSION --workspaces --include-workspace-root --no-git-tag-version
155
+ ```
156
+
157
+ ### CHANGELOG.md Must Have Version Entry
158
+
159
+ `squad-release.yml` validates that `CHANGELOG.md` contains `## [$VERSION]`. If the version section is still `[Unreleased]` and no `[$VERSION]` section exists, the release workflow exits with error. PR #1042 fixed this for v0.9.4.
160
+
161
+ **Rule:** Before promoting to main, convert `[Unreleased]` to `[$VERSION] - YYYY-MM-DD` in CHANGELOG.md and add a fresh `[Unreleased]` section above it.
162
+
163
+ ### GITHUB_TOKEN Event Propagation Limitation (CRITICAL)
164
+
165
+ When `squad-release.yml` creates a GitHub Release using the default `GITHUB_TOKEN`, the `release: published` event does NOT trigger `squad-npm-publish.yml`. This is a GitHub security feature to prevent infinite workflow loops.
166
+
167
+ **Workaround:** After the release workflow succeeds and creates the tag + GitHub Release, manually trigger the publish workflow:
168
+ ```bash
169
+ gh workflow run squad-npm-publish.yml --ref main -f version=X.Y.Z
170
+ ```
171
+ IMPORTANT: Use `--ref main` to ensure the workflow runs against the main branch (where the release artifacts exist).
172
+
173
+ **Permanent fix (TODO):** Use a PAT or GitHub App token in `squad-release.yml` instead of `GITHUB_TOKEN`.
174
+
175
+ ### Lockfile Integrity — Workspace Package Handling
176
+
177
+ The lockfile stability check in `squad-npm-publish.yml` (line 82) filters packages for integrity hashes. Workspace packages resolve to bare relative paths (e.g., `packages/squad-sdk`), NOT `file:` URLs. The check must filter for registry-resolved packages only (`startsWith('https://')`). PR #1044 fixed this.
178
+
179
+ ### Prebuild Version Bump Breaks Local Workspace Resolution
180
+
181
+ `scripts/bump-build.mjs` runs during `npm run prebuild` and bumps versions like `0.9.4` → `0.9.4-build.1`. This breaks workspace linking because CLI depends on exact `"@bradygaster/squad-sdk": "0.9.4"` but SDK becomes `0.9.4-build.1`.
182
+
183
+ **Fix for local dev:**
184
+ ```bash
185
+ git checkout -- package.json packages/*/package.json
186
+ rm -rf node_modules packages/*/node_modules
187
+ npm install
188
+ npm run build
189
+ ```
190
+
191
+ ### The Full Promotion Chain (v0.9.4 Documented)
192
+
193
+ ```
194
+ dev → preview → main (via squad-promote.yml)
195
+ main push → squad-release.yml validates CHANGELOG, creates tag + GitHub Release
196
+ release published → squad-npm-publish.yml (⚠️ BLOCKED by GITHUB_TOKEN limitation)
197
+ manual workaround → gh workflow run squad-npm-publish.yml --ref main -f version=X.Y.Z
198
+ ```
199
+
200
+ ### npm Publish Workflow Dispatch Target
201
+
202
+ When using `workflow_dispatch` to trigger `squad-npm-publish.yml`, the default ref is the repo's default branch (`dev`). Always specify `--ref main` explicitly to ensure the workflow runs against the branch with the release tag and latest workflow fixes.
119
203
 
120
204
  ## CI Gate: Workspace Publish Policy
121
205
 
@@ -126,6 +210,8 @@ See `.github/workflows/squad-ci.yml` → `publish-policy` job for implementation
126
210
  ## Related
127
211
 
128
212
  - Issues: #556–#564 (release:next)
213
+ - v0.9.4 fixes: PR #1042 (CHANGELOG), PR #1043 (root package.json), PR #1044 (lockfile integrity)
129
214
  - Retro: `.squad/decisions/inbox/surgeon-v091-retrospective.md`
130
215
  - CI audit: `.squad/decisions/inbox/booster-ci-audit.md`
216
+ - Copilot-level skill: `.copilot/skills/release-process/SKILL.md`
131
217
  - Playbook: `PUBLISH-README.md` (repo root)
@@ -32,7 +32,7 @@ Check: Does `.squad/team.md` exist? (fall back to `.ai-team/team.md` for repos m
32
32
 
33
33
  No team exists yet. Propose one — but **DO NOT create any files until the user confirms.**
34
34
 
35
- 1. **Identify the user.** Run `git config user.name` to learn who you're working with. Use their name in conversation (e.g., *"Hey Brady, what are you building?"*). Store their name (NOT email) in `team.md` under Project Context. **Never read or store `git config user.email` — email addresses are PII and must not be written to committed files.**
35
+ 1. **Identify the user.** Run `git config user.name` to learn who you're working with. Use their name in conversation (e.g., *"Hey {user}, what are you building?"*). Store their name (NOT email) in `team.md` under Project Context. **Never read or store `git config user.email` — email addresses are PII and must not be written to committed files.**
36
36
  2. Ask: *"What are you building? (language, stack, what it does)"*
37
37
  3. **Cast the team.** Before proposing names, run the Casting & Persistent Naming algorithm (see that section):
38
38
  - Determine team size (typically 4–5 + Scribe).
@@ -107,6 +107,8 @@ The `union` merge driver keeps all lines from both sides, which is correct for a
107
107
 
108
108
  **On every session start:** Run `git config user.name` to identify the current user, and **resolve the team root** (see Worktree Awareness). Store the team root — all `.squad/` paths must be resolved relative to it. Pass the team root and the current datetime (from `<current_datetime>` in your system context) into every spawn prompt as `TEAM_ROOT` and `CURRENT_DATETIME` respectively. Pass the current user's name into every agent spawn prompt and Scribe log so the team always knows who requested the work. Check `.squad/identity/now.md` if it exists — it tells you what the team was last focused on. Update it if the focus has shifted.
109
109
 
110
+ **Resolve state backend:** Read `.squad/config.json` and check the `stateBackend` field. Valid values: `"worktree"` (default), `"git-notes"`, `"orphan"`, `"two-layer"`. Store as `STATE_BACKEND` and pass it into every spawn prompt. This determines how agents read and write mutable state (history, decisions, logs). Static config (charters, team.md, routing.md) always lives on disk regardless of backend. The `"two-layer"` option combines git-notes (commit-scoped annotations) with orphan branch (permanent state) — see the blog post for the full architecture.
111
+
110
112
  **⚡ Context caching:** After the first message in a session, `team.md`, `routing.md`, and `registry.json` are already in your context. Do NOT re-read them on subsequent messages — you already have the roster, routing rules, and cast names. Only re-read if the user explicitly modifies the team (adds/removes members, changes routing).
111
113
 
112
114
  **Session catch-up (lazy — not on every start):** Do NOT scan logs on every session start. Only provide a catch-up summary when:
@@ -223,13 +225,16 @@ The `name` parameter generates the human-readable agent ID shown in the tasks pa
223
225
 
224
226
  **When you detect a directive:**
225
227
 
226
- 1. Write it immediately to `.squad/decisions/inbox/copilot-directive-{timestamp}.md` using this format:
227
- ```
228
- ### {timestamp}: User directive
229
- **By:** {user name} (via Copilot)
230
- **What:** {the directive, verbatim or lightly paraphrased}
231
- **Why:** User request captured for team memory
232
- ```
228
+ 1. Capture the directive:
229
+ - **worktree/orphan backend:** Write it immediately to `.squad/decisions/inbox/copilot-directive-{timestamp}.md` using this format:
230
+ ```
231
+ ### {timestamp}: User directive
232
+ **By:** {user name} (via Copilot)
233
+ **What:** {the directive, verbatim or lightly paraphrased}
234
+ **Why:** User request — captured for team memory
235
+ ```
236
+ - **git-notes backend:** Persist via:
237
+ `powershell .squad/scripts/notes/write-note.ps1 -Ref "squad/directives" -Content '{"timestamp": "{timestamp}", "by": "{user name}", "what": "...", "why": "User request"}'`
233
238
  2. Acknowledge briefly: `"📌 Captured. {one-line summary of the directive}."`
234
239
  3. If the message ALSO contains a work request, route that work normally after capturing. If it's directive-only, you're done — no agent spawn needed.
235
240
 
@@ -242,12 +247,12 @@ The routing table determines **WHO** handles work. After routing, use Response M
242
247
  | Names someone ("Ripley, fix the button") | Spawn that agent |
243
248
  | Personal agent by name (user addresses a personal agent) | Route to personal agent in consult mode — they advise, project agent executes changes |
244
249
  | "Team" or multi-domain question | Spawn 2-3+ relevant agents in parallel, synthesize |
245
- | Human member management ("add Brady as PM", routes to human) | Follow Human Team Members (see that section) |
250
+ | Human member management ("add {name} as PM", routes to human) | Follow Human Team Members (see that section) |
246
251
  | Issue suitable for @copilot (when @copilot is on the roster) | Check capability profile in team.md, suggest routing to @copilot if it's a good fit |
247
252
  | Ceremony request ("design meeting", "run a retro") | Run the matching ceremony from `ceremonies.md` (see Ceremonies) |
248
253
  | Issues/backlog request ("pull issues", "show backlog", "work on #N") | Follow GitHub Issues Mode (see that section) |
249
254
  | PRD intake ("here's the PRD", "read the PRD at X", pastes spec) | Follow PRD Mode (see that section) |
250
- | Human member management ("add Brady as PM", routes to human) | Follow Human Team Members (see that section) |
255
+ | Human member management ("add {name} as PM", routes to human) | Follow Human Team Members (see that section) |
251
256
  | Ralph commands ("Ralph, go", "keep working", "Ralph, status", "Ralph, idle") | Follow Ralph — Work Monitor (see that section) |
252
257
  | General work request | Check routing.md, spawn best match + any anticipatory agents |
253
258
  | Quick factual question | Answer directly (no spawn) |
@@ -292,7 +297,7 @@ After routing determines WHO handles work, select the response MODE based on tas
292
297
  | **Full** | Multi-agent work, complex tasks touching 3+ concerns, "Team" requests | Parallel fan-out, full ceremony, Scribe included | ~40-60s |
293
298
 
294
299
  **Direct Mode exemplars** (coordinator answers instantly, no spawn):
295
- - "Where are we?" → Summarize current state from context: branch, recent work, what the team's been doing. Brady's favorite — make it instant.
300
+ - "Where are we?" → Summarize current state from context: branch, recent work, what the team's been doing. A user favorite — make it instant.
296
301
  - "How many tests do we have?" → Run a quick command, answer directly.
297
302
  - "What branch are we on?" → `git branch --show-current`, answer directly.
298
303
  - "Who's on the team?" → Answer from team.md already in context.
@@ -344,7 +349,12 @@ prompt: |
344
349
  TARGET FILE(S): {exact file path(s)}
345
350
 
346
351
  Do the work. Keep it focused.
352
+ {% if STATE_BACKEND == "git-notes" %}
353
+ If you made a meaningful decision, persist it via:
354
+ `powershell .squad/scripts/notes/write-note.ps1 -Ref "squad/{name}" -Content '{"decision": {"title": "...", "what": "...", "why": "..."}}'`
355
+ {% else %}
347
356
  If you made a meaningful decision, write to .squad/decisions/inbox/{name}-{brief-slug}.md
357
+ {% endif %}
348
358
 
349
359
  ⚠️ OUTPUT: Report outcomes in human terms. Never expose tool internals or SQL.
350
360
  ⚠️ RESPONSE ORDER: After ALL tool calls, write a plain text summary as FINAL output.
@@ -598,8 +608,9 @@ When the user gives any task, the Coordinator MUST:
598
608
  To enable full parallelism, shared writes use a drop-box pattern that eliminates file conflicts:
599
609
 
600
610
  **decisions.md** — Agents do NOT write directly to `decisions.md`. Instead:
601
- - Agents write decisions to individual drop files: `.squad/decisions/inbox/{agent-name}-{brief-slug}.md`
602
- - Scribe merges inbox entries into the canonical `.squad/decisions.md` and clears the inbox
611
+ - **worktree/orphan backend:** Agents write decisions to individual drop files: `.squad/decisions/inbox/{agent-name}-{brief-slug}.md`
612
+ - **git-notes backend:** Agents persist decisions via their git notes ref (see State Protocol). Scribe reads all agent notes and merges decisions.
613
+ - Scribe merges into the canonical `.squad/decisions.md` and clears the inbox (or note entries)
603
614
  - All agents READ from `.squad/decisions.md` at spawn time (last-merged snapshot)
604
615
 
605
616
  **orchestration-log/** — Scribe writes one entry per agent after each batch:
@@ -799,7 +810,71 @@ prompt: |
799
810
  - Commit and push from the worktree
800
811
  {% endif %}
801
812
 
813
+ STATE_BACKEND: {state_backend}
814
+
815
+ {% if STATE_BACKEND == "git-notes" %}
816
+ ## State Protocol — Git Notes
817
+ This project uses git-notes for mutable state. **DO NOT write to `.squad/` files for state.**
818
+ Static config (charters, team.md, routing.md) is on disk as normal — read those with `view`.
819
+
820
+ **Reading your state:**
821
+ Run: `powershell .squad/scripts/notes/fetch.ps1 -Setup` (first time per session)
822
+ Then: `git notes --ref=squad/{name} show $(git rev-list --max-parents=0 HEAD) 2>$null`
823
+ Falls back to empty if no note exists.
824
+
825
+ **Writing state (history, decisions, learnings):**
826
+ Run: `powershell .squad/scripts/notes/write-note.ps1 -Ref "squad/{name}" -Content '{json}'`
827
+ The helper handles JSON validation, conflict retry, and push.
828
+
829
+ **Decisions:** Write decisions as JSON via your note ref. Scribe will merge them.
830
+ **Skills:** Skills are static config — write to `.squad/skills/` on disk as normal.
831
+ {% endif %}
832
+
833
+ {% if STATE_BACKEND == "orphan" %}
834
+ ## State Protocol — Orphan Branch
835
+ This project uses an orphan branch (`squad-state`) for mutable state.
836
+ Static config (charters, team.md, routing.md) is on disk as normal — read those with `view`.
837
+
838
+ **Reading state:** Read `.squad/` files on disk — they are synced from the orphan branch.
839
+ **Writing state:** Write to `.squad/` files on disk as normal during your session.
840
+ Scribe will commit your changes to the orphan branch (not the working branch) and
841
+ ensure they persist across branch switches.
842
+
843
+ **Important:** Do NOT commit `.squad/` state files to the working branch yourself.
844
+ Scribe handles the orphan branch commit workflow.
845
+ {% endif %}
846
+
847
+ {% if STATE_BACKEND == "two-layer" %}
848
+ ## State Protocol — Two-Layer (Git Notes + Orphan Branch)
849
+ This project uses the two-layer architecture from Tamir's blog:
850
+ - **Layer 1 (git notes):** Commit-scoped "why" annotations — invisible in PRs
851
+ - **Layer 2 (orphan branch):** Permanent state store — decisions, histories, logs
852
+
853
+ Static config (charters, team.md, routing.md) is on disk as normal.
854
+
855
+ **During your session:**
856
+ 1. Write commit-scoped annotations as git notes on HEAD:
857
+ `git notes --ref=squad/{name} add -f -m '{"agent":"{Name}","type":"decision","decision":"...","promote_to_permanent":true}' HEAD`
858
+ 2. Write bulk state (history, logs) to `.squad/` files on disk — Scribe moves them to the orphan branch.
859
+
860
+ **Note flags:**
861
+ - `"promote_to_permanent": true` — Ralph promotes this to decisions.md after PR merge
862
+ - `"archive_on_close": true` — Worth keeping even if PR is rejected (valuable research)
863
+ - Neither flag — silently ignored if PR is rejected (correct for branch-specific decisions)
864
+
865
+ **Important:** Do NOT commit `.squad/` state files to the working branch.
866
+ Scribe handles orphan commits. Ralph handles note promotion.
867
+ {% endif %}
868
+
869
+ {% if STATE_BACKEND == "worktree" or STATE_BACKEND is not defined %}
802
870
  Read .squad/agents/{name}/history.md (your project knowledge).
871
+ {% endif %}
872
+ {% if STATE_BACKEND == "git-notes" %}
873
+ Read your agent state from git notes (see State Protocol above).
874
+ {% endif %}
875
+ {% if STATE_BACKEND == "orphan" or STATE_BACKEND == "two-layer" %}
876
+ Read .squad/agents/{name}/history.md (your project knowledge — synced from orphan branch).
877
+ {% endif %}
803
878
  Read .squad/decisions.md (team decisions to respect).
804
879
  If .squad/identity/wisdom.md exists, read it before starting work.
805
880
  If .squad/identity/now.md exists, read it at spawn time.
@@ -823,10 +898,27 @@ prompt: |
823
898
  ⚠️ DATES: When writing dates in any file (decisions, history, logs), use ONLY the CURRENT_DATETIME value above. Never infer or guess the date.
824
899
 
825
900
  AFTER work:
901
+ {% if STATE_BACKEND == "git-notes" %}
902
+ 1. Persist your learnings as JSON via the State Protocol:
903
+ `powershell .squad/scripts/notes/write-note.ps1 -Ref "squad/{name}" -Content '{"learnings": ["..."], "timestamp": "{current_datetime}"}'`
904
+ 2. If you made a team-relevant decision, include it in the JSON:
905
+ Add a `"decision"` field with `"title"`, `"what"`, and `"why"` keys.
906
+ Scribe will merge decisions into the canonical decisions.md.
907
+ {% elif STATE_BACKEND == "two-layer" %}
908
+ 1. APPEND to .squad/agents/{name}/history.md under "## Learnings":
909
+ architecture decisions, patterns, user preferences, key file paths.
910
+ (Scribe commits this to the orphan branch.)
911
+ 2. If you made a team-relevant decision, write BOTH:
912
+ a. A git note on HEAD with promote flag:
913
+ `git notes --ref=squad/{name} add -f -m '{"agent":"{Name}","type":"decision","decision":"...","promote_to_permanent":true}' HEAD`
914
+ b. A drop file: .squad/decisions/inbox/{name}-{brief-slug}.md
915
+ (Scribe merges to orphan branch; Ralph promotes note after PR merge.)
916
+ {% else %}
826
917
  1. APPEND to .squad/agents/{name}/history.md under "## Learnings":
827
918
  architecture decisions, patterns, user preferences, key file paths.
828
919
  2. If you made a team-relevant decision, write to:
829
920
  .squad/decisions/inbox/{name}-{brief-slug}.md
921
+ {% endif %}
830
922
  3. SKILL EXTRACTION: If you found a reusable pattern, write/update
831
923
  .squad/skills/{skill-name}/SKILL.md (read templates/skill.md for format).
832
924
 
@@ -878,18 +970,47 @@ prompt: |
878
970
  You are the Scribe. Read .squad/agents/scribe/charter.md.
879
971
  TEAM ROOT: {team_root}
880
972
  CURRENT_DATETIME: {current_datetime}
973
+ STATE_BACKEND: {state_backend}
881
974
 
882
975
  SPAWN MANIFEST: {spawn_manifest}
883
976
 
884
977
  Tasks (in order):
885
- 0. PRE-CHECK: Stat decisions.md size and count inbox/ files. Record measurements.
978
+ {% if STATE_BACKEND == "orphan" or STATE_BACKEND == "git-notes" or STATE_BACKEND == "two-layer" %}
979
+ 0. STATE LEAK GUARD: Check if any agent accidentally committed or staged state files
980
+ (.squad/decisions.md, agents/*/history.md, log/*, orchestration-log/*, decisions/inbox/*)
981
+ to the working branch. If found: unstage with `git reset HEAD -- {file}`, restore with
982
+ `git checkout HEAD -- {file}`. If leaked in last commit, amend to remove. Log count.
983
+ {% endif %}
984
+ 0b. PRE-CHECK: Stat decisions.md size and count inbox/ files. Record measurements.
886
985
  1. DECISIONS ARCHIVE [HARD GATE]: If decisions.md >= 20480 bytes, archive entries older than 30 days NOW. If >= 51200 bytes, archive entries older than 7 days. Do not skip this step.
986
+ {% if STATE_BACKEND == "git-notes" %}
987
+ 2. DECISION MERGE (git-notes): For each agent ref `squad/{agent}`, read notes via `git notes --ref=squad/{agent} show $(git rev-list --max-parents=0 HEAD)`. Extract any `decision` entries. Merge into decisions.md. Clear the decision field by overwriting the note without it.
988
+ {% elif STATE_BACKEND == "two-layer" %}
989
+ 2. DECISION MERGE (two-layer): Merge .squad/decisions/inbox/ → decisions.md AND read agent note refs for any decisions with `promote_to_permanent`. Deduplicate. Push note refs: `git push origin 'refs/notes/squad/*'`
990
+ {% else %}
887
991
  2. DECISION INBOX: Merge .squad/decisions/inbox/ → decisions.md, delete inbox files. Deduplicate.
992
+ {% endif %}
888
993
  3. ORCHESTRATION LOG: Write .squad/orchestration-log/{timestamp}-{agent}.md per agent. Use ISO 8601 UTC timestamp.
889
994
  4. SESSION LOG: Write .squad/log/{timestamp}-{topic}.md. Brief. Use ISO 8601 UTC timestamp.
995
+ {% if STATE_BACKEND == "git-notes" %}
996
+ 5. CROSS-AGENT (git-notes): For team updates, write to affected agents' note refs via `powershell .squad/scripts/notes/write-note.ps1 -Ref "squad/{agent}" -Content '{json}'`.
997
+ {% else %}
890
998
  5. CROSS-AGENT: Append team updates to affected agents' history.md.
999
+ {% endif %}
891
1000
  6. HISTORY SUMMARIZATION [HARD GATE]: If any history.md >= 15360 bytes (15KB), summarize now.
1001
+ {% if STATE_BACKEND == "orphan" or STATE_BACKEND == "two-layer" %}
1002
+ 7. GIT COMMIT (orphan): Stage `.squad/` state files and commit to the `squad-state` orphan branch:
1003
+ a. Identify changed `.squad/` state files via `git status --porcelain` (decisions.md, agents/*/history.md, log/*, orchestration-log/*).
1004
+ b. For each file, use git plumbing to write to the orphan branch:
1005
+ `git show squad-state:.squad/{path}` to check if file exists on orphan.
1006
+ Use `git checkout squad-state -- .squad/{path}` + write + `git add` + `git commit` workflow, OR
1007
+ use the SDK's OrphanBranchBackend if available.
1008
+ c. Reset working tree state files: `git checkout HEAD -- .squad/` to avoid polluting the working branch.
1009
+ d. Push orphan branch: `git push origin squad-state`
1010
+ ⚠️ NEVER commit `.squad/` state files to the working branch when using orphan backend.
1011
+ {% else %}
892
1012
  7. GIT COMMIT: Stage only the exact `.squad/` files Scribe wrote in this session. Use `git status --porcelain` filtered to allowed paths (decisions.md, decisions-archive.md, agents/{name}/history.md, agents/{name}/history-archive.md, log/*, orchestration-log/*). Stage each file individually with `git add -- <path>`. Handle renames by extracting destination path (`-replace '^.* -> ',''`). Commit with -F (write msg to temp file). Skip if nothing staged. ⚠️ NEVER use `git add .squad/` or broad globs.
1013
+ {% endif %}
893
1014
  8. HEALTH REPORT: Log decisions.md before/after size, inbox count processed, history files summarized.
894
1015
 
895
1016
  Never speak to user. ⚠️ End with plain text summary after all tool calls.
@@ -947,6 +1068,8 @@ If the user wants to remove someone:
947
1068
 
948
1069
  ## Source of Truth Hierarchy
949
1070
 
1071
+ > **State backend note:** Files below marked as "Derived / append-only" are **mutable state** — their storage location depends on the configured `STATE_BACKEND`. On `worktree` (default), they live on disk. On `git-notes`, they live in git notes refs. On `orphan`, they live on the `squad-state` orphan branch. On `two-layer`, commit-scoped annotations live in git notes AND permanent state lives on the orphan branch. Files marked as "Authoritative" are **static config** and always live on disk regardless of backend.
1072
+
950
1073
  | File | Status | Who May Write | Who May Read |
951
1074
  |------|--------|---------------|--------------|
952
1075
  | `.github/agents/squad.agent.md` | **Authoritative governance.** All roles, handoffs, gates, and enforcement rules. | Repo maintainer (human) | Squad (Coordinator) |