forgedock 1.0.19 → 1.0.21

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.
@@ -13,9 +13,9 @@ argument-hint: [milestone <slug> | #1 #2 #3 | next <N> | fast-lane | priority:P0
13
13
 
14
14
  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.
15
15
 
16
- 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.
16
+ 2. **You are a dispatcher, not a builder.** You resolve issues, build the dependency DAG, spawn agents, and report results. You NEVER read code, edit files, or implement fixes yourself.
17
17
 
18
- 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.
18
+ 3. **After each agent completes, verify it used `/work-on`.** Check that completed issues have `workflow:*` labels and structured comments. If an agent bypassed the pipeline, report it as a failure.
19
19
 
20
20
  ---
21
21
 
@@ -131,7 +131,7 @@ If a `workflow:decomposed` issue is found, automatically expand it to its open s
131
131
  **What the orchestrator SHOULD do:**
132
132
  - 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"
133
133
  - 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
134
- - Flag potential overlap in the wave plan for the user's awareness, but NEVER act on it
134
+ - Flag potential overlap in the DAG plan for the user's awareness, but NEVER act on it
135
135
 
136
136
  **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.
137
137
 
@@ -274,7 +274,7 @@ done
274
274
 
275
275
  ### Step 3B: Domain estimation
276
276
 
277
- 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.
277
+ For each issue, estimate which domains it touches based on title, body, and labels. This improves DAG construction — issues in the same domain likely touch the same files and should be serialized (one becomes a predecessor of the other).
278
278
 
279
279
  ```bash
280
280
  for NUM in {issue_numbers}; do
@@ -291,11 +291,11 @@ for NUM in {issue_numbers}; do
291
291
  done
292
292
  ```
293
293
 
294
- **Use domain info for wave planning:**
295
- - Issues in the SAME domain (especially WORKER, BILLING, DATABASE) are more likely to touch the same files → prefer sequential within a wave or adjacent waves
294
+ **Use domain info for DAG edge construction:**
295
+ - Issues in the SAME domain (especially WORKER, BILLING, DATABASE) are more likely to touch the same files → add predecessor edges to serialize them
296
296
  - Issues in DIFFERENT domains are more likely independent → safe to parallelize
297
297
  - BILLING + AUTH issues should be prioritized early (security-critical)
298
- - **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.
298
+ - **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. DATABASE issues form a linear predecessor chain in the DAG. If 3 DATABASE issues are in a batch: A has no predecessors, B has {A} as predecessor, C has {B} as predecessor.
299
299
 
300
300
  **Store domain tags per issue** for use in the plan presentation (Step 3E).
301
301
 
@@ -323,9 +323,9 @@ gh issue view $NUM --json body --jq '.body' | grep -oP '`[^`]*\.(py|tsx?|jsx?|sq
323
323
  ```
324
324
 
325
325
  **Cross-reference all extracted file lists:**
326
- - If two issues share ANY affected file → they MUST be in separate waves (sequential)
326
+ - If two issues share ANY affected file → one MUST be a predecessor of the other (serialized)
327
327
  - The issue with lower issue number goes first (stable ordering), unless an explicit `Depends on #` says otherwise
328
- - Add a conflict note to the wave plan: "#{A} and #{B} both modify `{file}` — serialized"
328
+ - Add a conflict note to the DAG plan: "#{A} and #{B} both modify `{file}` — #{A} is predecessor of #{B}"
329
329
 
330
330
  #### Layer 2: Directory-proximity detection
331
331
 
@@ -395,10 +395,10 @@ When file extraction yields **fewer than 2 file paths** for an issue (common for
395
395
 
396
396
  **Rules:**
397
397
  - If an issue has 0-1 extracted file paths AND shares a domain tag (from Step 3B) with another issue → serialize them
398
- - If an issue has 0 extracted file paths AND no domain tag could be determined → serialize it with the immediately preceding wave (safest default)
398
+ - If an issue has 0 extracted file paths AND no domain tag could be determined → add it as a predecessor of the next same-domain issue, or if no domain match exists, serialize it after the most recently added issue (safest default)
399
399
  - Add a note to the plan: "#{N} — low file-extraction confidence, serialized conservatively"
400
400
 
401
- **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.
401
+ **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 for one predecessor before starting). Always err toward serialization when uncertain.
402
402
 
403
403
  #### Combining all layers
404
404
 
@@ -413,29 +413,148 @@ Build the final conflict graph by merging signals from all four layers:
413
413
  | Layer 3: High-fan-in file touched | **Probable conflict** | Serialize with same-service issues |
414
414
  | Layer 3: Shared model/init pattern | **Probable conflict** | Serialize |
415
415
  | Layer 4: Low confidence + same domain | **Conservative** | Serialize |
416
- | Layer 4: Low confidence + no domain | **Conservative** | Serialize with prior wave |
416
+ | Layer 4: Low confidence + no domain | **Conservative** | Add predecessor edge to most recent issue |
417
417
 
418
418
  **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 WORKER and the other BILLING — Layer 2 catches this even though Layer 1 shows no direct file overlap).
419
419
 
420
- ### Step 3D: Build the execution plan
420
+ ### Step 3D: Build the dependency DAG
421
421
 
422
- Organize issues into **waves** (groups that can run in parallel):
422
+ Build a **directed acyclic graph (DAG)** of per-issue dependencies. Each issue gets a `predecessors` set — the specific issues that must reach a terminal state before this issue can dispatch. This replaces the previous wave-grouping model where all issues in a wave had to complete before any issue in the next wave could start.
423
423
 
424
- **Wave rules:**
425
- - Investigation issues already ran in Wave 0 (Phase 2) — they are NOT included in these waves
426
- - Issues with NO dependencies and NO file overlap → same wave (parallel)
427
- - Issues that depend on other issues in the set → later wave (after dependency completes)
428
- - **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.
429
- - Issues that touch the **same files** must be in separate waves (sequential) to avoid merge conflicts
430
- - If unsure about conflicts, err on the side of parallel — each agent works in its own worktree
424
+ **DAG construction rules:**
425
+ - Investigation issues already ran in Phase 2 — they are NOT included in this DAG
426
+ - Each issue starts with an empty predecessor set
427
+ - **Explicit dependencies**: If issue B says "Depends on #A" or "Blocked by #A", add A to B's predecessors
428
+ - **File-conflict edges**: If two issues share affected files (from Step 3C Layer 1), add a directed edge: lower issue number → higher issue number (unless explicit deps say otherwise). The later issue has the earlier issue in its predecessors.
429
+ - **Domain serialization edges**: DATABASE issues form a linear chain (each has the previous DATABASE issue as its predecessor). Same-small-directory issues (Layer 2) and high-fan-in file issues (Layer 3) get directed edges as per Step 3C rules.
430
+ - **Conservative fallback edges**: Low-confidence issues (Layer 4) get edges to same-domain issues as per Step 3C rules.
431
+ - **No artificial concurrency limit** — all issues with empty predecessor sets dispatch simultaneously. The only constraints are file overlap and explicit dependencies.
431
432
 
432
- **Example plan:**
433
+ **Terminology:**
434
+ - **Ready issues**: Issues whose predecessor set is empty (all predecessors have reached terminal state or were never added)
435
+ - **Blocked issues**: Issues with one or more unresolved predecessors
436
+ - **Critical path**: The longest chain of dependent issues in the DAG — determines minimum wall-clock time
437
+
438
+ **Example DAG:**
433
439
  ```
434
- Wave 0 (already done): #2644 (investigation) → spawned #2645, #2646, #2647
435
- Wave 1 (parallel): #2633 (orphaned queues), #2636 (invalidation sub), #2645 (new finding), #2646 (new finding)
436
- Wave 2 (after deps): #2634 (enable daemon — after #2633 fixes memory leak), #2647 (depends on #2645)
440
+ Phase 2 (already done): #2644 (investigation) → spawned #2645, #2646, #2647
441
+
442
+ Dependency graph:
443
+ #2633 (orphaned queues) → predecessors: {} ← READY
444
+ #2636 (invalidation sub) → predecessors: {} ← READY
445
+ #2645 (new finding) → predecessors: {} ← READY
446
+ #2646 (new finding) → predecessors: {} ← READY
447
+ #2634 (enable daemon) → predecessors: {#2633} ← blocked until #2633 completes
448
+ #2647 (depends on #2645) → predecessors: {#2645} ← blocked until #2645 completes
449
+
450
+ Critical path: #2633 → #2634 (2 steps) or #2645 → #2647 (2 steps)
451
+ Initial dispatch: #2633, #2636, #2645, #2646 (all ready — launched simultaneously)
437
452
  ```
438
453
 
454
+ **Key advantage over waves**: When #2633 completes, #2634 dispatches immediately — it does not wait for #2636, #2645, or #2646 to finish. Similarly, when #2645 completes, #2647 dispatches immediately regardless of other issues' status.
455
+
456
+ ### Step 3D.5: Cycle Detection (MANDATORY) <!-- Added: forge#1085 -->
457
+
458
+ **Run immediately after Step 3D's DAG edge construction, before presenting the plan (Step 3E).** This step validates that the predecessor graph is acyclic. Without it, mutual `Depends on` declarations (e.g., A depends on B AND B depends on A) cause both issues to remain permanently blocked in Step 4B's dispatch loop — no error, no timeout, indefinite deadlock.
459
+
460
+ **Algorithm**: Kahn's topological sort. Runs in O(V+E) — negligible overhead for typical batch sizes.
461
+
462
+ ```bash
463
+ # --- Step 3D.5: Cycle Detection ---
464
+ # Inputs:
465
+ # ISSUES[] — array of all issue numbers in the DAG
466
+ # PREDECESSORS[N] — space-separated list of predecessor issue numbers for issue N
467
+
468
+ # Step 1: Compute in-degree for each issue
469
+ declare -A IN_DEGREE
470
+ for NUM in "${ISSUES[@]}"; do
471
+ IN_DEGREE[$NUM]=0
472
+ done
473
+ for NUM in "${ISSUES[@]}"; do
474
+ for PRED in ${PREDECESSORS[$NUM]:-}; do
475
+ IN_DEGREE[$NUM]=$(( ${IN_DEGREE[$NUM]:-0} + 1 ))
476
+ done
477
+ done
478
+
479
+ # Step 2: Seed the queue with zero-in-degree issues (no predecessors)
480
+ KAHN_QUEUE=()
481
+ for NUM in "${ISSUES[@]}"; do
482
+ [ "${IN_DEGREE[$NUM]}" -eq 0 ] && KAHN_QUEUE+=("$NUM")
483
+ done
484
+
485
+ # Step 3: Process queue — reduce successor in-degrees, enqueue newly freed issues
486
+ PROCESSED_COUNT=0
487
+ PROCESSED_ORDER=()
488
+ while [ "${#KAHN_QUEUE[@]}" -gt 0 ]; do
489
+ # Dequeue
490
+ CURRENT="${KAHN_QUEUE[0]}"
491
+ KAHN_QUEUE=("${KAHN_QUEUE[@]:1}")
492
+ PROCESSED_ORDER+=("$CURRENT")
493
+ PROCESSED_COUNT=$(( PROCESSED_COUNT + 1 ))
494
+
495
+ # Reduce in-degree of all issues that depend on CURRENT (i.e., CURRENT is in their PREDECESSORS)
496
+ for SUCCESSOR in "${ISSUES[@]}"; do
497
+ for PRED in ${PREDECESSORS[$SUCCESSOR]:-}; do
498
+ if [ "$PRED" = "$CURRENT" ]; then
499
+ IN_DEGREE[$SUCCESSOR]=$(( ${IN_DEGREE[$SUCCESSOR]} - 1 ))
500
+ [ "${IN_DEGREE[$SUCCESSOR]}" -eq 0 ] && KAHN_QUEUE+=("$SUCCESSOR")
501
+ fi
502
+ done
503
+ done
504
+ done
505
+
506
+ # Step 4: Any issue not processed has in-degree > 0 — part of a cycle
507
+ CYCLE_ISSUES=()
508
+ EXCLUDED_CYCLE=()
509
+ for NUM in "${ISSUES[@]}"; do
510
+ FOUND=false
511
+ for P in "${PROCESSED_ORDER[@]}"; do [ "$P" = "$NUM" ] && FOUND=true && break; done
512
+ [ "$FOUND" = "false" ] && CYCLE_ISSUES+=("$NUM")
513
+ done
514
+
515
+ # Step 5: Handle cycles
516
+ if [ "${#CYCLE_ISSUES[@]}" -gt 0 ]; then
517
+ echo "CYCLE DETECTED in dependency graph — the following issues form a circular dependency:"
518
+ for C in "${CYCLE_ISSUES[@]}"; do
519
+ echo " #${C}: predecessors=[${PREDECESSORS[$C]}]"
520
+ # Label each cyclic issue needs-human
521
+ gh issue edit "$C" -R {GH_REPO} --add-label "needs-human" 2>/dev/null || true
522
+ gh issue comment "$C" -R {GH_REPO} --body "**Cycle detected by /orchestrate**: This issue is part of a circular dependency chain involving issues: ${CYCLE_ISSUES[*]/#/#}. The orchestrator cannot dispatch it automatically. Please fix the \`Depends on\` declarations so that no cycle exists, then re-run /orchestrate." 2>/dev/null || true
523
+ # Remove from DAG — store in EXCLUDED_CYCLE for Step 3E reporting
524
+ EXCLUDED_CYCLE+=("$C")
525
+ # Remove from ISSUES array for all downstream processing
526
+ # Use exact-match filter loop — pattern substitution (${array[@]/pattern}) leaves blank
527
+ # slots and corrupts partial matches (e.g., removing 100 changes 1000 to 0).
528
+ NEW_ISSUES=()
529
+ for I in "${ISSUES[@]}"; do
530
+ [ "$I" != "$C" ] && NEW_ISSUES+=("$I")
531
+ done
532
+ ISSUES=("${NEW_ISSUES[@]}")
533
+ done
534
+ echo ""
535
+ echo "These issues have been labeled needs-human and excluded from the DAG."
536
+ echo "Fix their dependency declarations and re-run /orchestrate."
537
+ else
538
+ echo "DAG cycle check: PASS — no cycles detected. Proceeding with ${#PROCESSED_ORDER[@]} issues."
539
+ fi
540
+
541
+ # Guard: if all issues were cyclic, ISSUES[] is now empty — abort before presenting an empty plan
542
+ if [ "${#ISSUES[@]}" -eq 0 ]; then
543
+ echo ""
544
+ echo "ERROR: All issues in this batch form circular dependencies and have been excluded."
545
+ echo "Every issue has been labeled needs-human."
546
+ echo "Fix the Depends on / Blocked by declarations so no cycle exists, then re-run /orchestrate."
547
+ exit 1
548
+ fi
549
+ # --- End Step 3D.5 ---
550
+ ```
551
+
552
+ **After this step**:
553
+ - `ISSUES[]` contains only acyclic issues — safe to dispatch
554
+ - `EXCLUDED_CYCLE[]` contains cyclic issue numbers — reported in Step 3E, never dispatched
555
+ - If `EXCLUDED_CYCLE` is non-empty, report it clearly in the Step 3E plan before asking for user confirmation
556
+ - If `ISSUES[]` is empty after cycle exclusion (all issues were cyclic), the guard above aborts with `exit 1` — Step 3E is never reached with an empty plan <!-- Added: forge#1110 -->
557
+
439
558
  ### Step 3E: Present the plan to the user
440
559
 
441
560
  ```
@@ -443,103 +562,117 @@ Wave 2 (after deps): #2634 (enable daemon — after #2633 fixes memory leak), #2
443
562
 
444
563
  **Scope**: {milestone name / "N issues" / "fast-lane"}
445
564
  **Total issues**: {count} ({investigation_count} investigations + {implementation_count} implementations)
446
- **Estimated waves**: {wave_count} (Wave 0 = investigations, then implementation waves)
565
+ **Execution model**: Dependency-graph streaming (issues dispatch as predecessors complete)
447
566
 
448
567
  {IF investigations exist:}
449
- ### Wave 0 — Investigations (run first, may spawn new issues)
568
+ ### Investigations (run first, may spawn new issues)
450
569
  | # | Title | Expected Output |
451
570
  |---|-------|----------------|
452
- | #{INV1} | {title} | New issues → folded into subsequent waves |
571
+ | #{INV1} | {title} | New issues → folded into dependency graph |
453
572
 
454
- ### Implementation Waves (after investigations complete)
573
+ ### Implementation (after investigations complete)
455
574
  {END IF}
456
575
 
457
576
  ### Domain Distribution
458
577
  | Domain | Issues | Notes |
459
578
  |--------|--------|-------|
460
579
  | FRONTEND | {N} | {Independent pages / Shared components} |
461
- | BILLING | {N} | {Critical — prioritize in Wave 1} |
462
- | DATABASE | {N} | {Sequential — migration order matters} |
463
- | AUTH | {N} | {Critical — Wave 1} |
580
+ | BILLING | {N} | {Critical — dispatches immediately} |
581
+ | DATABASE | {N} | {Serialized chain — migration order matters} |
582
+ | AUTH | {N} | {Critical — dispatches immediately} |
464
583
  | WORKER | {N} | {High overlap risk within worker service} |
465
584
  | AI | {N} | {Independent} |
466
585
  | INFRA | {N} | {Independent} |
467
586
 
468
587
  (Omit rows with 0 issues. Add project-specific domain rows from forge.yaml → review.domains.)
469
588
 
470
- ### Implementation Waves
589
+ ### Dependency Graph
471
590
 
472
- | Wave | Issues | Strategy |
473
- |------|--------|----------|
474
- | 1 | #{A}, #{B}, #{C} | Parallel (no deps, different domains) |
475
- | 2 | #{D} (after #{A}), #{E} | Parallel (D waits for A) |
476
- | 3 | #{F} (after #{D}) | Sequential |
591
+ | Issue | Predecessors | Domain | Status |
592
+ |-------|-------------|--------|--------|
593
+ | #{A} | — | FRONTEND | Ready (dispatches immediately) |
594
+ | #{B} | — | BILLING | Ready (dispatches immediately) |
595
+ | #{C} | — | WORKER | Ready (dispatches immediately) |
596
+ | #{D} | #{A} | FRONTEND | Blocked (waits for #{A} only) |
597
+ | #{E} | — | DATABASE | Ready (dispatches immediately) |
598
+ | #{F} | #{E} | DATABASE | Blocked (serialized — waits for #{E}) |
477
599
 
478
- **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.
600
+ **Critical path**: #{E} → #{F} (2 steps, determines minimum wall-clock time)
601
+ **Initial dispatch**: #{A}, #{B}, #{C}, #{E} (all predecessors resolved)
602
+ **Streaming**: #{D} dispatches as soon as #{A} completes — does NOT wait for #{B}, #{C}, or #{E}
603
+
604
+ **Note**: Investigations may create additional issues that will be automatically added to the dependency graph. The final graph will be confirmed after investigations complete.
479
605
 
480
606
  **Excluded** (already in progress / ineligible):
481
607
  - #{X} — {reason}
482
608
 
609
+ {IF EXCLUDED_CYCLE is non-empty:}
610
+ ### ⚠ Circular Dependencies Detected — Manual Fix Required
611
+
612
+ The following issues form a circular dependency chain and **cannot be dispatched** until the cycle is resolved:
613
+
614
+ | Issue | Depends On | Problem |
615
+ |-------|------------|---------|
616
+ {rows: each EXCLUDED_CYCLE issue, its predecessor list, "mutual dependency — forms cycle with #{other}"}
617
+
618
+ **Action required**: Edit each issue's body to remove or correct the `Depends on` / `Blocked by` declarations so no cycle exists. Each issue has been labeled `needs-human`. After fixing, re-run `/orchestrate` to dispatch them.
619
+ {END IF}
620
+
483
621
  Proceed? (yes / adjust / pick specific issues)
484
622
  ```
485
623
 
486
624
  **Wait for user confirmation before spawning agents.** This is the checkpoint — once agents launch, they run autonomously.
487
625
 
488
- **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.
626
+ **After confirmation**: If investigations exist, execute Phase 2B-E first. Then re-present the expanded plan (with newly spawned issues added to the dependency graph) for a quick confirmation before launching implementation.
489
627
 
490
628
  ---
491
629
 
492
- ## Phase 4: Execute Waves
630
+ ## Phase 4: Streaming DAG Execution
493
631
 
494
- ### Step 4A-pre: Capture staging baseline (MANDATORY before each wave)
632
+ ### Step 4A-pre: Staging baseline tracking (MANDATORY — continuous)
495
633
 
496
- **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.
634
+ **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. In the streaming DAG model, there are no discrete wave boundaries — instead, track a running baseline and check after each agent completion.
497
635
 
498
- **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.
636
+ **When to run**: Capture the initial baseline before the first dispatch. Then re-check after every agent that merges a PR targeting `staging`. Skip for pure milestone-branch batches where all issues target `milestone/*`.
499
637
 
500
638
  ```bash
501
- # Capture staging baseline before launching the wave
639
+ # Capture initial staging baseline before first dispatch
502
640
  git fetch origin
503
641
  if [ "$DEFAULT_BRANCH" = "$STAGING_BRANCH" ]; then
504
- # Single-branch repo — staging and default are the same; diff is always zero
505
- STAGING_LINES_BEFORE=0
506
- echo "Staging baseline before Wave {N}: skipped — single-branch repo (staging == default)"
642
+ STAGING_LINES_BASELINE=0
643
+ echo "Staging baseline: skipped — single-branch repo (staging == default)"
507
644
  else
508
- STAGING_LINES_BEFORE=$(git diff --stat origin/$DEFAULT_BRANCH...origin/$STAGING_BRANCH 2>/dev/null \
645
+ STAGING_LINES_BASELINE=$(git diff --stat origin/$DEFAULT_BRANCH...origin/$STAGING_BRANCH 2>/dev/null \
509
646
  | tail -1 \
510
647
  | grep -oP '\d+ insertion' \
511
648
  | grep -oP '\d+' \
512
649
  || echo "0")
513
- echo "Staging baseline before Wave {N}: ${STAGING_LINES_BEFORE} lines ahead of $DEFAULT_BRANCH"
650
+ echo "Staging baseline: ${STAGING_LINES_BASELINE} lines ahead of $DEFAULT_BRANCH"
514
651
  fi
652
+
653
+ # Track cumulative expected growth from merged PRs
654
+ CUMULATIVE_EXPECTED_DELTA=0
515
655
  ```
516
656
 
517
- Store `STAGING_LINES_BEFORE` per wave. After the wave completes (after Step 4C), run the integrity check:
657
+ **Per-agent-completion integrity check** (run in Step 4B after each agent merges a PR targeting staging):
518
658
 
519
659
  ```bash
520
- # Check staging integrity after wave completes (run after Step 4C)
660
+ # After agent completes and its PR merges to staging:
521
661
  git fetch origin
522
- if [ "$DEFAULT_BRANCH" = "$STAGING_BRANCH" ]; then
523
- STAGING_LINES_AFTER=0
524
- STAGING_GROWTH=0
525
- echo "Staging integrity check: skipped — single-branch repo (staging == default)"
526
- else
527
- STAGING_LINES_AFTER=$(git diff --stat origin/$DEFAULT_BRANCH...origin/$STAGING_BRANCH 2>/dev/null \
662
+ if [ "$DEFAULT_BRANCH" != "$STAGING_BRANCH" ]; then
663
+ STAGING_LINES_NOW=$(git diff --stat origin/$DEFAULT_BRANCH...origin/$STAGING_BRANCH 2>/dev/null \
528
664
  | tail -1 \
529
665
  | grep -oP '\d+ insertion' \
530
666
  | grep -oP '\d+' \
531
667
  || echo "0")
532
- STAGING_GROWTH=$((STAGING_LINES_AFTER - STAGING_LINES_BEFORE))
533
- echo "Staging after Wave {N}: ${STAGING_LINES_AFTER} lines (+${STAGING_GROWTH} vs baseline)"
534
-
535
- # Alert if growth exceeds expected delta
536
- # Expected: only the lines from PRs merged in this wave
537
- # Alert threshold: growth > 500 lines more than the sum of changed lines from wave PRs
538
- # (Use 0 as threshold if no PRs merged — any growth is unexpected)
539
- EXPECTED_DELTA={SUM_OF_PR_LINE_COUNTS_THIS_WAVE} # set from PR diffs collected in 4B
540
- UNEXPECTED_GROWTH=$((STAGING_GROWTH - EXPECTED_DELTA))
668
+ STAGING_TOTAL_GROWTH=$((STAGING_LINES_NOW - STAGING_LINES_BASELINE))
669
+
670
+ # Add this PR's line count to cumulative expected delta
671
+ CUMULATIVE_EXPECTED_DELTA=$((CUMULATIVE_EXPECTED_DELTA + {THIS_PR_LINE_COUNT}))
672
+
673
+ UNEXPECTED_GROWTH=$((STAGING_TOTAL_GROWTH - CUMULATIVE_EXPECTED_DELTA))
541
674
  if [ "$UNEXPECTED_GROWTH" -gt 500 ]; then
542
- echo "ALERT: Staging grew by ${STAGING_GROWTH} lines (+${UNEXPECTED_GROWTH} beyond expected ${EXPECTED_DELTA})."
675
+ echo "ALERT: Staging grew by ${STAGING_TOTAL_GROWTH} lines (+${UNEXPECTED_GROWTH} beyond expected ${CUMULATIVE_EXPECTED_DELTA})."
543
676
  echo "This may indicate milestone-code contamination via agent merge commits."
544
677
  echo "Review: git log --oneline --merges origin/$DEFAULT_BRANCH..origin/$STAGING_BRANCH"
545
678
  echo "Do NOT merge $STAGING_BRANCH → $DEFAULT_BRANCH until the unexpected growth is investigated."
@@ -548,29 +681,29 @@ else
548
681
  fi
549
682
  ```
550
683
 
551
- If `UNEXPECTED_GROWTH > 500`, report the alert clearly before launching the next wave. The user confirms whether to continue.
684
+ If `UNEXPECTED_GROWTH > 500`, report the alert clearly before dispatching any more agents. The user confirms whether to continue.
552
685
 
553
686
  ---
554
687
 
555
- ### Step 4A.pre.0: Pre-create milestone branches for the wave (MANDATORY before classify-lane) <!-- Added: forge#901 -->
688
+ ### Step 4A.pre.0: Pre-create milestone branches for ready issues (MANDATORY before classify-lane) <!-- Added: forge#901 -->
556
689
 
557
- **WHY THIS EXISTS**: Feature-lane milestone branches were created lazily — by whichever feature-lane agent reached its build phase first. When a wave launches N feature-lane agents in parallel, they each run the lane check at roughly the same time. Every agent that runs before the branch is first pushed observes "branch absent" and is misrouted (hard-fail / `needs-human`, or fallback to staging in older code paths). The result is a single milestone's PRs scattered across the milestone branch and staging — a branch-routing nondeterminism that recurs under parallelism.
690
+ **WHY THIS EXISTS**: Feature-lane milestone branches were created lazily — by whichever feature-lane agent reached its build phase first. When multiple agents are dispatched simultaneously, they each run the lane check at roughly the same time. Every agent that runs before the branch is first pushed observes "branch absent" and is misrouted (hard-fail / `needs-human`, or fallback to staging in older code paths). The result is a single milestone's PRs scattered across the milestone branch and staging — a branch-routing nondeterminism that recurs under parallelism.
558
691
 
559
- The fix is deterministic: create every milestone branch the wave will target **once, up front, before any agent runs `classify-lane.sh`**. After this step, every agent's lane check sees the branch and routes consistently.
692
+ The fix is deterministic: create every milestone branch the ready issues will target **once, up front, before any agent runs `classify-lane.sh`**. After this step, every agent's lane check sees the branch and routes consistently.
560
693
 
561
- **When to run**: Before the classify-lane loop in Step 4A.pre, for every wave. The step is a no-op for pure fast-lane waves (no issue in the wave has a milestone).
694
+ **When to run**: Before the classify-lane loop in Step 4A.pre, for every dispatch group (initial ready set + each subsequent batch of newly unblocked issues). The step is a no-op for pure fast-lane issues (no issue in the group has a milestone).
562
695
 
563
- **Requires bash 4+**: This snippet uses an associative array (`declare -A SEEN_MILESTONE_SLUG`) to de-dupe milestone slugs, so it must run under bash 4 or newer. Under a non-bash POSIX shell (`sh`/dash), `declare -A` fails and the de-dupe silently no-ops. This degrades gracefully — branch creation stays correct because the `git ls-remote --exit-code` exists-check below still skips any milestone branch that already exists; the only effect is redundant, idempotent `ls-remote`/`push` attempts for milestones referenced by more than one issue in the wave. Run this command's blocks under bash 4+. <!-- Added: forge#901 -->
696
+ **Requires bash 4+**: This snippet uses an associative array (`declare -A SEEN_MILESTONE_SLUG`) to de-dupe milestone slugs, so it must run under bash 4 or newer. Under a non-bash POSIX shell (`sh`/dash), `declare -A` fails and the de-dupe silently no-ops. This degrades gracefully — branch creation stays correct because the `git ls-remote --exit-code` exists-check below still skips any milestone branch that already exists; the only effect is redundant, idempotent `ls-remote`/`push` attempts for milestones referenced by more than one issue. Run this command's blocks under bash 4+. <!-- Added: forge#901 -->
564
697
 
565
698
  ```bash
566
- # Pre-create the origin milestone branch for every distinct milestone referenced by the wave.
699
+ # Pre-create the origin milestone branch for every distinct milestone referenced by ready issues.
567
700
  # Slugification MUST byte-match scripts/classify-lane.sh — otherwise a branch is created that
568
701
  # the classifier will not select. Keep these two slug pipelines identical.
569
702
  git fetch origin
570
703
 
571
- # Collect distinct milestone titles among the wave's issues
704
+ # Collect distinct milestone titles among the ready issues
572
705
  declare -A SEEN_MILESTONE_SLUG
573
- for NUM in {wave_issue_numbers}; do
706
+ for NUM in {ready_issue_numbers}; do
574
707
  MILESTONE_TITLE=$(gh issue view "$NUM" -R {GH_REPO} --json milestone --jq '.milestone.title // empty' 2>/dev/null || echo "")
575
708
  [ -z "$MILESTONE_TITLE" ] && continue # fast-lane issue — no milestone branch needed
576
709
 
@@ -615,11 +748,11 @@ for NUM in {wave_issue_numbers}; do
615
748
  done
616
749
  ```
617
750
 
618
- This step is the deterministic counterpart to fix #2 (atomic create-if-absent in the classifier): by guaranteeing the branch exists before the wave's lane checks run, no agent can observe a missing branch. `classify-lane.sh`'s hard-fail is intentionally preserved as the phantom-slug gate for any path that bypasses this step.
751
+ This step is the deterministic counterpart to fix #2 (atomic create-if-absent in the classifier): by guaranteeing the branch exists before the lane checks run, no agent can observe a missing branch. `classify-lane.sh`'s hard-fail is intentionally preserved as the phantom-slug gate for any path that bypasses this step.
619
752
 
620
- ### Step 4A.pre: Classify lane for each issue (MANDATORY before spawning agents)
753
+ ### Step 4A.pre: Classify lane for each issue (MANDATORY before dispatching agents)
621
754
 
622
- Before building agent prompts, run `classify-lane.sh` for every issue in the current wave to compute `{LANE}` and `{PR_BASE}` deterministically. The script output is authoritative — the LLM MUST NOT override or reason around it.
755
+ Before building agent prompts, run `classify-lane.sh` for every issue in the current dispatch group to compute `{LANE}` and `{PR_BASE}` deterministically. The script output is authoritative — the LLM MUST NOT override or reason around it.
623
756
 
624
757
  ```bash
625
758
  # Requires classify-lane.sh to be available at ~/.claude/scripts/classify-lane.sh
@@ -629,7 +762,15 @@ Before building agent prompts, run `classify-lane.sh` for every issue in the cur
629
762
  declare -A ISSUE_LANE
630
763
  declare -A ISSUE_PR_BASE
631
764
 
632
- for NUM in {wave_issue_numbers}; do
765
+ # Batch-level accumulators for review-finding cascade control (Step 4C) and
766
+ # Completion Sweep (Step 4F). Declared here so they persist across ALL agent
767
+ # completions — Step 4C runs per-agent and must NOT re-initialize these.
768
+ DEFERRED_FINDINGS=()
769
+ QUEUED_FINDINGS=()
770
+ declare -A DEFERRED_REASONS
771
+ declare -A AGENT_ISSUE_MAP
772
+
773
+ for NUM in {ready_issue_numbers}; do
633
774
  PR_BASE=$(bash ~/.claude/scripts/classify-lane.sh "$NUM" -R {GH_REPO}) || {
634
775
  echo "ERROR: classify-lane.sh failed for #$NUM — adding needs-human label and skipping" >&2
635
776
  gh issue edit "$NUM" -R {GH_REPO} --add-label "needs-human" 2>/dev/null || true
@@ -649,11 +790,11 @@ done
649
790
 
650
791
  Use `${ISSUE_LANE[$NUM]}` and `${ISSUE_PR_BASE[$NUM]}` to populate `{LANE}` and `{PR_BASE}` in the agent template below. Never substitute prose guesses for these values — the script output is the only valid source. <!-- Added: forge#677 -->
651
792
 
652
- ### Step 4A: Launch a wave
793
+ ### Step 4A: Dispatch ready issues
653
794
 
654
795
  **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.**
655
796
 
656
- For each issue in the current wave, spawn an Agent sub-agent that runs the full `/work-on` pipeline.
797
+ For each **ready** issue (all predecessors resolved or no predecessors), spawn an Agent sub-agent that runs the full `/work-on` pipeline. On the initial dispatch, this is every issue with an empty predecessor set. On subsequent dispatches (triggered by agent completions in Step 4B), this is every newly-unblocked issue.
657
798
 
658
799
  **One agent per issue.** Do NOT group multiple issues into a single agent. `/work-on` handles branching, labels, and PRs per-issue.
659
800
 
@@ -712,7 +853,7 @@ If the label is NOT terminal (e.g., `workflow:investigating`, `workflow:ready-to
712
853
  )
713
854
  ```
714
855
 
715
- **`{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:
856
+ **`{GIST_CONTEXT}` generation**: For each issue being dispatched, 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:
716
857
 
717
858
  ```bash
718
859
  # Build GIST_CONTEXT for an issue
@@ -739,12 +880,43 @@ fi
739
880
 
740
881
  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 -->
741
882
 
742
- **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.
883
+ **Capture agent IDs after the batch spawn (MANDATORY)**: Each `Agent(...)` call returns an agent ID. Store each returned ID in `AGENT_ISSUE_MAP` keyed by issue number. This map is the only way to resume a stalled agent by ID in Steps 4B and 4B.5:
884
+
885
+ ```
886
+ # After the single-message batch spawn, capture each returned ID:
887
+ AGENT_ISSUE_MAP[{NUMBER}] = <agent_id returned by Agent()>
888
+ ```
743
889
 
744
- ### Step 4B: Monitor wave completion
890
+ `AGENT_ISSUE_MAP` starts empty and accumulates entries as agents are spawned. For parallel dispatch (all Agent() calls in one message), capture the returned IDs from the batch response — one entry per issue — before entering Step 4B's monitoring loop. Without this capture, `resume=` calls in Steps 4B and 4B.5 will have no agent ID to reference and the resume will fail. <!-- Added: forge#1083 -->
891
+
892
+ **Launch all ready agents simultaneously** by putting multiple Agent tool calls in a single message. Use `run_in_background=true` so they execute in parallel.
893
+
894
+ ### Step 4B: Monitor completions and dispatch newly ready issues
745
895
 
746
896
  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.
747
897
 
898
+ **Core streaming dispatch loop**: After processing each agent completion, check the DAG for newly unblocked issues. If any issue now has all predecessors in a terminal state, dispatch it immediately (run Steps 4A.pre.0, 4A.pre, and 4A for the newly ready issues). This is the key difference from the wave model — issues dispatch as soon as their specific predecessors complete, not after an entire group finishes.
899
+
900
+ ```bash
901
+ # After each agent completion, check for newly ready issues:
902
+ for BLOCKED_NUM in {all_blocked_issue_numbers}; do
903
+ ALL_PREDS_DONE=true
904
+ for PRED in {predecessors_of_BLOCKED_NUM}; do
905
+ PRED_STATE=$(gh issue view $PRED -R {GH_REPO} --json labels,state --jq '{state: .state, workflow: [.labels[].name | select(startswith("workflow:"))]}')
906
+ # Terminal states: workflow:merged, workflow:invalid, needs-human, or state=CLOSED
907
+ if ! echo "$PRED_STATE" | grep -qE 'workflow:merged|workflow:invalid|needs-human|CLOSED'; then
908
+ ALL_PREDS_DONE=false
909
+ break
910
+ fi
911
+ done
912
+ if [ "$ALL_PREDS_DONE" = "true" ]; then
913
+ echo "#{BLOCKED_NUM} is now READY — all predecessors resolved. Dispatching."
914
+ # Add to dispatch batch for this completion cycle
915
+ fi
916
+ done
917
+ # Run Steps 4A.pre.0 → 4A.pre → 4A for newly ready issues (batch them in a single message)
918
+ ```
919
+
748
920
  **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:
749
921
 
750
922
  1. **Check if the agent completed the FULL pipeline** — not just one phase:
@@ -757,7 +929,7 @@ You will be automatically notified when each background agent completes. **Do NO
757
929
  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**:
758
930
  ```
759
931
  Agent(
760
- resume="{AGENT_ID}",
932
+ resume=AGENT_ISSUE_MAP[{NUMBER}],
761
933
  description="Resume #{NUMBER} pipeline",
762
934
  run_in_background=true,
763
935
  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."
@@ -765,11 +937,15 @@ You will be automatically notified when each background agent completes. **Do NO
765
937
  ```
766
938
  **Resume ALL stalled agents in a single message** (parallel resume). Do not wait between resumes.
767
939
 
768
- 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.
940
+ 3. **Track resume cycles per agent.** If an agent has been resumed 2+ times and still hasn't reached a terminal state, report it as a failure — do not resume again.
769
941
 
770
942
  4. **Record completed results**: Success (PR merged), Invalid (issue closed), Blocked (needs human), or Error
771
943
 
772
- 5. **Verify pipeline compliance** — for each truly completed issue, check that the agent used `/work-on`:
944
+ 5. **Check for newly unblocked issues** — run the DAG readiness check above. If any issues are now ready, dispatch them immediately (Steps 4A.pre.0 → 4A.pre → 4A). Batch all newly ready issues into a single dispatch message.
945
+
946
+ 6. **Handle predecessor failures** — if a completed agent's issue FAILED (needs-human, invalid, or error), check for dependent issues in the DAG. Mark all transitive dependents as "skipped — dependency #{X} failed" and report them. Do NOT dispatch them.
947
+
948
+ 7. **Verify pipeline compliance** — for each truly completed issue, check that the agent used `/work-on`:
773
949
  ```bash
774
950
  LABELS=$(gh issue view $NUM -R {GH_REPO} --json labels --jq '[.labels[].name | select(startswith("workflow:"))] | length')
775
951
  COMMENTS=$(gh api repos/{GH_REPO}/issues/${NUM}/comments --jq '[.[] | select(.body | test("FORGE:INVESTIGATOR|FORGE:BUILDER"))] | length')
@@ -779,19 +955,25 @@ You will be automatically notified when each background agent completes. **Do NO
779
955
  ```
780
956
  If an agent bypassed the pipeline, report it as a **failure** regardless of whether a PR exists.
781
957
 
782
- 6. **Post a brief status update** to the user after each agent reaches terminal state:
958
+ 8. **Post a brief status update** to the user after each agent reaches terminal state:
783
959
  ```
784
960
  ✓ #{NUMBER} — {title} → PR #{PR} merged to {target}
785
961
  ✗ #{NUMBER} — {title} → {reason for failure}
786
962
  ⚠ #{NUMBER} — {title} → PIPELINE BYPASS (no /work-on — PR invalid)
787
- ⏳ Wave 1: 2/3 complete (1 resumed after stall)...
963
+ ⏳ Progress: {completed}/{total} complete, {active} active, {blocked} blocked
964
+ → Dispatched #{NEWLY_READY} (predecessor #{PRED} completed)
788
965
  ```
789
966
 
967
+ 9. **Run staging integrity check** (from Step 4A-pre) if the completed agent merged a PR targeting staging.
968
+
969
+ **Termination condition**: All issues in the DAG are in a terminal state (merged, invalid, needs-human, or skipped due to dependency failure). When this condition is met, check whether deferred review-spawned findings exist (accumulated in `DEFERRED_FINDINGS` during Step 4C). If deferred findings exist → proceed to Step 4F (Completion Sweep). If no deferred findings → proceed to Phase 5.
970
+
790
971
  **Anti-pattern — DO NOT DO THIS:**
791
972
  - `sleep 60/120/180/300` loops to check status — you will be notified automatically
792
973
  - Spawning separate "progress check" agents — they waste tokens and add noise
793
974
  - Reading agent JSONL output files to check progress — use GitHub labels as the source of truth
794
975
  - Polling the same status check repeatedly on a timer
976
+ - Waiting for a "batch" of completions before checking for newly ready issues — check after EVERY completion
795
977
 
796
978
  ### Step 4B.5: Time-Based Stall Detection
797
979
 
@@ -799,7 +981,7 @@ You will be automatically notified when each background agent completes. **Do NO
799
981
 
800
982
  **When to run** (NOT a sleep loop — two trigger points only):
801
983
  1. On every background agent completion event (run BEFORE the terminal-state check in Step 4B)
802
- 2. Before posting any "waiting for wave..." status update to the user
984
+ 2. Before posting any "waiting for agents..." status update to the user
803
985
 
804
986
  **Do NOT poll on a timer. Do NOT use sleep. Run at these two trigger points only.**
805
987
 
@@ -808,9 +990,9 @@ You will be automatically notified when each background agent completes. **Do NO
808
990
  STALL_TIMEOUT=$(yq '.pipeline.stall_timeout_minutes // 15' forge.yaml 2>/dev/null || echo 15)
809
991
  ```
810
992
 
811
- **For each non-terminal agent in the current wave**:
993
+ **For each non-terminal agent in the current batch**:
812
994
  ```bash
813
- for NUM in {active_wave_issue_numbers}; do
995
+ for NUM in {active_issue_numbers}; do
814
996
  # Skip issues already in terminal state
815
997
  TERMINAL=$(gh issue view $NUM -R {GH_REPO} --json labels \
816
998
  --jq '[.labels[].name | select(. == "workflow:merged" or . == "workflow:invalid" or . == "needs-human")] | length')
@@ -870,26 +1052,26 @@ Issue #${NUM} has been auto-resumed ${STALL_COUNT} times without reaching a term
870
1052
  done
871
1053
 
872
1054
  # Launch all stall resumes in parallel (single message — same rule as Step 4B)
873
- # For each NUM in $STALL_RESUME_LIST, call Agent(resume="{AGENT_ID}", run_in_background=true, ...)
1055
+ # For each NUM in $STALL_RESUME_LIST, call Agent(resume=AGENT_ISSUE_MAP[NUM], run_in_background=true, ...)
874
1056
  ```
875
1057
 
876
1058
  **Resume all stalled agents in a single message** (parallel). Use the same `Agent(resume=...)` pattern as Step 4B — do not wait between individual resumes.
877
1059
 
878
1060
  **Track stall resume cycles separately** from completion-event resumes (Step 4B). If the same issue accumulates ≥ 2 `FORGE:STALL_DETECTED` comments AND still hasn't reached terminal state, do not resume again — the `needs-human` label is already set.
879
1061
 
880
- ### Step 4C: Collect review-finding issues from completed wave
1062
+ ### Step 4C: Collect review-finding issues from completed agents
881
1063
 
882
- 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.
1064
+ After each agent reaches a terminal state, check if its `/work-on` run spawned review-finding issues during the review phase. These are new work items that should be added to the dependency DAG and dispatched when ready.
883
1065
 
884
1066
  ```bash
885
1067
  # Method 1: Read TRAJECTORY comments from completed issues for "Finding issues" row
886
- for NUM in {completed_issue_numbers_this_wave}; do
1068
+ for NUM in {completed_issue_number}; do
887
1069
  gh api repos/{GH_REPO}/issues/${NUM}/comments \
888
1070
  --jq '.[] | select(.body | contains("FORGE:TRAJECTORY")) | .body' 2>/dev/null \
889
1071
  | grep -oP 'Finding issues\s*\|\s*#?\K\d+[^|]*' | grep -oP '\d+' | sort -u
890
1072
  done
891
1073
 
892
- # Method 2 (fallback): Check for recently created review-finding issues that reference PRs from this wave
1074
+ # Method 2 (fallback): Check for recently created review-finding issues that reference PRs from this batch
893
1075
  gh issue list -R {GH_REPO} --state open --label "review-finding" --limit 20 \
894
1076
  --json number,title,body,createdAt \
895
1077
  --jq "[.[] | select(.createdAt > \"$(date -u -d '2 hours ago' +%Y-%m-%dT%H:%M:%SZ)\")]"
@@ -897,7 +1079,7 @@ gh issue list -R {GH_REPO} --state open --label "review-finding" --limit 20 \
897
1079
 
898
1080
  **If review-finding issues were spawned:**
899
1081
 
900
- **Cascade control (MANDATORY — run before folding findings into waves):**
1082
+ **Cascade control (MANDATORY — run before folding findings into the DAG):**
901
1083
 
902
1084
  For each spawned finding, determine whether it should be **executed** or **deferred**:
903
1085
 
@@ -905,17 +1087,17 @@ For each spawned finding, determine whether it should be **executed** or **defer
905
1087
  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.
906
1088
  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.
907
1089
  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.
908
- 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.
1090
+ 4. **P3 + same-file overlap**: Finding is labeled `P3` AND the file it targets overlaps with ANY file already in the current batch (active or queued in the DAG). Rationale: same-file P3 findings add predecessor edges that serialize agents — one finding per original issue increases wall-clock time with no proportional value.
909
1091
 
910
- **Defer** (do NOT add to wave queue) if rules 1, 3, or 4 match.
1092
+ **Defer** (do NOT add to the DAG) if rules 1, 3, or 4 match.
911
1093
 
912
- **Execute** (add to wave queue) if:
1094
+ **Execute** (add to the DAG) if:
913
1095
  - Rule 2 matches (P1 or P2)
914
1096
  - None of the defer rules matched (generation 1, P3 with no file overlap, not a keyword match)
915
1097
 
916
1098
  **Before running the loop, build the batch file list (MANDATORY for Heuristic 3):**
917
1099
 
918
- 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.
1100
+ Collect all file paths from every issue in the current batch — both completed and remaining queued issues in the DAG. This produces `ALL_BATCH_FILES`, a newline-separated list of file paths used by Heuristic 3 to test same-file overlap.
919
1101
 
920
1102
  ```bash
921
1103
  # Build ALL_BATCH_FILES: collect file paths from ALL batch issues (completed + queued)
@@ -938,6 +1120,8 @@ ALL_BATCH_FILES=$(echo "$ALL_BATCH_FILES" | sort -u | grep -v '^$')
938
1120
 
939
1121
  ```bash
940
1122
  # For each finding, check its priority label and generation
1123
+ # NOTE: DEFERRED_FINDINGS, QUEUED_FINDINGS, and DEFERRED_REASONS are declared at
1124
+ # batch scope in Step 4A.pre — do NOT re-initialize them here (Step 4C runs per-agent).
941
1125
  for FINDING_NUM in {spawned_finding_numbers}; do
942
1126
  FINDING_DATA=$(gh issue view $FINDING_NUM -R {GH_REPO} --json labels,title,body \
943
1127
  --jq '{labels: [.labels[].name], title: .title, body: .body}')
@@ -971,6 +1155,7 @@ for FINDING_NUM in {spawned_finding_numbers}; do
971
1155
 
972
1156
  if [ "$DEFER" = "true" ]; then
973
1157
  DEFERRED_FINDINGS+=($FINDING_NUM)
1158
+ DEFERRED_REASONS[$FINDING_NUM]="$DEFER_REASON"
974
1159
  echo "Deferred #${FINDING_NUM}: $DEFER_REASON"
975
1160
  else
976
1161
  QUEUED_FINDINGS+=($FINDING_NUM)
@@ -980,28 +1165,27 @@ done
980
1165
 
981
1166
  **For queued (non-deferred) findings:**
982
1167
 
983
- 1. **Add them to the remaining wave plan.** They are implementation issues — same as issues spawned by investigations in Phase 2.
1168
+ 1. **Add them to the dependency DAG.** They are implementation issues — same as issues spawned by investigations in Phase 2. Compute their predecessor sets using the same conflict detection (Step 3C Layers 1-4) against all remaining blocked/active issues.
984
1169
  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.
985
1170
  3. **Report to user:**
986
1171
  ```
987
- Wave {N} review spawned {count} new finding issues: #{A}, #{B}
988
- Queued for execution: #{A}, #{B}
1172
+ Agent #{COMPLETED} spawned {count} new finding issues: #{A}, #{B}
1173
+ Added to DAG: #{A} (predecessors: {}), #{B} (predecessors: {#{X}})
989
1174
  Deferred (cascade control): #{C} (P3 same-file), #{D} (comment heuristic)
990
- Adding queued findings to Wave {N+1} (or new wave if no remaining waves).
991
1175
  ```
992
- 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.
1176
+ 4. **Re-run file-overlap detection** (Step 3C) on the expanded issue set — finding issues may conflict with active or queued issues that touch the same files. Ready findings dispatch immediately via the standard Step 4B dispatch loop.
993
1177
 
994
1178
  **For deferred findings:**
995
1179
 
996
- 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.
1180
+ Track them in `DEFERRED_FINDINGS` for re-evaluation in Step 4F (Completion Sweep) after the DAG drains. Do NOT close or label them yet — the sweep will determine their final disposition.
997
1181
 
998
- **If no review-finding issues were spawned:** Continue to next wave normally.
1182
+ **If no review-finding issues were spawned:** Continue monitoring for the next agent completion.
999
1183
 
1000
- ### Step 4C.5: Milestone lane-consistency check (run after each wave) <!-- Added: forge#901 -->
1184
+ ### Step 4C.5: Milestone lane-consistency check (periodic) <!-- Added: forge#901 -->
1001
1185
 
1002
1186
  **WHY THIS EXISTS**: A milestone's feature-lane PRs must all target the same milestone branch. If a branch-routing race ever scatters them — some on the milestone branch, some on staging — the milestone branch becomes incomplete relative to staging, and the split is otherwise invisible until the milestone tries to ship. Step 4A.pre.0 prevents the split deterministically; this check detects any residual split so it surfaces immediately instead of at ship time.
1003
1187
 
1004
- **When to run**: After each wave completes, for any batch where at least one issue has a milestone. Skip for pure fast-lane batches. This check is **non-blocking** — it alerts; it does not auto-resolve or stop the pipeline.
1188
+ **When to run**: After every 3rd agent completion (or after all agents complete, whichever comes first), for any batch where at least one issue has a milestone. Skip for pure fast-lane batches. This check is **non-blocking** — it alerts; it does not auto-resolve or stop the pipeline.
1005
1189
 
1006
1190
  ```bash
1007
1191
  # For each distinct milestone in the batch, assert all of its feature-lane PRs share one base.
@@ -1051,13 +1235,13 @@ for NUM in {all_batch_issue_numbers}; do
1051
1235
  done
1052
1236
  ```
1053
1237
 
1054
- Report any `ALERT` lines prominently before advancing to the next wave. Reconciliation of an existing split is a manual/`/milestone`-assisted step — this check only ensures the split is never silent.
1238
+ Report any `ALERT` lines prominently before dispatching more agents. Reconciliation of an existing split is a manual/`/milestone`-assisted step — this check only ensures the split is never silent.
1055
1239
 
1056
- ### Step 4D: Milestone integration build gate (MANDATORY between waves)
1240
+ ### Step 4D: Milestone integration build gate (MANDATORY — periodic for milestone batches)
1057
1241
 
1058
- **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.
1242
+ **WHY THIS EXISTS**: Session Intelligence milestone shipped 116 PRs across multiple dispatches 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.
1059
1243
 
1060
- **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.
1244
+ **When to run**: After every 3rd milestone-targeted agent completion (or when all milestone issues are complete), IF the batch targets a milestone branch AND any `.tsx`/`.ts` files were changed by agents in the completed set. Running after every single agent would be too frequent — batch the check to reduce overhead while still catching integration errors before they accumulate.
1061
1245
 
1062
1246
  All tool commands are read from `forge.yaml → verification.commands`; each step logs `SKIPPED — not configured` when the corresponding key is absent rather than silently passing.
1063
1247
 
@@ -1072,7 +1256,7 @@ MILESTONE_BRANCH="milestone/{milestone_slug}"
1072
1256
  TS_CHANGED=$(git diff origin/{DEFAULT_BRANCH}...origin/${MILESTONE_BRANCH} --name-only | grep -E '\.(tsx?|jsx?)$' | head -1)
1073
1257
 
1074
1258
  if [ -n "$TS_CHANGED" ]; then
1075
- echo "=== Integration Build Gate (TypeScript): Wave {N} ==="
1259
+ echo "=== Integration Build Gate (TypeScript): batch checkpoint ==="
1076
1260
  cd {REPO_PATH}
1077
1261
  git fetch origin ${MILESTONE_BRANCH}
1078
1262
  git checkout origin/${MILESTONE_BRANCH} --detach 2>/dev/null
@@ -1096,18 +1280,18 @@ if [ -n "$TS_CHANGED" ]; then
1096
1280
  git checkout - 2>/dev/null
1097
1281
 
1098
1282
  if [ "$TSC_EXIT" -ne 0 ]; then
1099
- echo "BLOCKING: TypeScript errors on ${MILESTONE_BRANCH} after Wave {N}."
1100
- echo "Fix type errors before starting next wave."
1283
+ echo "BLOCKING: TypeScript errors on ${MILESTONE_BRANCH} after batch checkpoint."
1284
+ echo "Fix type errors before dispatching more milestone agents."
1101
1285
  elif [ "${BUILD_EXIT:-0}" -ne 0 ]; then
1102
- echo "BLOCKING: build failed on ${MILESTONE_BRANCH} after Wave {N}."
1103
- echo "Build/prerender errors — fix before starting next wave."
1286
+ echo "BLOCKING: build failed on ${MILESTONE_BRANCH} after batch checkpoint."
1287
+ echo "Build/prerender errors — fix before dispatching more milestone agents."
1104
1288
  fi
1105
1289
  fi
1106
1290
 
1107
1291
  # Python format check
1108
1292
  PY_CHANGED=$(git diff origin/{DEFAULT_BRANCH}...origin/${MILESTONE_BRANCH} --name-only | grep -E '\.py$' | head -1)
1109
1293
  if [ -n "$PY_CHANGED" ]; then
1110
- echo "=== Integration Build Gate (Python): Wave {N} ==="
1294
+ echo "=== Integration Build Gate (Python): batch checkpoint ==="
1111
1295
  cd {REPO_PATH}
1112
1296
  git checkout origin/${MILESTONE_BRANCH} --detach 2>/dev/null
1113
1297
 
@@ -1122,32 +1306,234 @@ if [ -n "$PY_CHANGED" ]; then
1122
1306
  git checkout - 2>/dev/null
1123
1307
 
1124
1308
  if [ "$FORMAT_EXIT" -ne 0 ]; then
1125
- echo "WARNING: Python formatting issues on ${MILESTONE_BRANCH} after Wave {N}."
1309
+ echo "WARNING: Python formatting issues on ${MILESTONE_BRANCH} after batch checkpoint."
1126
1310
  echo "Not blocking but should be fixed before milestone→staging."
1127
1311
  fi
1128
1312
  fi
1129
1313
  ```
1130
1314
 
1131
- **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. Build failures are BLOCKING — SSG/prerender crashes are invisible to typecheck alone — configure `typescript.build` in `verification.commands` to catch them.
1132
-
1133
- ### Step 4E: Advance to next wave
1315
+ **If the gate fails**: Report the errors to the user. Do NOT dispatch any more milestone-targeted agents until the integration errors are resolved. The accumulated milestone branch has integration errors that will only get worse with more PRs on top. Build failures are BLOCKING — SSG/prerender crashes are invisible to typecheck alone — configure `typescript.build` in `verification.commands` to catch them. Non-milestone (fast-lane) agents may continue dispatching normally.
1134
1316
 
1135
- When wave results are processed, the integration gate passes (or is not applicable), and any new finding issues are folded in:
1136
-
1137
- 1. Check if any failures in the current wave block the next wave (dependency failures)
1138
- 2. If a dependency failed, skip dependent issues in the next wave and report them as "skipped — dependency #{X} failed"
1139
- 3. Launch the next wave (same process as Step 4A)
1140
-
1141
- ### Step 4F: Handle individual agent failures
1317
+ ### Step 4E: Handle individual agent failures
1142
1318
 
1143
1319
  If an agent reports failure or error:
1144
1320
  - **Merge conflict**: Report to user, mark issue as needing human attention
1145
1321
  - **Invalid issue**: Already handled by the agent (closed with comment) — just report it
1146
1322
  - **Build/test failure**: Report the error, suggest manual intervention
1147
1323
  - **Agent timeout**: Report which issue timed out, suggest re-running with `/work-on #{N}`
1324
+ - **Dependency cascade**: Mark all transitive dependents in the DAG as "skipped — dependency #{X} failed"
1148
1325
 
1149
1326
  **Do NOT retry failed agents automatically.** Report the failure and let the user decide.
1150
1327
 
1328
+ ### Step 4F: Completion Sweep (deferred review-spawned findings) <!-- Added: forge#1105 -->
1329
+
1330
+ **When to run**: After all DAG issues reach terminal state AND `DEFERRED_FINDINGS` is non-empty. Skip if no findings were deferred during this batch.
1331
+
1332
+ **WHY THIS EXISTS**: Deferred findings accumulate during the batch because of file-overlap and cascade-control heuristics (Step 4C). But once the DAG drains, the conditions that caused deferral often no longer apply — completed issues no longer occupy files, so same-file overlap vanishes. Without this sweep, deferred findings silently pile up across runs and never get resolved.
1333
+
1334
+ **Step 4F.1: Classify deferred findings into permanent vs re-evaluable**
1335
+
1336
+ ```bash
1337
+ PERMANENT_DEFERRED=()
1338
+ SWEEP_CANDIDATES=()
1339
+
1340
+ for FINDING_NUM in "${DEFERRED_FINDINGS[@]}"; do
1341
+ DEFER_REASON="${DEFERRED_REASONS[$FINDING_NUM]}"
1342
+
1343
+ # Generation >= 2 deferrals are PERMANENT — unbounded cascade prevention
1344
+ if echo "$DEFER_REASON" | grep -qi "generation"; then
1345
+ PERMANENT_DEFERRED+=($FINDING_NUM)
1346
+ else
1347
+ # All other deferrals (comment/typo, P3 same-file) are re-evaluable
1348
+ SWEEP_CANDIDATES+=($FINDING_NUM)
1349
+ fi
1350
+ done
1351
+
1352
+ echo "Completion sweep: ${#SWEEP_CANDIDATES[@]} re-evaluable, ${#PERMANENT_DEFERRED[@]} permanent"
1353
+ ```
1354
+
1355
+ **Step 4F.2: Re-evaluate sweep candidates**
1356
+
1357
+ Re-run the Step 4C heuristics against the now-empty DAG. Since all original batch issues are in terminal state, the `ALL_BATCH_FILES` list for file-overlap detection is empty — P3 same-file deferrals will now pass.
1358
+
1359
+ ```bash
1360
+ SWEEP_EXECUTE=()
1361
+ SWEEP_STILL_DEFERRED=()
1362
+
1363
+ for FINDING_NUM in "${SWEEP_CANDIDATES[@]}"; do
1364
+ FINDING_DATA=$(gh issue view $FINDING_NUM -R {GH_REPO} --json labels,title,body,state \
1365
+ --jq '{labels: [.labels[].name], title: .title, body: .body, state: .state}')
1366
+
1367
+ # Skip if already closed (resolved by another process)
1368
+ STATE=$(echo "$FINDING_DATA" | jq -r '.state')
1369
+ [ "$STATE" = "CLOSED" ] && continue
1370
+
1371
+ PRIORITY=$(echo "$FINDING_DATA" | jq -r '.labels[] | select(startswith("priority:P")) | ltrimstr("priority:")' | head -1)
1372
+ TITLE=$(echo "$FINDING_DATA" | jq -r '.title')
1373
+
1374
+ # Re-apply heuristics against the drained DAG (no active batch files)
1375
+ # Comment/typo heuristic still applies — these are cosmetic regardless of DAG state
1376
+ if echo "$TITLE" | grep -qi "comment\|typo"; then
1377
+ SWEEP_STILL_DEFERRED+=($FINDING_NUM)
1378
+ echo "Sweep: #${FINDING_NUM} still deferred (comment/typo — cosmetic)"
1379
+ else
1380
+ # P3 same-file overlap no longer applies (DAG is drained, no active files)
1381
+ # All other findings are safe to execute
1382
+ SWEEP_EXECUTE+=($FINDING_NUM)
1383
+ echo "Sweep: #${FINDING_NUM} cleared for execution (file overlap resolved)"
1384
+ fi
1385
+ done
1386
+ ```
1387
+
1388
+ **Step 4F.3: Dispatch cleared findings**
1389
+
1390
+ For each finding in `SWEEP_EXECUTE`, add it to a fresh sweep DAG and dispatch using the same Steps 4A.pre.0, 4A.pre, and 4A logic. Run file-overlap detection between the swept findings themselves (they may conflict with each other).
1391
+
1392
+ **MANDATORY**: Use the full Step 4A agent template verbatim for each swept finding. Do NOT use a bare `prompt="Run /work-on N"` — that bypasses the label-state loop contract, source branch detection, and all pipeline enforcement rules. Swept findings are always `review-finding` issues that require source branch detection to route correctly.
1393
+
1394
+ **Step 4F.3.pre: Classify lane for each sweep finding (MANDATORY before dispatching)**
1395
+
1396
+ Run `classify-lane.sh` per finding — same pattern as Step 4A.pre:
1397
+
1398
+ ```bash
1399
+ declare -A SWEEP_LANE
1400
+ declare -A SWEEP_PR_BASE
1401
+
1402
+ for FINDING_NUM in "${SWEEP_EXECUTE[@]}"; do
1403
+ SWEEP_BASE=$(bash ~/.claude/scripts/classify-lane.sh "$FINDING_NUM" -R {GH_REPO}) || {
1404
+ echo "ERROR: classify-lane.sh failed for #$FINDING_NUM — adding needs-human and skipping" >&2
1405
+ gh issue edit "$FINDING_NUM" -R {GH_REPO} --add-label "needs-human" 2>/dev/null || true
1406
+ continue
1407
+ }
1408
+ if [ "$SWEEP_BASE" = "staging" ]; then
1409
+ SWEEP_LANE[$FINDING_NUM]="fast-lane"
1410
+ else
1411
+ SWEEP_LANE[$FINDING_NUM]="feature-lane"
1412
+ fi
1413
+ SWEEP_PR_BASE[$FINDING_NUM]="$SWEEP_BASE"
1414
+ echo "#$FINDING_NUM → lane=${SWEEP_LANE[$FINDING_NUM]}, PR_BASE=$SWEEP_BASE"
1415
+ done
1416
+ ```
1417
+
1418
+ **Step 4F.3.dispatch: Dispatch with full Step 4A template**
1419
+
1420
+ ```bash
1421
+ if [ ${#SWEEP_EXECUTE[@]} -gt 0 ]; then
1422
+ echo "Completion sweep: dispatching ${#SWEEP_EXECUTE[@]} cleared findings"
1423
+
1424
+ # Build sweep DAG — same conflict detection as Step 3C Layers 1-4
1425
+ # but only among the swept findings (no original batch issues remain active)
1426
+ # Dispatch ready findings, monitor completions using the same Step 4B loop
1427
+ # This is a SINGLE pass — findings spawned during the sweep are NOT swept again
1428
+ # (they follow the standard Step 4C triage: queued or deferred for next run)
1429
+
1430
+ # NOTE: Step 4A.pre.0 (milestone branch pre-creation) is intentionally skipped here.
1431
+ # Swept findings are always review-finding issues that target an already-existing branch
1432
+ # (staging or a milestone branch). The branch was created when the original batch ran —
1433
+ # it always exists by the time the sweep runs.
1434
+
1435
+ for FINDING_NUM in "${SWEEP_EXECUTE[@]}"; do
1436
+ # Skip any finding whose classify-lane call failed (needs-human already set above)
1437
+ [ -z "${SWEEP_PR_BASE[$FINDING_NUM]:-}" ] && continue
1438
+
1439
+ FINDING_TITLE=$(gh issue view "$FINDING_NUM" -R {GH_REPO} --json title --jq '.title' 2>/dev/null || echo "")
1440
+
1441
+ # Build GIST_CONTEXT for sweep finding — same as Step 4A generation block
1442
+ GIST_CONTEXT=""
1443
+ PARENT_INV=$(gh issue view "$FINDING_NUM" -R {GH_REPO} --json body --jq '.body' \
1444
+ | grep -oP '(?i)parent[: ]*#\K\d+|spawned from[: ]*#\K\d+' | head -1)
1445
+
1446
+ if [ -n "$PARENT_INV" ] && [ -n "${INVESTIGATION_GISTS[$PARENT_INV]:-}" ]; then
1447
+ GIST_CONTEXT="
1448
+ **CONTEXT FROM PRIOR INVESTIGATION**: Investigation #${PARENT_INV} produced Knowledge Gist(s) with findings relevant to this issue:
1449
+ $(echo "${INVESTIGATION_GISTS[$PARENT_INV]}" | while IFS= read -r url; do echo "- ${url}"; done)
1450
+ Fetch the Gist content during the context-gathering phase for implementation guidance."
1451
+ fi
1452
+
1453
+ if [ -n "$MILESTONE_INDEX_URL" ]; then
1454
+ GIST_CONTEXT="${GIST_CONTEXT}
1455
+
1456
+ **MILESTONE KNOWLEDGE INDEX**: All investigation findings for this milestone are aggregated in a single index Gist:
1457
+ - ${MILESTONE_INDEX_URL}
1458
+ The context-gathering phase can fetch this index to discover all investigation Gists for the milestone."
1459
+ fi
1460
+
1461
+ # Use the full Step 4A template verbatim — copied here so sweep agents receive
1462
+ # the complete pipeline contract. Keep in sync with Step 4A when the template changes.
1463
+ Agent(
1464
+ subagent_type="general-purpose",
1465
+ model="sonnet",
1466
+ description="Work on {PROJECT_PREFIX}#${FINDING_NUM}",
1467
+ run_in_background=true,
1468
+ prompt="You are working on GitHub issue #${FINDING_NUM} for the {PROJECT_NAME} project.
1469
+
1470
+ **Project**: {PROJECT_NAME}
1471
+ **Repository**: {GH_REPO}
1472
+ **Repo path**: {REPO_PATH}
1473
+
1474
+ **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.
1475
+
1476
+ **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.
1477
+
1478
+ **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.
1479
+
1480
+ **IMPORTANT RULES**:
1481
+ - **MANDATORY**: You MUST use the Skill tool to invoke 'work-on' with args '${FINDING_NUM}'. 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.
1482
+ - For default repo issues: \`Skill(skill='work-on', args='${FINDING_NUM}')\`
1483
+ - For satellite repo issues: \`Skill(skill='work-on', args='{SATELLITE_PREFIX}:${FINDING_NUM}')\` (prefix from forge.yaml → repos.satellites)
1484
+ - NEVER bypass /work-on with manual git/gh commands — the label updates and structured comments are critical for tracking
1485
+ - NEVER target \`main\` for PRs targeting the default repo. Use \`{STAGING_BRANCH}\` for fast-lane issues, or \`milestone/{slug}\` for milestone issues.
1486
+ - Satellite repos (MCP, n8n) have no staging branch — fast-lane PRs go to \`main\` for those.
1487
+ - If the issue is INVALID after investigation, close it with a comment explaining why
1488
+ - If you hit merge conflicts or blockers, post a comment on the issue and STOP — do not force anything
1489
+ - Do not interact with the user — you are running autonomously in the background
1490
+ - **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.
1491
+
1492
+ **LABEL-STATE LOOP CONTRACT — enforce after EVERY Skill return**:
1493
+ After EVERY \`Skill(skill='work-on', ...)\` call returns, immediately check the issue's current workflow label:
1494
+ \`\`\`bash
1495
+ gh issue view ${FINDING_NUM} -R {GH_REPO} --json labels --jq '[.labels[].name | select(startswith(\"workflow:\"))]'
1496
+ \`\`\`
1497
+ **Terminal labels** (only these allow you to stop): \`workflow:merged\`, \`workflow:invalid\`
1498
+ **Terminal condition also**: \`needs-human\` label present OR issue state is \`closed\`
1499
+ 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='${FINDING_NUM}')\` 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.
1500
+
1501
+ **CRITICAL — SOURCE BRANCH DETECTION**:
1502
+ - If the issue has the \`review-finding\` label, read the issue body for \`**Code branch**: \\\`{branch}\\\`\`
1503
+ - If found, that is the SOURCE_BRANCH — the code ONLY exists on that branch (e.g., \`staging\`), NOT on \`origin/main\`
1504
+ - Investigation MUST use \`git show origin/{SOURCE_BRANCH}:{filepath}\` to verify the code exists
1505
+ - Worktree MUST branch from \`origin/{SOURCE_BRANCH}\`, NOT \`origin/main\`
1506
+ - PR target is \`{SOURCE_BRANCH}\` (the fix goes back to where the code lives)
1507
+
1508
+ **LANE**: ${SWEEP_LANE[$FINDING_NUM]} (PR target: ${SWEEP_PR_BASE[$FINDING_NUM]})
1509
+ **Issue title**: ${FINDING_TITLE}
1510
+ ${GIST_CONTEXT}
1511
+ "
1512
+ )
1513
+ done
1514
+
1515
+ # Monitor sweep agents using the same Step 4B completion loop
1516
+ # IMPORTANT: Findings spawned by sweep agents are NOT re-swept —
1517
+ # they follow standard Step 4C triage to prevent recursive cascades
1518
+ fi
1519
+ ```
1520
+
1521
+ **Step 4F.4: Report sweep results**
1522
+
1523
+ ```
1524
+ Completion Sweep Results:
1525
+ Dispatched: #{A}, #{B} (file overlap cleared after DAG drain)
1526
+ Still deferred (cosmetic): #{C} (comment/typo)
1527
+ Permanently deferred (gen2): #{D} (generation >= 2 cascade cap)
1528
+ ```
1529
+
1530
+ **After sweep agents complete** (or if no findings were dispatched): proceed to Phase 5.
1531
+
1532
+ **Anti-patterns — DO NOT DO THIS:**
1533
+ - Re-sweeping findings spawned during the sweep itself — this creates unbounded recursion. Sweep is a single pass.
1534
+ - Overriding generation >= 2 deferrals — the cascade cap is absolute.
1535
+ - Skipping the sweep because "there are only a few" deferred findings — even one deferred finding represents unresolved work.
1536
+
1151
1537
  ---
1152
1538
 
1153
1539
  ## Phase 5: Post-Batch Cleanup
@@ -1191,7 +1577,7 @@ Include the cleanup summary in the final report (Phase 6). If cleanup found prob
1191
1577
 
1192
1578
  ## Phase 6: Consolidated Report
1193
1579
 
1194
- After ALL waves AND cleanup complete, present a final summary.
1580
+ After ALL issues reach terminal state AND cleanup completes, present a final summary.
1195
1581
 
1196
1582
  ### Step 6A: Collect trajectory data
1197
1583
 
@@ -1212,7 +1598,7 @@ Aggregate into the batch-level analytics for Step 6B.
1212
1598
  **Scope**: {milestone / issue list}
1213
1599
  **Duration**: {approximate time}
1214
1600
 
1215
- ### Wave 0: Investigations
1601
+ ### Investigations (Phase 2)
1216
1602
  {IF investigations ran:}
1217
1603
  | # | Investigation | Issues Created | Result |
1218
1604
  |---|-------------|---------------|--------|
@@ -1232,17 +1618,31 @@ Aggregate into the batch-level analytics for Step 6B.
1232
1618
 
1233
1619
  ### Review-Spawned Issues
1234
1620
 
1235
- {IF review findings were created during any wave:}
1621
+ {IF review findings were created during any agent run:}
1236
1622
  | # | Finding | Source Issue | Source PR | Status |
1237
1623
  |---|---------|-------------|-----------|--------|
1238
- | #{F1} | {title} | #{A} | PR #{PR} | ✓ Merged (wave {N}) / ⏳ Open (deferred) |
1624
+ | #{F1} | {title} | #{A} | PR #{PR} | ✓ Merged (in-batch) / ✓ Swept (completion sweep) / ⏳ Deferred (cosmetic) / ⛔ Deferred (gen2 cascade cap) |
1239
1625
 
1240
1626
  {ELSE: "No review findings created during this batch."}
1241
1627
 
1628
+ ### Completion Sweep <!-- Added: forge#1105 -->
1629
+ - **Re-evaluated**: {N} deferred findings re-checked after DAG drain
1630
+ - **Dispatched**: {N} findings cleared and executed (file overlap resolved)
1631
+ - **Still deferred (cosmetic)**: {N} comment/typo findings (low-value, safe to leave)
1632
+ - **Permanently deferred (gen2)**: {N} generation >= 2 findings (cascade cap — requires manual `/work-on`)
1633
+
1634
+ {IF permanently deferred > 0:}
1635
+ **Action required**: The following findings were permanently deferred due to the generation >= 2 cascade cap. They will NOT be picked up automatically — run `/work-on #{N}` manually or include them in the next `/orchestrate` batch:
1636
+ {list of permanently deferred issue numbers and titles}
1637
+
1638
+ {IF cosmetic deferred > 0:}
1639
+ **Low-priority leftovers**: The following cosmetic findings remain open. They will be picked up by future runs or can be batched with `/orchestrate #{N1} #{N2}`:
1640
+ {list of cosmetic deferred issue numbers and titles}
1641
+
1242
1642
  ### Summary
1243
1643
  - **Investigations**: {N} completed, spawned {M} new issues
1244
- - **Review findings**: {N} spawned, {M} resolved in-batch, {K} deferred
1245
- - **Succeeded**: {N} issues resolved (implementation)
1644
+ - **Review findings**: {N} spawned, {M} resolved in-batch, {K} swept, {J} deferred (cosmetic), {L} deferred (gen2)
1645
+ - **Succeeded**: {N} issues resolved (implementation + sweep)
1246
1646
  - **Failed**: {N} issues need attention
1247
1647
  - **Skipped**: {N} issues (dependency failures)
1248
1648
 
@@ -1259,7 +1659,7 @@ Aggregate into the batch-level analytics for Step 6B.
1259
1659
  |-------|-------|-------|--------|-------|---------|--------------|
1260
1660
  {per-agent rows from audit}
1261
1661
 
1262
- **Wave efficiency**: {avg_idle_pct}% idle time
1662
+ **Batch efficiency**: {avg_idle_pct}% idle time
1263
1663
  **Total resume cycles**: {sum_resumes} across {agent_count} agents
1264
1664
  **Clean agents** (no stalls): {clean_count}/{agent_count}
1265
1665
 
@@ -1283,7 +1683,7 @@ Aggregate into the batch-level analytics for Step 6B.
1283
1683
  **Systematic issues** (flag if detected):
1284
1684
  - False positive rate > 30% → review agents may need tuning
1285
1685
  - Invalidation rate < 10% or > 35% → investigation calibration off
1286
- - Same-domain merge conflicts in 2+ waves → domain sequencing too aggressive
1686
+ - Same-domain merge conflicts between concurrent agents → domain serialization edges too loose
1287
1687
  - Contract divergences > 20% → investigation quality may need improvement
1288
1688
  - **Idle% > 50%** → agents stalling at phase boundaries; check work-on routing loop
1289
1689
  - **Avg resumes > 1** → orchestrator having to compensate for agent stops
@@ -1304,10 +1704,10 @@ Aggregate into the batch-level analytics for Step 6B.
1304
1704
  4. **Always confirm with user before launching** — Step 3E is the mandatory checkpoint
1305
1705
  5. **No retries** — if an agent fails, report it and move on
1306
1706
  6. **Respect existing work** — skip issues already being worked on (`workflow:building`, `workflow:in-review`)
1307
- 7. **Dependency failures cascade** — if A fails and B depends on A, B is skipped (not attempted)
1707
+ 7. **Dependency failures cascade** — if A fails and B depends on A, all transitive dependents of A are skipped (not attempted)
1308
1708
  8. **Always run post-batch cleanup** — Phase 5 is mandatory. Never skip `/cleanup all` after orchestration.
1309
1709
  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.
1310
- 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.
1710
+ 10. **Per-completion verification** — after each agent completes, check that it has `workflow:*` labels and structured comments. If an agent bypassed `/work-on`, report it as a pipeline failure.
1311
1711
 
1312
1712
  ---
1313
1713
 
@@ -1315,31 +1715,38 @@ Aggregate into the batch-level analytics for Step 6B.
1315
1715
 
1316
1716
  ### "orchestrate milestone api-expansion"
1317
1717
  → Fetches all open issues in "API Expansion v1" milestone
1318
- → Analyzes deps (#1526 → #1527 → #1528 → #1529, #1530 independent)
1319
- → Plans 3 waves, confirms with user, executes
1718
+ → Builds DAG: #1526 → #1527 → #1528 → #1529 (chain), #1530 (no predecessors)
1719
+ → Confirms with user, dispatches #1526 + #1530 immediately
1720
+ → When #1526 completes, dispatches #1527 immediately (doesn't wait for #1530)
1721
+ → Streaming continues until all issues reach terminal state
1320
1722
 
1321
1723
  ### "orchestrate #1533 #1250"
1322
1724
  → Fetches those 2 issues, no deps between them
1323
- → Single wave, both parallel, confirms, executes
1725
+ → DAG: both have empty predecessor sets
1726
+ → Confirms, dispatches both simultaneously
1324
1727
 
1325
1728
  ### "orchestrate next 5"
1326
- → Gets top 5 priority issues, checks deps
1327
- → Plans waves, confirms, executes
1729
+ → Gets top 5 priority issues, builds DAG
1730
+ → Confirms, dispatches all ready issues immediately
1731
+ → Dependent issues dispatch as predecessors complete
1328
1732
 
1329
1733
  ### "orchestrate P0"
1330
1734
  → Gets all P0 issues, runs them all (presumably urgent)
1331
- → Single wave if possible, confirms, executes
1735
+ → DAG: likely all independent — dispatches simultaneously
1736
+ → Confirms, executes
1332
1737
 
1333
1738
  ### "orchestrate fast-lane"
1334
1739
  → Gets all unmilestoned bugs/fixes
1335
- → Groups by independence, runs in waves
1740
+ → Builds DAG from file-conflict and domain-serialization edges
1741
+ → Dispatches all ready issues, streams dependent issues as predecessors complete
1336
1742
 
1337
1743
  ### "orchestrate milestone user-auth-v2"
1338
1744
  → Fetches all open issues in "User Auth v2" milestone
1339
1745
  → Detects #42 is an investigation issue (title: "Investigate: session expiry race condition under high concurrency")
1340
- → Wave 0: Runs `/work-on 42` — investigation creates 3 new issues (#55-#57)
1746
+ → Phase 2: Runs `/work-on 42` — investigation creates 3 new issues (#55-#57)
1341
1747
  → Re-fetches milestone issues, now includes #55-#57
1342
- → Wave 1: #38 (P0 token refresh bug), #41 (session store migration), #55, #56 (all independent)
1343
- → Wave 2: #39 (enable refresh rotation — after #38 fixes token bug), #57 (depends on #55)
1344
- → Wave 3: remaining issues
1345
- → Final report shows investigation spawned issues and their results
1748
+ → Builds DAG: #38, #41, #55, #56 have no predecessors (ready); #39 depends on #38; #57 depends on #55
1749
+ → Dispatches #38, #41, #55, #56 simultaneously
1750
+ → When #38 completes, #39 dispatches immediately (doesn't wait for #41, #55, or #56)
1751
+ → When #55 completes, #57 dispatches immediately (doesn't wait for others)
1752
+ → Final report shows investigation spawned issues and streaming execution results