forgedock 1.0.0

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 (44) hide show
  1. package/LICENSE +244 -0
  2. package/README.md +251 -0
  3. package/bin/forgedock.mjs +232 -0
  4. package/commands/analytics.md +777 -0
  5. package/commands/audit-agents.md +410 -0
  6. package/commands/audit.md +410 -0
  7. package/commands/autopilot.md +432 -0
  8. package/commands/cleanup.md +264 -0
  9. package/commands/deploy-info.md +292 -0
  10. package/commands/failure-recon.md +375 -0
  11. package/commands/forge-stats.md +183 -0
  12. package/commands/geo-audit.md +343 -0
  13. package/commands/incident-response.md +384 -0
  14. package/commands/issue.md +506 -0
  15. package/commands/milestone.md +599 -0
  16. package/commands/orchestrate.md +1044 -0
  17. package/commands/pipeline-health.md +2343 -0
  18. package/commands/qa-sweep.md +237 -0
  19. package/commands/quality-gate.md +777 -0
  20. package/commands/review-pr-agents.md +1711 -0
  21. package/commands/review-pr-staging.md +325 -0
  22. package/commands/review-pr.md +888 -0
  23. package/commands/rollback.md +253 -0
  24. package/commands/security-audit.md +529 -0
  25. package/commands/sync-ecosystem.md +527 -0
  26. package/commands/validate.md +131 -0
  27. package/commands/work-on/build/architect.md +291 -0
  28. package/commands/work-on/build/context.md +349 -0
  29. package/commands/work-on/build/implement.md +280 -0
  30. package/commands/work-on/build/validate.md +292 -0
  31. package/commands/work-on/build.md +262 -0
  32. package/commands/work-on/close.md +350 -0
  33. package/commands/work-on/decompose.md +254 -0
  34. package/commands/work-on/investigate.md +437 -0
  35. package/commands/work-on/review.md +245 -0
  36. package/commands/work-on-monolithic.md +298 -0
  37. package/commands/work-on.md +1089 -0
  38. package/docs/CODEX.md +92 -0
  39. package/docs/WORKFLOW.md +104 -0
  40. package/package.json +45 -0
  41. package/scripts/verify-env-vars.sh +201 -0
  42. package/scripts/verify-host-headers.sh +117 -0
  43. package/scripts/verify-route-registration.sh +118 -0
  44. package/scripts/verify-sops-chain.sh +163 -0
@@ -0,0 +1,1044 @@
1
+ ---
2
+ description: Orchestrate parallel work on multiple issues or an entire milestone — spawns sub-agents that each run the full /work-on pipeline
3
+ argument-hint: [milestone <slug> | #1 #2 #3 | next <N> | fast-lane | P0]
4
+ ---
5
+
6
+ # /orchestrate — Multi-Issue Parallel Orchestrator
7
+
8
+ **Input**: $ARGUMENTS
9
+
10
+ ## HARD RULES — READ BEFORE ANYTHING ELSE
11
+
12
+ 1. **Every agent MUST invoke `/work-on` via the Skill tool.** You do NOT write implementation prompts. You copy the Phase 4A template verbatim and fill in the `{VARIABLES}`. Nothing else. No custom prompts. No "just read and edit" shortcuts. The Skill tool invocation is what triggers labels, investigation comments, structured review, and trajectory tracking. Without it, the agent's work has no paper trail and is worthless.
13
+
14
+ 2. **You are a dispatcher, not a builder.** You resolve issues, plan waves, spawn agents, and report results. You NEVER read code, edit files, or implement fixes yourself.
15
+
16
+ 3. **After each wave, verify agents used `/work-on`.** Check that completed issues have `workflow:*` labels and structured comments. If an agent bypassed the pipeline, report it as a failure.
17
+
18
+ ---
19
+
20
+ You are the top-level orchestrator. Your job is to take a batch of issues, plan the execution order, spawn parallel sub-agents (each running the full `/work-on` pipeline), and report consolidated results.
21
+
22
+ **You have access to ALL tools** — Agent tool (critical), Task tool, Skill tool, Bash, everything. Use the Agent tool aggressively to parallelize work.
23
+
24
+ **Agent model policy**: Default `model: "sonnet"`. If Sonnet is rate-limited, fall back to `model: "opus"`. User can override with `--model <name>`.
25
+
26
+ ---
27
+
28
+ ## Multi-Repo Support
29
+
30
+ This orchestrator supports issues across the AlterLab ecosystem. See `/work-on` → "Multi-Repo Support" for the full project registry and context variables.
31
+
32
+ ### Cross-Repo Issue References
33
+
34
+ Issues can be prefixed with a project shorthand:
35
+ - `#123` or `123` → AlterLab (default: `RapierCraft/AlterLab`)
36
+ - `mcp:5` → MCP Server (`RapierCraft/alterlab-mcp-server`)
37
+ - `n8n:12` → n8n Node (`RapierCraft/n8n-nodes-alterlab`)
38
+ - `all-repos` → Scan all ecosystem repos for open issues (combine and prioritize)
39
+
40
+ When spawning sub-agents, pass the project prefix so `/work-on` resolves to the correct repo.
41
+
42
+ ---
43
+
44
+ ## Phase 1: Resolve the Issue Set
45
+
46
+ Parse `$ARGUMENTS` to determine which issues to work on:
47
+
48
+ ### Input Patterns
49
+
50
+ | Input | Resolution |
51
+ |-------|------------|
52
+ | `milestone <slug>` | All open issues assigned to that GitHub milestone (default repo) |
53
+ | `#1 #2 #3` or `1 2 3` | Specific issue numbers, optionally repo-prefixed (e.g., `#123 mcp:5 n8n:12`) |
54
+ | `next <N>` | Top N priority open issues (P0 first, then P1, etc.) |
55
+ | `next <N> all-repos` | Top N across ALL ecosystem repos |
56
+ | `fast-lane` or `fast` | All open fast-lane issues (no milestone, bugs/fixes) |
57
+ | `P0` or `P1` | All open issues with that priority label |
58
+ | `mcp:fast` or `n8n:next 3` | Repo-scoped queries |
59
+ | `<slug>` (no keyword) | Try milestone first, then fall back to label search |
60
+
61
+ ### Fetch the issues
62
+
63
+ For each resolved repo, use the appropriate `-R` flag:
64
+
65
+ ```bash
66
+ # AlterLab (default — no flag needed):
67
+ gh issue list --milestone "{TITLE}" --state open --limit 500 --json number,title,labels,milestone --jq '.[] | {number, title, labels: [.labels[].name], milestone: .milestone.title}'
68
+
69
+ # Satellite repos — always include -R flag:
70
+ gh issue list -R RapierCraft/alterlab-mcp-server --milestone "{TITLE}" --state open --limit 500 --json number,title,labels,milestone
71
+ gh issue list -R RapierCraft/n8n-nodes-alterlab --milestone "{TITLE}" --state open --limit 500 --json number,title,labels,milestone
72
+
73
+ # For specific numbers with repo prefix:
74
+ gh issue view 5 -R RapierCraft/alterlab-mcp-server --json number,title,labels,state,milestone,body
75
+
76
+ # For "all-repos" — fetch from all and combine:
77
+ gh issue list --state open --limit {N} --json number,title,labels,milestone
78
+ gh issue list -R RapierCraft/alterlab-mcp-server --state open --limit 500 --json number,title,labels,milestone
79
+ gh issue list -R RapierCraft/n8n-nodes-alterlab --state open --limit 500 --json number,title,labels,milestone
80
+ # Combine, sort by priority, take top N
81
+ ```
82
+
83
+ **Count validation**: After fetching issues, log the count returned. If a milestone query returns exactly 30 issues, warn the user: "WARNING: Exactly 30 issues returned — gh CLI default may have truncated results. Re-running with --limit 500 is recommended." (The --limit 500 above prevents this for standard runs, but verify if the count seems suspiciously low relative to milestone expectations.)
84
+
85
+ **Tag each issue with its project prefix** in your internal tracking so sub-agents receive the correct repo context.
86
+
87
+ ### Filter out ineligible issues
88
+
89
+ Remove issues that should NOT be worked on:
90
+ - Already closed or has `workflow:merged` label
91
+ - Has `workflow:invalid` label
92
+ - Has `needs-human` label (requires manual action)
93
+ - Has `workflow:decomposed` label (parent tracker — its sub-issues should be picked up instead)
94
+ - Has `workflow:building` or `workflow:in-review` label (already in progress)
95
+ - Is an epic (`epic` label) — these are planning containers, not buildable
96
+
97
+ If a `workflow:decomposed` issue is found, automatically expand it to its open sub-issues instead.
98
+
99
+ ### CRITICAL: No duplicate detection at orchestrator level
100
+
101
+ **The orchestrator NEVER closes, merges, or deduplicates issues.** Even if two issues look like duplicates (similar titles, same error message, same symptoms), they may target different code paths, different ORM objects, or different failure modes. Only a `/work-on` investigation agent — after reading the actual code — can determine whether an issue is truly a duplicate.
102
+
103
+ **What the orchestrator must NOT do:**
104
+ - Close an issue as "duplicate" based on title/symptom similarity
105
+ - Skip an issue because another issue "covers it"
106
+ - Merge two issues into one agent
107
+ - Make ANY judgment about issue validity
108
+
109
+ **What the orchestrator SHOULD do:**
110
+ - If two issues look related, add a note in the agent prompt: "Note: #{OTHER} has similar symptoms — investigate whether this is the same root cause or a different code path"
111
+ - Let both agents run independently — if one finds it's truly a duplicate, `/work-on`'s investigation phase will close it as invalid with evidence
112
+ - Flag potential overlap in the wave plan for the user's awareness, but NEVER act on it
113
+
114
+ **Why**: Surface-level similarity hides critical differences. #3842 (api_key.id lazy load) and #4039 (user.id lazy load) had identical error messages but targeted completely different ORM objects. Closing #4039 as a "duplicate" would have left a customer-impacting P0 bug unfixed.
115
+
116
+ ---
117
+
118
+ ## Phase 2: Investigation-First Triage
119
+
120
+ **Purpose**: Investigation issues produce NEW GitHub issues as their output. If the batch contains investigations, they must run FIRST so their output can be folded into the execution plan.
121
+
122
+ **If no investigations are found in Step 2A, skip this entire phase and proceed directly to Phase 3.**
123
+
124
+ ### Step 2A: Classify each issue
125
+
126
+ For each issue in the resolved set, read its title and body to classify it:
127
+
128
+ ```bash
129
+ for NUM in {issue_numbers}; do
130
+ gh issue view $NUM --json title,body,labels --jq '{title: .title, labels: [.labels[].name], body_preview: (.body[:500])}'
131
+ done
132
+ ```
133
+
134
+ **Classification rules:**
135
+
136
+ | Signal | Classification |
137
+ |--------|---------------|
138
+ | Title contains "Investigate", "Audit", "Research", "Evaluate", "Assess", "Deep dive" | **Investigation** |
139
+ | Issue body is primarily questions (`- [ ]` checklist) with no concrete code changes | **Investigation** |
140
+ | Issue body says "Deliverable: execution plan" or "create issues" | **Investigation** |
141
+ | Issue has `enhancement` label + title starts with "Enable" + body describes toggling a feature flag | **Implementation** |
142
+ | Issue has `bug`, `refactor`, `dead-code`, or `feature` label | **Implementation** |
143
+
144
+ Tag each issue internally as `INVESTIGATION` or `IMPLEMENTATION`.
145
+
146
+ ### Step 2B: Run investigations first (Wave 0)
147
+
148
+ If ANY issues are classified as `INVESTIGATION`:
149
+
150
+ 1. **Move them to Wave 0** — they run BEFORE all implementation issues
151
+ 2. **Spawn Wave 0 agents** using the same agent template as Phase 4A, but emphasize that `/work-on` will detect the investigation task type and produce issues (not code)
152
+ 3. **Wait for ALL Wave 0 agents to complete** before proceeding to Phase 3
153
+
154
+ ### Step 2C: Collect newly created issues
155
+
156
+ After Wave 0 completes, each investigation agent will have created new GitHub issues. Collect them:
157
+
158
+ ```bash
159
+ # For each investigation issue that completed successfully:
160
+ # Read the FORGE:BUILDER comment to find created issue numbers
161
+ for INV_NUM in {investigation_numbers}; do
162
+ gh api repos/{GH_REPO}/issues/${INV_NUM}/comments \
163
+ --jq '.[] | select(.body | contains("<!-- FORGE:BUILDER -->")) | .body' \
164
+ | grep -oP '#\K\d+' | sort -u
165
+ done
166
+
167
+ # Also check: if the investigation issue had a milestone, the new issues should too
168
+ # Verify milestone assignment:
169
+ for NEW_NUM in {newly_created_numbers}; do
170
+ gh issue view $NEW_NUM --json milestone --jq '.milestone.title // "NO MILESTONE"'
171
+ done
172
+ ```
173
+
174
+ ### Step 2C.5: Collect Knowledge Gist URLs from investigations
175
+
176
+ For each completed investigation issue, query its comments for `FORGE:KNOWLEDGE_GIST` annotations. Store the mapping of investigation issue number → Gist URL(s) so the agent template in Step 4A can include prior investigation context.
177
+
178
+ ```bash
179
+ # Build a map: investigation_number → gist_url(s)
180
+ declare -A INVESTIGATION_GISTS
181
+ for INV_NUM in {investigation_numbers}; do
182
+ GIST_URLS=$(gh api repos/{GH_REPO}/issues/${INV_NUM}/comments \
183
+ --jq '[.[] | select(.body | test("<!-- FORGE:KNOWLEDGE_GIST: https://")) | .body | capture("<!-- FORGE:KNOWLEDGE_GIST: (?<url>https://[^ ]+) -->").url] | unique | .[]')
184
+ if [ -n "$GIST_URLS" ]; then
185
+ INVESTIGATION_GISTS[$INV_NUM]="$GIST_URLS"
186
+ echo "Investigation #${INV_NUM}: found Gist URL(s)"
187
+ fi
188
+ done
189
+ ```
190
+
191
+ When spawning agents for implementation issues in Step 4A, include any Gist URLs from parent/sibling investigations in the agent prompt's context block. See the `{GIST_CONTEXT}` variable in the agent template.
192
+
193
+ **Milestone index Gist**: If the milestone has a `<!-- FORGE:MILESTONE_INDEX: {url} -->` annotation in its description, read the index URL and store it for inclusion in agent context. The milestone index aggregates all investigation Gist URLs for the milestone into a single reference.
194
+
195
+ ```bash
196
+ # Read milestone index URL from milestone description (if milestone exists)
197
+ MILESTONE_INDEX_URL=""
198
+ if [ -n "$MILESTONE_NUM" ]; then
199
+ MILESTONE_DESC=$(gh api repos/{GH_REPO}/milestones/${MILESTONE_NUM} --jq '.description // ""' 2>/dev/null)
200
+ MILESTONE_INDEX_URL=$(echo "$MILESTONE_DESC" | grep -oP '(?<=<!-- FORGE:MILESTONE_INDEX: )https://[^ ]+(?= -->)' | head -1)
201
+ if [ -n "$MILESTONE_INDEX_URL" ]; then
202
+ echo "Milestone index Gist found: ${MILESTONE_INDEX_URL}"
203
+ fi
204
+ fi
205
+ ```
206
+
207
+ ### Step 2D: Merge new issues into the batch
208
+
209
+ Add the newly created issues to the issue set. Re-check each one:
210
+ - Filter out any that are already ineligible (same rules as Phase 1)
211
+ - Tag them as `IMPLEMENTATION` (investigations don't spawn more investigations)
212
+ - They inherit the milestone from their parent investigation if applicable
213
+
214
+ **Updated issue set** = original `IMPLEMENTATION` issues + newly spawned issues from investigations.
215
+
216
+ ### Step 2E: Report Wave 0 results to user
217
+
218
+ ```
219
+ ## Wave 0: Investigations Complete
220
+
221
+ | # | Investigation | Result | Issues Created |
222
+ |---|--------------|--------|---------------|
223
+ | #{INV1} | {title} | ✓ Closed | #{N1}, #{N2}, #{N3} |
224
+ | #{INV2} | {title} | ✓ Closed | #{N4}, #{N5} |
225
+
226
+ **New issues added to batch**: {count}
227
+ **Updated total**: {original_count} → {new_total} issues
228
+
229
+ Proceeding to dependency analysis with the expanded issue set...
230
+ ```
231
+
232
+ ---
233
+
234
+ ## Phase 3: Dependency Analysis & Execution Plan
235
+
236
+ ### Step 3A: Analyze explicit dependencies
237
+
238
+ For each issue (including newly spawned ones from Phase 2), check:
239
+ 1. **Explicit dependencies**: Issue body contains "Depends on #X" or "Blocked by #X"
240
+ 2. **Milestone ordering**: Issues within a milestone may have a natural order (foundation → features → UI)
241
+ 3. **File conflicts**: If two issues modify the same files, they should be sequential (not parallel)
242
+ 4. **Parent-child links**: If issue body contains "Parent investigation: #{X}", it was spawned by an investigation — no special ordering needed unless explicitly stated
243
+
244
+ Read each issue's body briefly to check for dependency markers:
245
+ ```bash
246
+ for NUM in {issue_numbers}; do
247
+ gh issue view $NUM --json body --jq '.body' | grep -iE 'depends on|blocked by|after #|parent investigation' || echo "no deps"
248
+ done
249
+ ```
250
+
251
+ ### Step 3B: Domain estimation
252
+
253
+ For each issue, estimate which domains it touches based on title, body, and labels. This improves wave planning — issues in the same domain likely touch the same files and should be sequential.
254
+
255
+ ```bash
256
+ for NUM in {issue_numbers}; do
257
+ ISSUE=$(gh issue view $NUM --json title,body,labels --jq '{title: .title, labels: [.labels[].name], body: (.body[:300])}')
258
+ echo "=== #$NUM ==="
259
+ echo "$ISSUE" | grep -qiE "credit|billing|pricing|stripe|tier.*cost|charge|refund" && echo " BILLING" || true
260
+ echo "$ISSUE" | grep -qiE "auth|session|jwt|login|permission|oauth" && echo " AUTH" || true
261
+ echo "$ISSUE" | grep -qiE "scrape|tier|proxy|playwright|playbook|worker|unified_consumer|penetrat" && echo " SCRAPING" || true
262
+ echo "$ISSUE" | grep -qiE "migration|\.sql|database|postgres|alembic" && echo " DATABASE" || true
263
+ echo "$ISSUE" | grep -qiE "component|page|layout|dashboard|ui|ux|frontend|web/src" && echo " FRONTEND" || true
264
+ echo "$ISSUE" | grep -qiE "docker|deploy|traefik|nginx|ci|cd|infra|github.action" && echo " INFRA" || true
265
+ echo "$ISSUE" | grep -qiE "cortex|llm|extract|schema|format" && echo " CORTEX" || true
266
+ done
267
+ ```
268
+
269
+ **Use domain info for wave planning:**
270
+ - Issues in the SAME domain (especially SCRAPING, BILLING, DATABASE) are more likely to touch the same files → prefer sequential within a wave or adjacent waves
271
+ - Issues in DIFFERENT domains are more likely independent → safe to parallelize
272
+ - BILLING + AUTH issues should be prioritized early (security-critical)
273
+ - **DATABASE issues are ALWAYS serialized — hard rule, no exceptions.** Multiple agents writing migrations simultaneously will produce duplicate migration numbers (e.g., two `0067_*.sql` files), which breaks the migration runner. Every DATABASE issue must be in its own wave. If 3 DATABASE issues are in a batch: Wave 1 runs issue A, Wave 2 runs issue B, Wave 3 runs issue C.
274
+
275
+ **Store domain tags per issue** for use in the plan presentation (Step 3E).
276
+
277
+ ### Step 3C: Multi-layer conflict detection
278
+
279
+ Domain estimation (above) catches broad category overlap but misses cases where two issues modify the exact same file without mentioning the same keywords, or where two issues touch different files that share indirect dependencies (imports, config, barrel exports). This step uses three layers of structural analysis to catch conflicts the keyword heuristic misses.
280
+
281
+ #### Layer 1: Explicit file-overlap extraction
282
+
283
+ **For issues that already have an INVESTIGATOR comment** (from Wave 0 or a prior session), extract their Affected Files list:
284
+
285
+ ```bash
286
+ for NUM in {issue_numbers}; do
287
+ echo "=== #$NUM ==="
288
+ gh api repos/{GH_REPO}/issues/${NUM}/comments \
289
+ --jq '.[] | select(.body | contains("FORGE:INVESTIGATOR")) | .body' 2>/dev/null \
290
+ | grep -oP '`[^`]*\.(py|tsx?|jsx?|sql|json|ya?ml)`' | sort -u
291
+ done
292
+ ```
293
+
294
+ **For issues WITHOUT an investigation comment**, fall back to parsing the issue body for file paths:
295
+
296
+ ```bash
297
+ gh issue view $NUM --json body --jq '.body' | grep -oP '`[^`]*\.(py|tsx?|jsx?|sql|json|ya?ml)`' | sort -u
298
+ ```
299
+
300
+ **Cross-reference all extracted file lists:**
301
+ - If two issues share ANY affected file → they MUST be in separate waves (sequential)
302
+ - The issue with lower issue number goes first (stable ordering), unless an explicit `Depends on #` says otherwise
303
+ - Add a conflict note to the wave plan: "#{A} and #{B} both modify `{file}` — serialized"
304
+
305
+ #### Layer 2: Directory-proximity detection
306
+
307
+ Two issues that modify different files in the **same leaf directory** have a high probability of conflicting through shared `__init__.py`, `index.ts`, barrel re-exports, or tightly coupled sibling modules. This layer catches indirect conflicts that Layer 1 misses.
308
+
309
+ **Extract the leaf directory for each affected file:**
310
+
311
+ ```bash
312
+ # For each issue's file list, extract unique directories
313
+ for NUM in {issue_numbers}; do
314
+ echo "=== #$NUM directories ==="
315
+ # From the file list already extracted in Layer 1:
316
+ echo "$FILES_FOR_NUM" | xargs -I{} dirname {} | sort -u
317
+ done
318
+ ```
319
+
320
+ **Cross-reference directory lists:**
321
+ - If two issues share a leaf directory AND that directory is "small" (contains fewer than 10 tracked files), flag as **probable conflict** → serialize
322
+ - If the shared directory is a broad container (e.g., `services/api/app/routers/` with 15+ files), downgrade to **possible conflict** → serialize only if same domain tag (from Step 3B)
323
+ - **Known high-conflict directories** (always serialize if shared):
324
+ - `services/api/app/models/` (SQLAlchemy models often share Base, imports)
325
+ - `services/api/app/core/` (shared dependencies)
326
+ - `services/worker/worker/` (tightly coupled consumer modules)
327
+ - `web/src/lib/` (shared utilities)
328
+ - `shared/` (volume-mounted, affects all services)
329
+ - `infra/migrations/` (already covered by DATABASE hard rule, but explicit here too)
330
+
331
+ #### Layer 3: Shared-module inference
332
+
333
+ When two issues modify different files that **import from the same utility/init module**, both agents often end up modifying that shared module (adding imports, updating re-exports). This creates merge conflicts invisible to Layer 1.
334
+
335
+ **Heuristic rules (no git operations needed — pattern-based):**
336
+
337
+ | Pattern | Inference | Action |
338
+ |---------|-----------|--------|
339
+ | Issue A modifies `routers/billing.py`, Issue B modifies `routers/auth.py` | Both likely import from `routers/__init__.py` or `dependencies.py` | Flag as possible conflict if same service |
340
+ | Issue A modifies `models/user.py`, Issue B modifies `models/subscription.py` | Both likely modify `models/__init__.py` (SQLAlchemy model registry) | Flag as probable conflict → serialize |
341
+ | Issue A modifies `web/src/components/X.tsx`, Issue B modifies `web/src/components/Y.tsx` | May share `index.ts` barrel export | Flag only if same parent directory |
342
+ | Issue A modifies `services/api/app/main.py` | `main.py` is a high-fan-in file (router registration, middleware) | Serialize with ANY other API issue |
343
+ | Issue A modifies `docker-compose.yml` or `docker-compose.prod.yml` | Global config — any concurrent modification conflicts | Serialize with ALL other issues that touch infra |
344
+
345
+ **Apply inferences:**
346
+ ```
347
+ # High-fan-in files — if ANY issue touches these, serialize it with all same-service issues:
348
+ HIGH_FAN_IN = [
349
+ "services/api/app/main.py",
350
+ "services/worker/worker/unified_consumer.py",
351
+ "web/src/app/layout.tsx",
352
+ "docker-compose.yml",
353
+ "docker-compose.prod.yml",
354
+ ".env.example"
355
+ ]
356
+
357
+ # For each issue, check if affected files include a high-fan-in file
358
+ # If yes: that issue cannot be parallelized with any other issue touching the same service
359
+ ```
360
+
361
+ #### Layer 4: Conservative fallback (low-confidence cases)
362
+
363
+ When file extraction yields **fewer than 2 file paths** for an issue (common for issues without investigation comments, or issues described in prose without backtick-wrapped paths), the conflict detection has low confidence. Rather than assuming independence, apply conservative serialization.
364
+
365
+ **Rules:**
366
+ - If an issue has 0-1 extracted file paths AND shares a domain tag (from Step 3B) with another issue → serialize them
367
+ - If an issue has 0 extracted file paths AND no domain tag could be determined → serialize it with the immediately preceding wave (safest default)
368
+ - Add a note to the plan: "#{N} — low file-extraction confidence, serialized conservatively"
369
+
370
+ **Rationale**: The cost of a false-negative (two agents conflict → one fails with merge error, wasting the full agent run) far exceeds the cost of a false-positive (an issue waits one extra wave before starting). Always err toward serialization when uncertain.
371
+
372
+ #### Combining all layers
373
+
374
+ Build the final conflict graph by merging signals from all four layers:
375
+
376
+ | Signal | Strength | Action |
377
+ |--------|----------|--------|
378
+ | Layer 1: Same file | **Hard conflict** | Always serialize |
379
+ | Layer 2: Same small directory | **Probable conflict** | Serialize |
380
+ | Layer 2: Same broad directory + same domain | **Probable conflict** | Serialize |
381
+ | Layer 2: Same broad directory + different domain | **Possible conflict** | Parallelize (accept risk) |
382
+ | Layer 3: High-fan-in file touched | **Probable conflict** | Serialize with same-service issues |
383
+ | Layer 3: Shared model/init pattern | **Probable conflict** | Serialize |
384
+ | Layer 4: Low confidence + same domain | **Conservative** | Serialize |
385
+ | Layer 4: Low confidence + no domain | **Conservative** | Serialize with prior wave |
386
+
387
+ **This supplements, not replaces, the domain keyword estimation.** Domain tags still help with broad sequencing decisions. Multi-layer conflict detection catches the specific cases keywords miss (e.g., two issues that both modify files in `services/api/app/models/` where one is labeled SCRAPING and the other BILLING — Layer 2 catches this even though Layer 1 shows no direct file overlap).
388
+
389
+ ### Step 3D: Build the execution plan
390
+
391
+ Organize issues into **waves** (groups that can run in parallel):
392
+
393
+ **Wave rules:**
394
+ - Investigation issues already ran in Wave 0 (Phase 2) — they are NOT included in these waves
395
+ - Issues with NO dependencies and NO file overlap → same wave (parallel)
396
+ - Issues that depend on other issues in the set → later wave (after dependency completes)
397
+ - **No artificial agent limit per wave** — spawn as many parallel agents as there are independent issues. The only constraint is file overlap and explicit dependencies.
398
+ - Issues that touch the **same files** must be in separate waves (sequential) to avoid merge conflicts
399
+ - If unsure about conflicts, err on the side of parallel — each agent works in its own worktree
400
+
401
+ **Example plan:**
402
+ ```
403
+ Wave 0 (already done): #2644 (investigation) → spawned #2645, #2646, #2647
404
+ Wave 1 (parallel): #2633 (orphaned queues), #2636 (invalidation sub), #2645 (new finding), #2646 (new finding)
405
+ Wave 2 (after deps): #2634 (enable daemon — after #2633 fixes memory leak), #2647 (depends on #2645)
406
+ ```
407
+
408
+ ### Step 3E: Present the plan to the user
409
+
410
+ ```
411
+ ## Orchestration Plan
412
+
413
+ **Scope**: {milestone name / "N issues" / "fast-lane"}
414
+ **Total issues**: {count} ({investigation_count} investigations + {implementation_count} implementations)
415
+ **Estimated waves**: {wave_count} (Wave 0 = investigations, then implementation waves)
416
+
417
+ {IF investigations exist:}
418
+ ### Wave 0 — Investigations (run first, may spawn new issues)
419
+ | # | Title | Expected Output |
420
+ |---|-------|----------------|
421
+ | #{INV1} | {title} | New issues → folded into subsequent waves |
422
+
423
+ ### Implementation Waves (after investigations complete)
424
+ {END IF}
425
+
426
+ ### Domain Distribution
427
+ | Domain | Issues | Notes |
428
+ |--------|--------|-------|
429
+ | SCRAPING | {N} | {High overlap risk / Independent targets} |
430
+ | FRONTEND | {N} | {Independent pages / Shared components} |
431
+ | BILLING | {N} | {Critical — prioritize in Wave 1} |
432
+ | DATABASE | {N} | {Sequential — migration order matters} |
433
+ | AUTH | {N} | {Critical — Wave 1} |
434
+ | INFRA | {N} | {Independent} |
435
+ | CORTEX | {N} | {Independent} |
436
+
437
+ (Omit rows with 0 issues.)
438
+
439
+ ### Implementation Waves
440
+
441
+ | Wave | Issues | Strategy |
442
+ |------|--------|----------|
443
+ | 1 | #{A}, #{B}, #{C} | Parallel (no deps, different domains) |
444
+ | 2 | #{D} (after #{A}), #{E} | Parallel (D waits for A) |
445
+ | 3 | #{F} (after #{D}) | Sequential |
446
+
447
+ **Note**: Wave 0 investigations may create additional issues that will be automatically added to the implementation waves. The final wave plan will be confirmed after investigations complete.
448
+
449
+ **Excluded** (already in progress / ineligible):
450
+ - #{X} — {reason}
451
+
452
+ Proceed? (yes / adjust / pick specific issues)
453
+ ```
454
+
455
+ **Wait for user confirmation before spawning agents.** This is the checkpoint — once agents launch, they run autonomously.
456
+
457
+ **After confirmation**: If Wave 0 exists, execute Phase 2B-E first. Then re-present the expanded plan (with newly spawned issues slotted into waves) for a quick confirmation before launching implementation waves.
458
+
459
+ ---
460
+
461
+ ## Phase 4: Execute Waves
462
+
463
+ ### Step 4A-pre: Capture staging baseline (MANDATORY before each wave)
464
+
465
+ **WHY THIS EXISTS**: Milestone-code-onto-staging contamination incidents (see issue #150) produce unexpected growth on the staging branch that is otherwise invisible until after a deploy. Capturing a line-count baseline before each wave and re-checking after allows early detection of any contamination by agents in that wave.
466
+
467
+ **When to run**: Before launching EVERY wave in batches that target `staging` (fast-lane or milestone→staging). Skip only for pure milestone-branch batches where `{PR_BASE}` is `milestone/*` and staging is not involved.
468
+
469
+ ```bash
470
+ # Capture staging baseline before launching the wave
471
+ git fetch origin
472
+ STAGING_LINES_BEFORE=$(git diff --stat origin/main...origin/staging 2>/dev/null \
473
+ | tail -1 \
474
+ | grep -oP '\d+ insertion' \
475
+ | grep -oP '\d+' \
476
+ || echo "0")
477
+ echo "Staging baseline before Wave {N}: ${STAGING_LINES_BEFORE} lines ahead of main"
478
+ ```
479
+
480
+ Store `STAGING_LINES_BEFORE` per wave. After the wave completes (after Step 4C), run the integrity check:
481
+
482
+ ```bash
483
+ # Check staging integrity after wave completes (run after Step 4C)
484
+ git fetch origin
485
+ STAGING_LINES_AFTER=$(git diff --stat origin/main...origin/staging 2>/dev/null \
486
+ | tail -1 \
487
+ | grep -oP '\d+ insertion' \
488
+ | grep -oP '\d+' \
489
+ || echo "0")
490
+ STAGING_GROWTH=$((STAGING_LINES_AFTER - STAGING_LINES_BEFORE))
491
+
492
+ echo "Staging after Wave {N}: ${STAGING_LINES_AFTER} lines (+${STAGING_GROWTH} vs baseline)"
493
+
494
+ # Alert if growth exceeds expected delta
495
+ # Expected: only the lines from PRs merged in this wave
496
+ # Alert threshold: growth > 500 lines more than the sum of changed lines from wave PRs
497
+ # (Use 0 as threshold if no PRs merged — any growth is unexpected)
498
+ EXPECTED_DELTA={SUM_OF_PR_LINE_COUNTS_THIS_WAVE} # set from PR diffs collected in 4B
499
+ UNEXPECTED_GROWTH=$((STAGING_GROWTH - EXPECTED_DELTA))
500
+ if [ "$UNEXPECTED_GROWTH" -gt 500 ]; then
501
+ echo "ALERT: Staging grew by ${STAGING_GROWTH} lines (+${UNEXPECTED_GROWTH} beyond expected ${EXPECTED_DELTA})."
502
+ echo "This may indicate milestone-code contamination via agent merge commits."
503
+ echo "Review: git log --oneline --merges origin/main..origin/staging"
504
+ echo "Do NOT merge staging → main until the unexpected growth is investigated."
505
+ # Do NOT auto-stop — alert the user and let them decide
506
+ fi
507
+ ```
508
+
509
+ If `UNEXPECTED_GROWTH > 500`, report the alert clearly before launching the next wave. The user confirms whether to continue.
510
+
511
+ ---
512
+
513
+ ### Step 4A: Launch a wave
514
+
515
+ **REMINDER: You MUST use the template below verbatim. Only fill in `{VARIABLES}`. Do NOT rewrite the agent prompt. Do NOT write custom implementation instructions. The agent MUST invoke `/work-on` via the Skill tool — this is the HARD RULE from the top of this file.**
516
+
517
+ For each issue in the current wave, spawn an Agent sub-agent that runs the full `/work-on` pipeline.
518
+
519
+ **One agent per issue.** Do NOT group multiple issues into a single agent. `/work-on` handles branching, labels, and PRs per-issue.
520
+
521
+ **Copy this template. Fill in variables. Do not modify the structure:**
522
+
523
+ ```
524
+ Agent(
525
+ subagent_type="general-purpose",
526
+ model="sonnet",
527
+ description="Work on {PROJECT_PREFIX}#{NUMBER}",
528
+ run_in_background=true,
529
+ prompt="You are working on GitHub issue #{NUMBER} for the AlterLab ecosystem.
530
+
531
+ **Project**: {PROJECT_NAME}
532
+ **Repository**: {GH_REPO}
533
+ **Repo path**: {REPO_PATH}
534
+
535
+ **YOUR MISSION**: Invoke `/work-on` via the Skill tool and let it run to completion. `/work-on` is a self-contained routing loop that handles the ENTIRE pipeline: investigate → build (context → architect → implement → validate) → review (push → PR → /review-pr --auto-merge) → close (project board → trajectory log → worktree cleanup). Do NOT intervene, compensate, or manually close issues — `/work-on` handles everything including issue closure and label updates in its close phase.
536
+
537
+ **CRITICAL — DO NOT STOP EARLY**: /work-on runs as a multi-phase routing loop. Each phase (investigate, build, review, close) returns an intermediate result — these are NOT completion signals. You are NOT done until the issue reaches a terminal state: `workflow:merged`, `workflow:invalid`, or `needs-human`. If /work-on returns after only one phase (e.g., investigation), you MUST invoke it again immediately — it will re-read GitHub state and continue to the next phase. Keep invoking /work-on until it reaches a terminal state. Never output 'done' or stop after an intermediate result.
538
+
539
+ **HOW REVIEW FINDINGS WORK**: /review-pr may create GitHub issues (with `review-finding` label) for findings it discovers. These are NOT blockers — they are separate work items that will go through their own /work-on pipeline later. The original PR should ALWAYS merge after review. The only exception is build errors (code doesn't compile) — those must be fixed before merging.
540
+
541
+ **IMPORTANT RULES**:
542
+ - **MANDATORY**: You MUST use the Skill tool to invoke 'work-on' with args '{PROJECT_PREFIX}{NUMBER}'. Do NOT implement manually — /work-on handles the full pipeline including label state machine (workflow:investigating → workflow:building → workflow:in-review → workflow:merged), investigation reports, PR creation, and cleanup.
543
+ - For AlterLab issues: `Skill(skill='work-on', args='{NUMBER}')`
544
+ - For MCP issues: `Skill(skill='work-on', args='mcp:{NUMBER}')`
545
+ - For n8n issues: `Skill(skill='work-on', args='n8n:{NUMBER}')`
546
+ - NEVER bypass /work-on with manual git/gh commands — the label updates and structured comments are critical for tracking
547
+ - NEVER target `main` for AlterLab PRs. Use `staging` for fast-lane issues, or `milestone/{slug}` for milestone issues.
548
+ - Satellite repos (MCP, n8n) have no staging branch — fast-lane PRs go to `main` for those.
549
+ - If the issue is INVALID after investigation, close it with a comment explaining why
550
+ - If you hit merge conflicts or blockers, post a comment on the issue and STOP — do not force anything
551
+ - Do not interact with the user — you are running autonomously in the background
552
+ - **NEVER ask the user questions** — you are a background agent. If review finds issues, auto-fix simple ones and proceed. For complex findings, merge anyway and create follow-up issues.
553
+
554
+ **LABEL-STATE LOOP CONTRACT — enforce after EVERY Skill return**:
555
+ After EVERY `Skill(skill='work-on', ...)` call returns, immediately check the issue's current workflow label:
556
+ ```bash
557
+ gh issue view {NUMBER} -R {GH_REPO} --json labels --jq '[.labels[].name | select(startswith("workflow:"))]'
558
+ ```
559
+ **Terminal labels** (only these allow you to stop): `workflow:merged`, `workflow:invalid`
560
+ **Terminal condition also**: `needs-human` label present OR issue state is `closed`
561
+ If the label is NOT terminal (e.g., `workflow:investigating`, `workflow:ready-to-build`, `workflow:building`, `workflow:in-review`), invoke `Skill(skill='work-on', args='{NUMBER}')` again immediately. The `/work-on` skill will re-read GitHub state and advance to the next phase. Do NOT output a summary, do NOT pause, do NOT ask for confirmation — just invoke it again.
562
+
563
+ **CRITICAL — SOURCE BRANCH DETECTION**:
564
+ - If the issue has the `review-finding` label, read the issue body for `**Code branch**: \`{branch}\``
565
+ - If found, that is the SOURCE_BRANCH — the code ONLY exists on that branch (e.g., `staging`), NOT on `origin/main`
566
+ - Investigation MUST use `git show origin/{SOURCE_BRANCH}:{filepath}` to verify the code exists
567
+ - Worktree MUST branch from `origin/{SOURCE_BRANCH}`, NOT `origin/main`
568
+ - PR target is `{SOURCE_BRANCH}` (the fix goes back to where the code lives)
569
+
570
+ **LANE**: {LANE} (PR target: {PR_BASE})
571
+ **Issue title**: {ISSUE_TITLE}
572
+ {GIST_CONTEXT}
573
+ "
574
+ )
575
+ ```
576
+
577
+ **`{GIST_CONTEXT}` generation**: For each issue in the wave, check if it was spawned by an investigation that has a Knowledge Gist (from Step 2C.5). If so, include the Gist URL(s) in the agent prompt:
578
+
579
+ ```bash
580
+ # Build GIST_CONTEXT for an issue
581
+ GIST_CONTEXT=""
582
+ PARENT_INV=$(gh issue view {NUMBER} -R {GH_REPO} --json body --jq '.body' \
583
+ | grep -oP '(?i)parent[: ]*#\K\d+|spawned from[: ]*#\K\d+' | head -1)
584
+
585
+ if [ -n "$PARENT_INV" ] && [ -n "${INVESTIGATION_GISTS[$PARENT_INV]:-}" ]; then
586
+ GIST_CONTEXT="
587
+ **CONTEXT FROM PRIOR INVESTIGATION**: Investigation #${PARENT_INV} produced Knowledge Gist(s) with findings relevant to this issue:
588
+ $(echo "${INVESTIGATION_GISTS[$PARENT_INV]}" | while IFS= read -r url; do echo "- ${url}"; done)
589
+ Fetch the Gist content during the context-gathering phase for implementation guidance."
590
+ fi
591
+
592
+ # Include milestone index URL if available (from Step 2C.5)
593
+ if [ -n "$MILESTONE_INDEX_URL" ]; then
594
+ GIST_CONTEXT="${GIST_CONTEXT}
595
+
596
+ **MILESTONE KNOWLEDGE INDEX**: All investigation findings for this milestone are aggregated in a single index Gist:
597
+ - ${MILESTONE_INDEX_URL}
598
+ The context-gathering phase can fetch this index to discover all investigation Gists for the milestone."
599
+ fi
600
+ ```
601
+
602
+ If `GIST_CONTEXT` is empty (no parent investigation or milestone index found), the variable resolves to a blank line in the template — no impact on the agent prompt. <!-- Updated: forge#341 -->
603
+
604
+ **Launch all agents in the current wave simultaneously** by putting multiple Agent tool calls in a single message. Use `run_in_background=true` so they execute in parallel.
605
+
606
+ ### Step 4B: Monitor wave completion
607
+
608
+ You will be automatically notified when each background agent completes. **Do NOT use `sleep` loops to poll for completion.** Instead, wait for the automatic notification. When you receive a notification that an agent completed, immediately process it.
609
+
610
+ **CRITICAL — Stall detection and recovery**: Background agents sometimes stop mid-pipeline (`stop_reason=end_turn`) after completing a sub-phase (e.g., investigation completes but build never starts). This causes the agent to "complete" from the Agent tool's perspective even though the `/work-on` pipeline is only partially done. When you receive a completion notification:
611
+
612
+ 1. **Check if the agent completed the FULL pipeline** — not just one phase:
613
+ ```bash
614
+ # Check final workflow state — only workflow:merged or workflow:invalid means truly done
615
+ FINAL_STATE=$(gh issue view $NUM -R {GH_REPO} --json labels,state --jq '{state: .state, workflow: [.labels[].name | select(startswith("workflow:"))]}')
616
+ echo "#{NUM}: $FINAL_STATE"
617
+ ```
618
+
619
+ 2. **If the issue is NOT in a terminal state** (`workflow:merged`, `workflow:invalid`, or `needs-human`), the agent stalled mid-pipeline. **Resume it immediately**:
620
+ ```
621
+ Agent(
622
+ resume="{AGENT_ID}",
623
+ description="Resume #{NUMBER} pipeline",
624
+ run_in_background=true,
625
+ prompt="The previous /work-on invocation stopped before completing the full pipeline. The issue is currently at {CURRENT_WORKFLOW_STATE}. Continue — invoke Skill(skill='work-on', args='{NUMBER}') to resume the routing loop from the current state. /work-on will re-read GitHub state and pick up where it left off."
626
+ )
627
+ ```
628
+ **Resume ALL stalled agents in a single message** (parallel resume). Do not wait between resumes.
629
+
630
+ 3. **Track resume cycles per agent.** If an agent has been resumed 3+ times and still hasn't reached a terminal state, report it as a failure — do not resume again.
631
+
632
+ 4. **Record completed results**: Success (PR merged), Invalid (issue closed), Blocked (needs human), or Error
633
+
634
+ 5. **Verify pipeline compliance** — for each truly completed issue, check that the agent used `/work-on`:
635
+ ```bash
636
+ LABELS=$(gh issue view $NUM -R {GH_REPO} --json labels --jq '[.labels[].name | select(startswith("workflow:"))] | length')
637
+ COMMENTS=$(gh api repos/{GH_REPO}/issues/${NUM}/comments --jq '[.[] | select(.body | test("FORGE:INVESTIGATOR|FORGE:BUILDER"))] | length')
638
+ if [ "$LABELS" -eq 0 ] || [ "$COMMENTS" -eq 0 ]; then
639
+ echo "PIPELINE FAILURE: #{NUM} — agent bypassed /work-on (no labels or structured comments)"
640
+ fi
641
+ ```
642
+ If an agent bypassed the pipeline, report it as a **failure** regardless of whether a PR exists.
643
+
644
+ 6. **Post a brief status update** to the user after each agent reaches terminal state:
645
+ ```
646
+ ✓ #{NUMBER} — {title} → PR #{PR} merged to {target}
647
+ ✗ #{NUMBER} — {title} → {reason for failure}
648
+ ⚠ #{NUMBER} — {title} → PIPELINE BYPASS (no /work-on — PR invalid)
649
+ ⏳ Wave 1: 2/3 complete (1 resumed after stall)...
650
+ ```
651
+
652
+ **Anti-pattern — DO NOT DO THIS:**
653
+ - `sleep 60/120/180/300` loops to check status — you will be notified automatically
654
+ - Spawning separate "progress check" agents — they waste tokens and add noise
655
+ - Reading agent JSONL output files to check progress — use GitHub labels as the source of truth
656
+ - Polling the same status check repeatedly on a timer
657
+
658
+ ### Step 4C: Collect review-finding issues from completed wave
659
+
660
+ When ALL agents in the current wave have completed, check if any `/work-on` runs spawned review-finding issues during their review phase. These are new work items that should be folded into the remaining waves.
661
+
662
+ ```bash
663
+ # Method 1: Read TRAJECTORY comments from completed issues for "Finding issues" row
664
+ for NUM in {completed_issue_numbers_this_wave}; do
665
+ gh api repos/{GH_REPO}/issues/${NUM}/comments \
666
+ --jq '.[] | select(.body | contains("FORGE:TRAJECTORY")) | .body' 2>/dev/null \
667
+ | grep -oP 'Finding issues\s*\|\s*#?\K\d+[^|]*' | grep -oP '\d+' | sort -u
668
+ done
669
+
670
+ # Method 2 (fallback): Check for recently created review-finding issues that reference PRs from this wave
671
+ gh issue list -R {GH_REPO} --state open --label "review-finding" --limit 20 \
672
+ --json number,title,body,createdAt \
673
+ --jq "[.[] | select(.createdAt > \"$(date -u -d '2 hours ago' +%Y-%m-%dT%H:%M:%SZ)\")]"
674
+ ```
675
+
676
+ **If review-finding issues were spawned:**
677
+
678
+ **Cascade control (MANDATORY — run before folding findings into waves):**
679
+
680
+ For each spawned finding, determine whether it should be **executed** or **deferred**:
681
+
682
+ **Evaluation order** (first matching rule wins):
683
+ 1. **Generation ≥ 2** (always defer, even for P1/P2): Finding was spawned by an issue that was itself a review-finding. Check the source issue's labels for `review-finding` — if the source has that label, the new finding is generation 2. Always defer. Rationale: gen-2+ cascade is theoretically unbounded — cap it here.
684
+ 2. **Priority override** (P1 or P2 → always execute): If the finding is labeled P1 or P2, skip all remaining heuristics and execute. Rationale: high-priority findings must never be suppressed by keyword matching.
685
+ 3. **Comment/typo heuristic** (P3 and below only): Finding title contains the word "comment" or "typo" (case-insensitive). These are 1-line cosmetic fixes that do not block other work.
686
+ 4. **P3 + same-file overlap**: Finding is labeled `P3` AND the file it targets overlaps with ANY file already in the current or remaining batch waves. Rationale: same-file P3 findings serialize the entire remaining batch — one finding per original issue doubles wall-clock time with no proportional value.
687
+
688
+ **Defer** (do NOT add to wave queue) if rules 1, 3, or 4 match.
689
+
690
+ **Execute** (add to wave queue) if:
691
+ - Rule 2 matches (P1 or P2)
692
+ - None of the defer rules matched (generation 1, P3 with no file overlap, not a keyword match)
693
+
694
+ **Before running the loop, build the batch file list (MANDATORY for Heuristic 3):**
695
+
696
+ Collect all file paths from every issue in the current batch — both completed waves and remaining queued waves. This produces `ALL_BATCH_FILES`, a newline-separated list of file paths used by Heuristic 3 to test same-file overlap.
697
+
698
+ ```bash
699
+ # Build ALL_BATCH_FILES: collect file paths from ALL batch issues (completed + queued)
700
+ # Use the same extraction pattern as Step 3C Layer 1
701
+ ALL_BATCH_FILES=""
702
+ for NUM in {all_batch_issue_numbers}; do
703
+ # Try INVESTIGATOR comment first (most reliable source of affected files)
704
+ FILES=$(gh api repos/{GH_REPO}/issues/${NUM}/comments \
705
+ --jq '.[] | select(.body | contains("FORGE:INVESTIGATOR")) | .body' 2>/dev/null \
706
+ | grep -oP '`[^`]*\.(py|tsx?|jsx?|sql|json|ya?ml|sh|md)`' | tr -d '`' | sort -u)
707
+ # Fall back to issue body if no investigator comment
708
+ if [ -z "$FILES" ]; then
709
+ FILES=$(gh issue view $NUM -R {GH_REPO} --json body --jq '.body' \
710
+ | grep -oP '`[^`]*\.(py|tsx?|jsx?|sql|json|ya?ml|sh|md)`' | tr -d '`' | sort -u)
711
+ fi
712
+ ALL_BATCH_FILES=$(printf '%s\n%s' "$ALL_BATCH_FILES" "$FILES")
713
+ done
714
+ ALL_BATCH_FILES=$(echo "$ALL_BATCH_FILES" | sort -u | grep -v '^$')
715
+ ```
716
+
717
+ ```bash
718
+ # For each finding, check its priority label and generation
719
+ for FINDING_NUM in {spawned_finding_numbers}; do
720
+ FINDING_DATA=$(gh issue view $FINDING_NUM -R {GH_REPO} --json labels,title,body \
721
+ --jq '{labels: [.labels[].name], title: .title, body: .body}')
722
+
723
+ PRIORITY=$(echo "$FINDING_DATA" | jq -r '.labels[] | select(startswith("P")) | .' | head -1)
724
+ TITLE=$(echo "$FINDING_DATA" | jq -r '.title')
725
+
726
+ # Heuristic 1: Generation check — source issue has review-finding label (always defer, even for P1/P2)
727
+ if SOURCE_NUM=$(echo "$FINDING_DATA" | jq -r '.body' | grep -oP '(?i)spawned from issue #\K\d+|source issue[: #]+\K\d+' | head -1) && \
728
+ [ -n "$SOURCE_NUM" ] && \
729
+ gh issue view $SOURCE_NUM -R {GH_REPO} --json labels --jq '[.labels[].name]' 2>/dev/null | grep -q "review-finding"; then
730
+ DEFER=true; DEFER_REASON="generation >= 2 (source #${SOURCE_NUM} is also a review-finding)"
731
+ # Priority override: P1 or P2 always execute — skip remaining heuristics
732
+ elif [ "$PRIORITY" = "P1" ] || [ "$PRIORITY" = "P2" ]; then
733
+ DEFER=false
734
+ # Heuristic 2: Comment/typo keyword (only applies to P3 and below)
735
+ elif echo "$TITLE" | grep -qi "comment\|typo"; then
736
+ DEFER=true; DEFER_REASON="comment/typo heuristic"
737
+ # Heuristic 3: P3 + same-file overlap
738
+ elif [ "$PRIORITY" = "P3" ]; then
739
+ # Extract file target from finding body (look for code block or backtick path)
740
+ FINDING_FILE=$(echo "$FINDING_DATA" | jq -r '.body' | grep -oP '`[^\`]+\.(py|ts|tsx|sh|md)`' | head -1 | tr -d '`')
741
+ if [ -n "$FINDING_FILE" ] && echo "$ALL_BATCH_FILES" | grep -qF "$FINDING_FILE"; then
742
+ DEFER=true; DEFER_REASON="P3 + same file as batch: $FINDING_FILE"
743
+ else
744
+ DEFER=false
745
+ fi
746
+ else
747
+ DEFER=false
748
+ fi
749
+
750
+ if [ "$DEFER" = "true" ]; then
751
+ DEFERRED_FINDINGS+=($FINDING_NUM)
752
+ echo "Deferred #${FINDING_NUM}: $DEFER_REASON"
753
+ else
754
+ QUEUED_FINDINGS+=($FINDING_NUM)
755
+ fi
756
+ done
757
+ ```
758
+
759
+ **For queued (non-deferred) findings:**
760
+
761
+ 1. **Add them to the remaining wave plan.** They are implementation issues — same as issues spawned by investigations in Phase 2.
762
+ 2. **Respect source branch context.** Review-finding issues have `**Code branch**: \`{branch}\`` in their body — the `/work-on` agent will read this and branch from the right origin. No special handling needed from the orchestrator.
763
+ 3. **Report to user:**
764
+ ```
765
+ Wave {N} review spawned {count} new finding issues: #{A}, #{B}
766
+ Queued for execution: #{A}, #{B}
767
+ Deferred (cascade control): #{C} (P3 same-file), #{D} (comment heuristic)
768
+ Adding queued findings to Wave {N+1} (or new wave if no remaining waves).
769
+ ```
770
+ 4. **Re-run file-overlap detection** (Step 3C) on the expanded issue set — finding issues may conflict with planned issues that touch the same files.
771
+
772
+ **For deferred findings:**
773
+
774
+ They remain open in GitHub — they will be picked up by future `/orchestrate` or `/work-on` runs. Log them in the final batch summary under "deferred" so they are visible. Do NOT close or label them — leave them for the next pipeline pass.
775
+
776
+ **If no review-finding issues were spawned:** Continue to next wave normally.
777
+
778
+ ### Step 4D: Milestone integration build gate (MANDATORY between waves)
779
+
780
+ **WHY THIS EXISTS**: Session Intelligence milestone shipped 116 PRs across multiple waves with zero integration testing. Each PR built in isolation — type errors from cross-PR interactions (wrong prop types, missing components, incompatible interfaces) were invisible until the milestone→staging merge broke the build with 4 distinct errors. This gate catches those failures early.
781
+
782
+ **When to run**: After each wave completes, IF the batch targets a milestone branch AND any `.tsx`/`.ts` files were changed by agents in the completed wave.
783
+
784
+ ```bash
785
+ # Check if this is a milestone batch with TypeScript changes
786
+ MILESTONE_BRANCH="milestone/{milestone_slug}"
787
+ TS_CHANGED=$(git diff origin/{DEFAULT_BRANCH}...origin/${MILESTONE_BRANCH} --name-only | grep -E '\.(tsx?|jsx?)$' | head -1)
788
+
789
+ if [ -n "$TS_CHANGED" ]; then
790
+ echo "=== Integration Build Gate (TypeScript): Wave {N} ==="
791
+ cd {REPO_PATH}
792
+ git fetch origin ${MILESTONE_BRANCH}
793
+ git checkout origin/${MILESTONE_BRANCH} --detach 2>/dev/null
794
+ cd web && npx tsc --noEmit 2>&1 | head -30
795
+ TSC_EXIT=$?
796
+ if [ "$TSC_EXIT" -eq 0 ]; then
797
+ npx next build 2>&1 | tail -30
798
+ BUILD_EXIT=$?
799
+ fi
800
+ cd .. && git checkout - 2>/dev/null
801
+
802
+ if [ "$TSC_EXIT" -ne 0 ]; then
803
+ echo "BLOCKING: TypeScript errors on ${MILESTONE_BRANCH} after Wave {N}."
804
+ echo "Fix type errors before starting next wave."
805
+ elif [ "${BUILD_EXIT:-0}" -ne 0 ]; then
806
+ echo "BLOCKING: next build failed on ${MILESTONE_BRANCH} after Wave {N}."
807
+ echo "SSG prerender or build errors — fix before starting next wave."
808
+ fi
809
+ fi
810
+
811
+ # Python format check
812
+ PY_CHANGED=$(git diff origin/{DEFAULT_BRANCH}...origin/${MILESTONE_BRANCH} --name-only | grep -E '\.py$' | head -1)
813
+ if [ -n "$PY_CHANGED" ]; then
814
+ echo "=== Integration Build Gate (Python): Wave {N} ==="
815
+ cd {REPO_PATH}
816
+ git checkout origin/${MILESTONE_BRANCH} --detach 2>/dev/null
817
+ cd services/api && poetry run black --check app/ 2>&1 | tail -10
818
+ BLACK_EXIT=$?
819
+ poetry run isort --check app/ 2>&1 | tail -10
820
+ ISORT_EXIT=$?
821
+ cd ../.. && git checkout - 2>/dev/null
822
+
823
+ if [ "$BLACK_EXIT" -ne 0 ] || [ "$ISORT_EXIT" -ne 0 ]; then
824
+ echo "WARNING: Python formatting issues on ${MILESTONE_BRANCH} after Wave {N}."
825
+ echo "Not blocking but should be fixed before milestone→staging."
826
+ fi
827
+ fi
828
+ ```
829
+
830
+ **If the gate fails**: Report the errors to the user. Do NOT proceed to the next wave. The accumulated milestone branch has integration errors that will only get worse with more PRs on top. `next build` failures are BLOCKING (not just `tsc`) — SSG prerender crashes are invisible to type checking.
831
+
832
+ ### Step 4E: Advance to next wave
833
+
834
+ When wave results are processed, the integration gate passes (or is not applicable), and any new finding issues are folded in:
835
+
836
+ 1. Check if any failures in the current wave block the next wave (dependency failures)
837
+ 2. If a dependency failed, skip dependent issues in the next wave and report them as "skipped — dependency #{X} failed"
838
+ 3. Launch the next wave (same process as Step 4A)
839
+
840
+ ### Step 4F: Handle individual agent failures
841
+
842
+ If an agent reports failure or error:
843
+ - **Merge conflict**: Report to user, mark issue as needing human attention
844
+ - **Invalid issue**: Already handled by the agent (closed with comment) — just report it
845
+ - **Build/test failure**: Report the error, suggest manual intervention
846
+ - **Agent timeout**: Report which issue timed out, suggest re-running with `/work-on #{N}`
847
+
848
+ **Do NOT retry failed agents automatically.** Report the failure and let the user decide.
849
+
850
+ ---
851
+
852
+ ## Phase 5: Post-Batch Cleanup
853
+
854
+ **This phase is MANDATORY after every orchestration batch.** It prevents the rot that accumulates when multiple agents merge PRs in parallel — stale labels, orphaned worktrees, unclosed issues.
855
+
856
+ ### Step 5A: Run cleanup sweep
857
+
858
+ Invoke the `/cleanup` skill with `all` to sweep everything:
859
+
860
+ ```
861
+ Skill(skill="cleanup", args="all")
862
+ ```
863
+
864
+ This will:
865
+ - Fix stale workflow labels on any issues the agents left behind
866
+ - Close orphaned open issues whose PRs were merged (common when merging to `staging`)
867
+ - Remove worktrees created by agents in this batch
868
+ - Delete local/remote branches for merged PRs
869
+ - Report milestones that hit 0 open issues — these are ready for `/milestone ship` (staging review + merge). Do NOT close them; closure happens after code reaches staging.
870
+ - Sync Project board state
871
+
872
+ ### Step 5B: Run agent audit
873
+
874
+ Invoke `/audit-agents` on this session to measure pipeline efficiency:
875
+
876
+ ```
877
+ Skill(skill="audit-agents", args="latest")
878
+ ```
879
+
880
+ Include the audit summary in the final report (Phase 6). Key metrics to surface:
881
+ - **Avg idle%** — percentage of time agents spent stalled vs working
882
+ - **Resume cycles** — how many times agents had to be resumed
883
+ - **Stall boundaries** — which phase transitions cause the most stalls
884
+
885
+ ### Step 5C: Report cleanup results
886
+
887
+ Include the cleanup summary in the final report (Phase 6). If cleanup found problems, call them out — they indicate agent pipeline failures that may need investigation.
888
+
889
+ ---
890
+
891
+ ## Phase 6: Consolidated Report
892
+
893
+ After ALL waves AND cleanup complete, present a final summary.
894
+
895
+ ### Step 6A: Collect trajectory data
896
+
897
+ ```bash
898
+ for NUM in {all_completed_issue_numbers}; do
899
+ gh api repos/{GH_REPO}/issues/${NUM}/comments \
900
+ --jq '.[] | select(.body | contains("<!-- FORGE:TRAJECTORY -->")) | .body' 2>/dev/null
901
+ done
902
+ ```
903
+
904
+ Aggregate into the batch-level analytics for Step 6B.
905
+
906
+ ### Step 6B: Present consolidated report
907
+
908
+ ```
909
+ ## Orchestration Complete
910
+
911
+ **Scope**: {milestone / issue list}
912
+ **Duration**: {approximate time}
913
+
914
+ ### Wave 0: Investigations
915
+ {IF investigations ran:}
916
+ | # | Investigation | Issues Created | Result |
917
+ |---|-------------|---------------|--------|
918
+ | #{INV1} | {title} | #{N1}, #{N2}, #{N3} | ✓ Closed |
919
+ {ELSE: "No investigations in this batch."}
920
+
921
+ ### Implementation Results
922
+
923
+ | # | Issue | Source | Result | PR | Target |
924
+ |---|-------|--------|--------|----|--------|
925
+ | #{A} | {title} | original | ✓ Merged | #{PR} | staging |
926
+ | #{B} | {title} | original | ✓ Merged | #{PR} | milestone/x |
927
+ | #{N1} | {title} | from #{INV1} | ✓ Merged | #{PR} | staging |
928
+ | #{C} | {title} | original | ✗ Invalid | — | — |
929
+ | #{D} | {title} | original | ⚠ Blocked | — | — |
930
+ | #{E} | {title} | original | ⏭ Skipped (dep) | — | — |
931
+
932
+ ### Review-Spawned Issues
933
+
934
+ {IF review findings were created during any wave:}
935
+ | # | Finding | Source Issue | Source PR | Status |
936
+ |---|---------|-------------|-----------|--------|
937
+ | #{F1} | {title} | #{A} | PR #{PR} | ✓ Merged (wave {N}) / ⏳ Open (deferred) |
938
+
939
+ {ELSE: "No review findings created during this batch."}
940
+
941
+ ### Summary
942
+ - **Investigations**: {N} completed, spawned {M} new issues
943
+ - **Review findings**: {N} spawned, {M} resolved in-batch, {K} deferred
944
+ - **Succeeded**: {N} issues resolved (implementation)
945
+ - **Failed**: {N} issues need attention
946
+ - **Skipped**: {N} issues (dependency failures)
947
+
948
+ ### Post-Batch Cleanup
949
+ - Labels fixed: {N}
950
+ - Orphaned issues closed: {N}
951
+ - Worktrees removed: {N}
952
+ - Branches pruned: {N}
953
+ - Milestones closed: {N}
954
+
955
+ ### Agent Efficiency (from /audit-agents)
956
+
957
+ | Agent | Issue | Total | Active | Idle% | Resumes | Stall Points |
958
+ |-------|-------|-------|--------|-------|---------|--------------|
959
+ {per-agent rows from audit}
960
+
961
+ **Wave efficiency**: {avg_idle_pct}% idle time
962
+ **Total resume cycles**: {sum_resumes} across {agent_count} agents
963
+ **Clean agents** (no stalls): {clean_count}/{agent_count}
964
+
965
+ ### Batch Trajectory Analytics
966
+
967
+ | Metric | Value |
968
+ |--------|-------|
969
+ | Issues attempted | {N} |
970
+ | Investigation invalidation rate | {N_invalid}/{N_investigated} ({%}) |
971
+ | Contracts posted | {N} |
972
+ | Contract→code divergences | {N} (agents that updated contract mid-build) |
973
+ | Review findings created | {N_total} |
974
+ | Findings after synthesis | {N} (deduplicated/arbitrated) |
975
+ | Findings validated | {N} |
976
+ | False positives | {N} ({%}) |
977
+ | Anomalies flagged | {N} |
978
+
979
+ **Domain breakdown**: {N} scraping, {N} frontend, {N} billing, ...
980
+ **Routing**: {N} fast-lane, {N} feature-lane
981
+
982
+ **Systematic issues** (flag if detected):
983
+ - False positive rate > 30% → review agents may need tuning
984
+ - Invalidation rate < 10% or > 35% → investigation calibration off
985
+ - Same-domain merge conflicts in 2+ waves → domain sequencing too aggressive
986
+ - Contract divergences > 20% → investigation quality may need improvement
987
+ - **Idle% > 50%** → agents stalling at phase boundaries; check work-on routing loop
988
+ - **Avg resumes > 1** → orchestrator having to compensate for agent stops
989
+
990
+ ### Next Steps
991
+ {If milestone and all issues done: "Milestone ready to ship. Run `/milestone ship {slug}` when ready. The ship command includes a pre-merge hunk-loss audit (Step 2.5) that detects staging-only hunks in milestone-modified files and rebases the milestone branch to absorb them before creating the PR — protecting against squash-merge regressions."}
992
+ {If some failed: "Issues #{X}, #{Y} need manual attention. Re-run with `/work-on #{X}` after resolving blockers."}
993
+ {If fast-lane: "All fixes merged to staging. Merge staging → main via GitHub web UI when ready to deploy."}
994
+ ```
995
+
996
+ ---
997
+
998
+ ## Safety Rules
999
+
1000
+ 1. **Every agent MUST invoke `/work-on` via the Skill tool.** No custom prompts. No manual implementation. Copy the Phase 4A template, fill in variables, done. (See HARD RULES at top of file.)
1001
+ 2. **NEVER merge anything to `main`** — agents merge to `staging` or `milestone/{slug}` only
1002
+ 3. **No artificial concurrency limit** — spawn as many agents as there are independent issues. Only file overlap and dependencies require sequencing.
1003
+ 4. **Always confirm with user before launching** — Step 3E is the mandatory checkpoint
1004
+ 5. **No retries** — if an agent fails, report it and move on
1005
+ 6. **Respect existing work** — skip issues already being worked on (`workflow:building`, `workflow:in-review`)
1006
+ 7. **Dependency failures cascade** — if A fails and B depends on A, B is skipped (not attempted)
1007
+ 8. **Always run post-batch cleanup** — Phase 5 is mandatory. Never skip `/cleanup all` after orchestration.
1008
+ 9. **NEVER close/skip issues as duplicates** — only `/work-on` investigation agents can make that call after examining the actual code. The orchestrator delegates, it does not adjudicate.
1009
+ 10. **Post-wave verification** — after each wave, check that every completed issue has `workflow:*` labels and structured comments. If an agent bypassed `/work-on`, report it as a pipeline failure.
1010
+
1011
+ ---
1012
+
1013
+ ## Examples
1014
+
1015
+ ### "orchestrate milestone api-expansion"
1016
+ → Fetches all open issues in "API Expansion v1" milestone
1017
+ → Analyzes deps (#1526 → #1527 → #1528 → #1529, #1530 independent)
1018
+ → Plans 3 waves, confirms with user, executes
1019
+
1020
+ ### "orchestrate #1533 #1250"
1021
+ → Fetches those 2 issues, no deps between them
1022
+ → Single wave, both parallel, confirms, executes
1023
+
1024
+ ### "orchestrate next 5"
1025
+ → Gets top 5 priority issues, checks deps
1026
+ → Plans waves, confirms, executes
1027
+
1028
+ ### "orchestrate P0"
1029
+ → Gets all P0 issues, runs them all (presumably urgent)
1030
+ → Single wave if possible, confirms, executes
1031
+
1032
+ ### "orchestrate fast-lane"
1033
+ → Gets all unmilestoned bugs/fixes
1034
+ → Groups by independence, runs in waves
1035
+
1036
+ ### "orchestrate milestone cortex-recursive-loop"
1037
+ → Fetches all open issues in "Cortex Recursive Loop" milestone
1038
+ → Detects #2644 is an investigation issue (title: "Investigate: Complete the autonomous recursive loop...")
1039
+ → Wave 0: Runs `/work-on 2644` — investigation creates 5 new issues (#2650-#2654)
1040
+ → Re-fetches milestone issues, now includes #2650-#2654
1041
+ → Wave 1: #2633 (P0 orphaned queues), #2636 (invalidation), #2650, #2651 (all independent)
1042
+ → Wave 2: #2634 (enable daemon, after #2633), #2652 (depends on #2650)
1043
+ → Wave 3: remaining issues
1044
+ → Final report shows investigation spawned issues and their results