forgedock 1.1.8 → 1.1.9

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 (84) hide show
  1. package/README.md +10 -33
  2. package/bin/cinema.mjs +23 -5
  3. package/bin/engine-cli.mjs +137 -11
  4. package/bin/env-detect.mjs +249 -0
  5. package/bin/forge-utils.mjs +364 -4
  6. package/bin/forgedock.mjs +1064 -135
  7. package/bin/hooks/session-start.mjs +68 -3
  8. package/bin/init-detect.mjs +101 -12
  9. package/bin/journey.mjs +917 -16
  10. package/bin/recall.mjs +24 -7
  11. package/bin/registry.mjs +91 -3
  12. package/bin/runner.mjs +38 -2
  13. package/bin/tui.mjs +49 -3
  14. package/commands/adopt.md +1 -1
  15. package/commands/analytics.md +8 -8
  16. package/commands/audit.md +1 -1
  17. package/commands/autopilot.md +1 -1
  18. package/commands/ci-audit.md +1 -1
  19. package/commands/cleanup.md +87 -52
  20. package/commands/compat-audit.md +1 -1
  21. package/commands/deploy-info.md +1 -1
  22. package/commands/deploy-pr.md +1 -1
  23. package/commands/diagnose.md +1 -1
  24. package/commands/explain.md +1 -1
  25. package/commands/fix-ci.md +1 -1
  26. package/commands/forgedock-init.md +1 -1
  27. package/commands/incident-response.md +3 -3
  28. package/commands/issue.md +1 -1
  29. package/commands/optimize.md +1 -1
  30. package/commands/orchestrate/config.md +13 -3
  31. package/commands/orchestrate/phase-1-resolve.md +1 -1
  32. package/commands/orchestrate/phase-2-triage.md +1 -1
  33. package/commands/orchestrate/phase-2.5-synthesis.md +1 -1
  34. package/commands/orchestrate/phase-3-dependency.md +128 -20
  35. package/commands/orchestrate/phase-4-execution.md +598 -43
  36. package/commands/orchestrate/phase-5-cleanup.md +1 -1
  37. package/commands/orchestrate/phase-6-report.md +51 -3
  38. package/commands/orchestrate/safety.md +2 -2
  39. package/commands/qa-sweep.md +1 -1
  40. package/commands/quality-gate.md +1 -1
  41. package/commands/recover-orphans.md +5 -5
  42. package/commands/replay.md +1 -1
  43. package/commands/review-pr-agents/api.md +2 -2
  44. package/commands/review-pr-agents/auth.md +2 -2
  45. package/commands/review-pr-agents/billing.md +2 -2
  46. package/commands/review-pr-agents/concurrency.md +2 -2
  47. package/commands/review-pr-agents/database.md +2 -2
  48. package/commands/review-pr-agents/frontend.md +2 -2
  49. package/commands/review-pr-agents/infra.md +2 -2
  50. package/commands/review-pr-agents/protocols.md +1 -1
  51. package/commands/review-pr-agents/scraper.md +2 -2
  52. package/commands/review-pr-agents/security.md +2 -2
  53. package/commands/review-pr-staging.md +41 -3
  54. package/commands/review-pr.md +71 -6
  55. package/commands/rollback.md +1 -1
  56. package/commands/scope.md +1 -1
  57. package/commands/security-audit.md +1 -1
  58. package/commands/test-gate.md +1 -1
  59. package/commands/validate.md +1 -1
  60. package/commands/work-on/build/architect.md +1 -1
  61. package/commands/work-on/build/context.md +9 -6
  62. package/commands/work-on/build/implement.md +1 -1
  63. package/commands/work-on/build/validate.md +1 -1
  64. package/commands/work-on/build.md +31 -6
  65. package/commands/work-on/close.md +3 -2
  66. package/commands/work-on/decompose.md +1 -1
  67. package/commands/work-on/investigate.md +14 -5
  68. package/commands/work-on/remediate.md +331 -0
  69. package/commands/work-on/review.md +1 -1
  70. package/commands/work-on-monolithic.md +1 -1
  71. package/commands/work-on.md +194 -56
  72. package/docs/CI.md +2 -2
  73. package/docs/CODEX.md +1 -1
  74. package/docs/CONFIG.md +186 -0
  75. package/docs/site/command-reference.md +31 -1
  76. package/docs/site/getting-started.md +9 -34
  77. package/docs/site/index.md +1 -1
  78. package/docs/spec/label-state-machine.md +1 -1
  79. package/package.json +2 -1
  80. package/packages/protocol/src/types.js +1 -1
  81. package/scripts/check-command-docs-drift.sh +4 -2
  82. package/scripts/danger-zones.mjs +19 -12
  83. package/scripts/transition-label.sh +101 -13
  84. package/scripts/refresh-bot-token.sh +0 -97
@@ -50,7 +50,7 @@ All `{GH_REPO}`, `{GH_FLAG}`, `{REPO_PATH}`, `{STAGING_BRANCH}`, `{PROJECT_BOARD
50
50
  | `branches` | Prune worktrees and remote branches for merged PRs |
51
51
  | `milestones` | Report milestones with 0 open issues (advisory — never closes) |
52
52
  | `board` | Sync closed issues to Project board with correct terminal state |
53
- | `batch-p3` | Sweep: fold stale unbatched P3 review findings into domain-grouped batch issues |
53
+ | `batch-p3` | Sweep: fold stale unbatched P3 review findings into surface-area-grouped batch issues (same file, then leaf directory) |
54
54
  | `all` | All of the above, in order |
55
55
 
56
56
  ---
@@ -68,6 +68,9 @@ gh issue list {GH_FLAG} --state closed --label "workflow:in-review" --limit 100
68
68
  echo "=== Stale workflow:building ==="
69
69
  gh issue list {GH_FLAG} --state closed --label "workflow:building" --limit 100 --json number,title --jq '.[] | "#\(.number) — \(.title)"'
70
70
 
71
+ echo "=== Stale workflow:awaiting-merge ==="
72
+ gh issue list {GH_FLAG} --state closed --label "workflow:awaiting-merge" --limit 100 --json number,title --jq '.[] | "#\(.number) — \(.title)"'
73
+
71
74
  echo "=== Stale workflow:investigating ==="
72
75
  gh issue list {GH_FLAG} --state closed --label "workflow:investigating" --limit 100 --json number,title --jq '.[] | "#\(.number) — \(.title)"'
73
76
 
@@ -79,11 +82,11 @@ gh issue list {GH_FLAG} --state closed --label "needs-validation" --limit 100 --
79
82
 
80
83
  For each closed issue with a stale intermediate label:
81
84
 
82
- **Stale `workflow:in-review`, `workflow:building`** — these were merged but label wasn't updated:
85
+ **Stale `workflow:in-review`, `workflow:building`, `workflow:awaiting-merge`** — these were merged but label wasn't updated:
83
86
  ```bash
84
87
  for NUM in {stale_issue_numbers}; do
85
88
  gh issue edit $NUM {GH_FLAG} --add-label "workflow:merged"
86
- gh issue edit $NUM {GH_FLAG} --remove-label "workflow:in-review,workflow:building,needs-validation" 2>/dev/null || true
89
+ gh issue edit $NUM {GH_FLAG} --remove-label "workflow:in-review,workflow:building,workflow:awaiting-merge,needs-validation" 2>/dev/null || true
87
90
  done
88
91
  ```
89
92
 
@@ -223,7 +226,7 @@ For each orphaned issue found:
223
226
  ```bash
224
227
  gh issue close $NUM {GH_FLAG} --comment "Closed by cleanup — PR #$MERGED_PR was already merged."
225
228
  gh issue edit $NUM {GH_FLAG} --add-label "workflow:merged"
226
- gh issue edit $NUM {GH_FLAG} --remove-label "workflow:in-review" 2>/dev/null || true
229
+ gh issue edit $NUM {GH_FLAG} --remove-label "workflow:in-review,workflow:awaiting-merge" 2>/dev/null || true
227
230
  ```
228
231
 
229
232
  Also check open issues with `workflow:building` — same pattern (search for merged PRs referencing them).
@@ -370,101 +373,132 @@ Do NOT call `gh api ... -X PATCH -f state=closed` on any milestone. This phase i
370
373
  ## Phase 4B: P3 Batch Sweep (if `batch-p3` or `all`)
371
374
 
372
375
  <!-- Added: forge#1333 -->
376
+ <!-- Extended: forge#1828 — brought into lockstep with the P3 batching rule extension in orchestrate/phase-1-resolve.md and orchestrate/phase-4-execution.md (forge#1818): default-batchable eligibility, surface-area grouping, two-tier threshold, extended safety exclusions -->
373
377
 
374
- **Purpose**: Fold stale unbatched P3 review findings into domain-grouped batch issues. Reduces pipeline overhead from individual per-finding full-pipeline runs.
378
+ **Purpose**: Fold stale unbatched P3 review findings into surface-area-grouped batch issues (same file first, leaf directory as broader fallback). Reduces pipeline overhead from individual per-finding full-pipeline runs. This sweep applies the same batching rule as `orchestrate/phase-1-resolve.md`'s "P3 Review-Finding Batching" section (the canonical definition) — this file is a periodic, cross-run pass over the same policy that Phase 1 (at `/orchestrate` start) and Phase 4C (mid-run cascade findings, `phase-4-execution.md`) already apply.
375
379
 
376
380
  **Skip if**: Input is not `batch-p3` and not `all`.
377
381
 
378
382
  ### Step 4B.1: Fetch open batchable P3 findings
379
383
 
384
+ Eligibility is **default-batchable**: any open `review-finding` + `priority:P3` issue qualifies unless excluded below. The `<!-- FORGE:BATCHABLE -->` marker (still appended by `review-pr.md` at finding-creation time) is honored when present but is no longer required — matching `phase-1-resolve.md`'s eligibility rule. <!-- Changed: forge#1828 — marker was previously mandatory -->
385
+
380
386
  ```bash
381
- # Find all open P3 review findings that carry the FORGE:BATCHABLE annotation
382
- # and are not already members of a batch (no "batch" label)
387
+ # Find all open, unbatched P3 review findings. Excludes findings already
388
+ # claimed by a batch ("batch" label), and applies the same extended safety
389
+ # exclusions (security, billing, anti-bot, auth) as the other two batching
390
+ # sites, using the identical jq test() (Oniguruma) patterns so classification
391
+ # cannot diverge between the three sweep locations. <!-- Changed: forge#1828 -->
383
392
  UNBATCHED_P3=$(gh issue list {GH_FLAG} \
384
393
  --state open \
385
394
  --label "review-finding,priority:P3" \
386
395
  --limit 500 \
387
396
  --json number,title,body,labels,createdAt \
388
- --jq '.[] | select(.body | test("<!-- FORGE:BATCHABLE -->"))
389
- | select(([.labels[].name] | any(. == "batch")) | not)
390
- | select(([.labels[].name] | any(. == "security" or . == "billing")) | not)
391
- | select((.title | test("security|billing|payment|stripe"; "i")) | not)')
397
+ --jq '.[] | select(([.labels[].name] | any(. == "batch")) | not)
398
+ | select((.title | test("security|billing|anti-bot|auth"; "i")) | not)
399
+ | select(.body | test("## Problem[\\s\\S]{0,500}(security|billing|anti-bot|auth)"; "i") | not)
400
+ | select(([.labels[].name] | any(. == "security" or . == "billing" or . == "anti-bot" or . == "auth")) | not)')
392
401
 
393
402
  echo "Unbatched batchable P3 findings: $(echo "$UNBATCHED_P3" | jq -s 'length')"
394
403
  ```
395
404
 
396
- ### Step 4B.2: Group by domain
405
+ ### Step 4B.2: Group by surface area (same file, then leaf directory)
397
406
 
398
- For each finding, extract the domain from the first affected file path under `## Affected Files`:
407
+ <!-- Changed: forge#1828 — was coarse domain grouping (`cut -d/ -f1,2,3`); now surface area, matching phase-1-resolve.md -->
399
408
 
400
- ```bash
401
- # Domain = top-level directory of the first affected file
402
- # e.g., "services/api/auth/login.py" → "services/api/auth"
403
- # "commands/review-pr.md" → "commands"
404
- # Exclude findings where the domain contains "billing" or "security"
409
+ For each finding, extract the exact affected-file path under `## Affected Files` (primary grouping key) and its leaf directory — `dirname` of that path — as a broader fallback key:
405
410
 
406
- # Group findings by domain, track oldest creation timestamp per group
407
- declare -A DOMAIN_ISSUES
408
- declare -A DOMAIN_OLDEST
411
+ ```bash
412
+ # Surface area = the exact affected file path listed first under "## Affected
413
+ # Files" (primary key). Leaf directory = dirname of that file (fallback key).
414
+ # e.g., "commands/orchestrate/phase-1-resolve.md" → file "commands/orchestrate/phase-1-resolve.md", leaf-dir "commands/orchestrate"
415
+ # "commands/review-pr.md" → file "commands/review-pr.md", leaf-dir "commands"
416
+ # (security/billing/anti-bot/auth paths are already excluded in Step 4B.1)
417
+
418
+ # Group findings by file (primary) and by leaf directory (fallback), tracking
419
+ # oldest creation timestamp per group
420
+ declare -A FILE_ISSUES
421
+ declare -A FILE_OLDEST
422
+ declare -A DIR_ISSUES
423
+ declare -A DIR_OLDEST
409
424
 
410
425
  echo "$UNBATCHED_P3" | jq -c '.[]' | while read -r issue; do
411
426
  NUM=$(echo "$issue" | jq -r '.number')
412
427
  CREATED=$(echo "$issue" | jq -r '.createdAt')
413
428
  BODY=$(echo "$issue" | jq -r '.body')
414
429
 
415
- # Extract first affected file path from body
416
- FIRST_FILE=$(echo "$BODY" | sed -n '/^## Affected Files/,/^## /p' | grep -oP '`[^`]+`' | head -1 | tr -d '`')
430
+ # Extract first affected file path from body. Uses POSIX ERE (`grep -oE`),
431
+ # NOT PCRE lookbehind (`grep -oP '(?<=...)'`) — the prior fallback line here
432
+ # used `-P`, which BSD grep on macOS does not support (forge#1767). This
433
+ # pattern matches the portable one already used in phase-4-execution.md's
434
+ # same-run surface-area batching. <!-- Changed: forge#1828 -->
435
+ FIRST_FILE=$(echo "$BODY" | sed -n '/^## Affected Files/,/^## /p' | grep -oE '`[^`]+`' | head -1 | tr -d '`')
417
436
  if [ -z "$FIRST_FILE" ]; then
418
- FIRST_FILE=$(echo "$BODY" | grep -oP '(?<=\`)[^`]+\.(?:py|ts|tsx|js|go|rs|java|sh|md)(?=\`)' | head -1 || echo "unknown")
437
+ FIRST_FILE=$(echo "$BODY" | grep -oE '`[^`]+\.(py|tsx?|jsx?|sql|json|ya?ml|sh|md)`' | head -1 | tr -d '`')
419
438
  fi
420
- DOMAIN=$(echo "$FIRST_FILE" | cut -d/ -f1,2,3 | sed 's|/[^/]*$||')
421
- [ -z "$DOMAIN" ] && DOMAIN="general"
439
+ [ -z "$FIRST_FILE" ] && FIRST_FILE="unknown"
440
+ LEAF_DIR=$(dirname "$FIRST_FILE")
422
441
 
423
- echo "${NUM}:${DOMAIN}:${CREATED}"
442
+ echo "${NUM}:${FIRST_FILE}:${LEAF_DIR}:${CREATED}"
424
443
  done
425
444
  ```
426
445
 
427
- ### Step 4B.3: Apply batching threshold
446
+ ### Step 4B.3: Apply two-tier batching threshold
428
447
 
429
- For each domain group:
430
- - **Trigger**: 5+ unbatched batchable P3 findings in the domain, **OR** the oldest finding in the domain is > 72 hours old
431
- - **No trigger**: fewer than 5 findings AND none older than 72 hours → skip (no batch created)
448
+ <!-- Changed: forge#1828 — added lower same-file tier, matching phase-1-resolve.md's two-tier threshold -->
449
+
450
+ - **Same-file cluster** (primary, low threshold): **2+** unbatched batchable P3 findings share the exact same affected file → create a batch issue for that file cluster.
451
+ - **Leaf-directory cluster** (broader fallback, existing threshold preserved): among findings NOT already claimed by a same-file cluster above, **5+** share the same leaf directory, **OR** the oldest finding in that leaf directory is > 72 hours old → create a batch issue for that leaf-directory cluster.
452
+ - Form same-file clusters first; only findings left ungrouped after that pass are evaluated for leaf-directory clustering. A finding is claimed by at most one batch.
432
453
 
433
454
  ```bash
434
455
  NOW_EPOCH=$(date +%s)
435
456
  HOURS_72=$((72 * 3600))
457
+ MAX_MEMBERS=8 # Changed: forge#1828 — was 10, matching phase-1-resolve.md's cap
458
+
459
+ # Pass 1 — same-file clusters (2+ members). Claim member issue numbers into
460
+ # CLAIMED_BY_FILE so Pass 2 (leaf-directory) skips them.
461
+ CLAIMED_BY_FILE=""
462
+ echo "$FILE_ISSUES" | while IFS=: read -r file issue_list; do
463
+ COUNT=$(echo "$issue_list" | tr ',' '\n' | grep -c .)
464
+ if [ "$COUNT" -ge 2 ]; then
465
+ echo "Batching $COUNT P3 findings — same-file cluster: ${file}"
466
+ # Create batch issue(s) in chunks of <= MAX_MEMBERS members (see Batch
467
+ # creation below). Record consumed issue numbers in CLAIMED_BY_FILE.
468
+ fi
469
+ done
436
470
 
437
- # For each domain group that meets the threshold, create a batch issue
438
- # (max 10 members per batch — if more, create multiple batches of ≤ 10 each)
439
-
440
- # Exclude domains with "security" or "billing" in the domain path
441
- echo "$DOMAIN_ISSUES" | while IFS=: read -r domain issue_list; do
442
- echo "$domain" | grep -qiE 'security|billing' && continue
471
+ # Pass 2 — leaf-directory clusters (5+ members, or oldest > 72h), excluding
472
+ # any issue already claimed by a same-file cluster in Pass 1
473
+ echo "$DIR_ISSUES" | while IFS=: read -r dir issue_list; do
474
+ REMAINING=$(echo "$issue_list" | tr ',' '\n' | grep -vxF -f <(printf '%s\n' "$CLAIMED_BY_FILE" | tr ',' '\n') || true)
475
+ COUNT=$(echo "$REMAINING" | grep -c . || echo 0)
476
+ [ "$COUNT" -eq 0 ] && continue
443
477
 
444
- COUNT=$(echo "$issue_list" | tr ',' '\n' | wc -l)
445
- OLDEST_EPOCH=$(echo "$DOMAIN_OLDEST[$domain]" | date -d "$..." +%s 2>/dev/null || echo "$NOW_EPOCH")
478
+ OLDEST_EPOCH=$(echo "${DIR_OLDEST[$dir]}" | date -d "$..." +%s 2>/dev/null || echo "$NOW_EPOCH")
446
479
  AGE_SECS=$((NOW_EPOCH - OLDEST_EPOCH))
447
480
 
448
481
  if [ "$COUNT" -ge 5 ] || [ "$AGE_SECS" -ge "$HOURS_72" ]; then
449
- echo "Batching $COUNT P3 findings in domain: ${domain}"
450
- # Create batch issues (in chunks of ≤ 10)
451
- # See orchestrate.md Phase 1 for the batch creation template
482
+ echo "Batching $COUNT P3 findings — leaf-directory cluster: ${dir}"
483
+ # Create batch issue(s) in chunks of <= MAX_MEMBERS members (see Batch
484
+ # creation below).
452
485
  fi
453
486
  done
454
487
  ```
455
488
 
456
- **Batch creation** uses the same template as `orchestrate.md Phase 1 → P3 Review-Finding Batching`. After creating the batch issue, add the `batch` label to each member issue to prevent re-batching on the next sweep.
489
+ **Batch creation** uses the same template as `orchestrate.md Phase 1 → P3 Review-Finding Batching`, including its surface-area path sanitization (`tr -cd 'A-Za-z0-9._/-'` on the file/directory value before interpolating it into the batch issue's `--title`/`--body`). After creating the batch issue, add the `batch` label to each member issue to prevent re-batching on the next sweep.
457
490
 
458
491
  ### Step 4B.4: Report
459
492
 
460
493
  ```
461
494
  ## P3 Batch Sweep
462
495
 
463
- | Domain | Unbatched P3s | Threshold Met | Action |
464
- |--------|--------------|---------------|--------|
465
- | {domain} | {N} | ≥5 issues | Created batch #{NUM} ({M} members) |
466
- | {domain} | {N} | Oldest > 72h | Created batch #{NUM} ({M} members) |
467
- | {domain} | {N} | Below threshold | No action |
496
+ | Surface Area | Unbatched P3s | Threshold Met | Action |
497
+ |---------------|--------------|---------------|--------|
498
+ | {file} | {N} | ≥2 same-file | Created batch #{NUM} ({M} members) |
499
+ | {leaf-dir} | {N} | ≥5 issues | Created batch #{NUM} ({M} members) |
500
+ | {leaf-dir} | {N} | Oldest > 72h | Created batch #{NUM} ({M} members) |
501
+ | {leaf-dir} | {N} | Below threshold | No action |
468
502
 
469
503
  Total batch issues created: {N}
470
504
  Total P3 findings batched: {N}
@@ -550,8 +584,9 @@ fi
550
584
  {N} closed issues have no workflow label (closed outside pipeline — no action needed)
551
585
 
552
586
  ### P3 Batch Sweep Results
553
- | Domain | Unbatched P3s | Action |
554
- |--------|--------------|--------|
555
- | {domain} | {N} | Created batch #{NUM} with {M} members |
556
- | {domain} | {N} | Below threshold — no action (< 5 findings, none > 72h) |
587
+ | Surface Area | Unbatched P3s | Action |
588
+ |---------------|--------------|--------|
589
+ | {file} | {N} | Created batch #{NUM} with {M} members (same-file, ≥2) |
590
+ | {leaf-dir} | {N} | Created batch #{NUM} with {M} members (leaf-directory, ≥5 or >72h) |
591
+ | {leaf-dir} | {N} | Below threshold — no action (< 5 findings, none > 72h, no same-file pair) |
557
592
  ```
@@ -10,7 +10,7 @@ install: extras
10
10
 
11
11
  You are the ForgeDock compatibility auditor. Produce a point-in-time advisory report that shows whether the user's installed Claude Code version is current and which ForgeDock features may behave differently on their runtime. This command is **non-blocking** — it never aborts the session. All output is advisory.
12
12
 
13
- **Agent model policy**: `model: "sonnet"` (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
13
+ **Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet" (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
14
14
  **NEVER use plan mode (EnterPlanMode).**
15
15
 
16
16
  ---
@@ -16,7 +16,7 @@ install: extras
16
16
 
17
17
  You are the pipeline's deploy awareness layer. Before the user merges staging → main (triggering CI/CD deployment), this command shows exactly what's going out: which PRs, which issues, what changed, and what risks exist.
18
18
 
19
- **Agent model policy**: `model: "sonnet"` (standard tier). Fallback: `model: "opus"` if rate-limited. User can override with `--model <name>`. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
19
+ **Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet" (standard tier). Fallback: `model: "opus"` if rate-limited. User can override with `--model <name>`. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
20
20
 
21
21
  **NEVER use plan mode (EnterPlanMode)** — it breaks execution context.
22
22
 
@@ -13,7 +13,7 @@ allowed-tools: Bash, Read, Grep, Glob, Skill
13
13
  **NEVER use plan mode (EnterPlanMode)** — it breaks execution context.
14
14
  **NEVER use the Agent tool** — deploy-pr dispatches sub-skills via `Skill(...)` only. The Agent tool bypasses the allowed-tools constraint and produces opaque output that cannot be structured into the deploy result.
15
15
 
16
- **Agent model policy**: `model: "sonnet"` (standard tier). Fallback: `model: "opus"` if rate-limited. User can override with `--model <name>`.
16
+ **Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet" (standard tier). Fallback: `model: "opus"` if rate-limited. User can override with `--model <name>`.
17
17
 
18
18
  <!-- FORGE:SPEC_LOADED — deploy-pr.md loaded and active. Agent is bound by this spec. -->
19
19
 
@@ -10,7 +10,7 @@ install: extras
10
10
 
11
11
  **NEVER use plan mode (EnterPlanMode)** — it breaks execution context.
12
12
 
13
- **Agent model policy**: `model: "sonnet"` (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
13
+ **Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet" (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
14
14
 
15
15
  You are the pipeline's diagnostic layer. Given a single GitHub issue number, you reconstruct the full FORGE annotation chain for that pipeline run, identify the exact failure point, and suggest specific remediation steps. This command is **read-only** — it never mutates GitHub state.
16
16
 
@@ -10,7 +10,7 @@ install: extras
10
10
 
11
11
  You translate the FORGE pipeline's machine-readable annotations on a GitHub issue or PR into a clear, plain-English narrative that anyone can understand — no pipeline knowledge required. This is a read-only command: it never writes to GitHub, never changes labels, and never modifies any state.
12
12
 
13
- **Agent model policy**: `model: "sonnet"` (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
13
+ **Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet" (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
14
14
  **NEVER use plan mode (EnterPlanMode).**
15
15
 
16
16
  ---
@@ -13,7 +13,7 @@ argument-hint: <PR number or URL> [--max-attempts N] [--repo owner/repo]
13
13
 
14
14
  **Output**: Structured result — `{ pr, status, attempts, fixes_applied }` — printed to stdout and available to the calling skill.
15
15
 
16
- **Agent model policy**: `model: "sonnet"` (standard tier). Fallback: `model: "opus"` if rate-limited. User can override with `--model <name>`. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
16
+ **Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet" (standard tier). Fallback: `model: "opus"` if rate-limited. User can override with `--model <name>`. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
17
17
  **NEVER use plan mode (EnterPlanMode).**
18
18
  **NEVER force-push or amend commits** — always create new commits.
19
19
 
@@ -11,7 +11,7 @@ argument-hint: [--preserve | --interactive | --section <name>]
11
11
 
12
12
  You complete the `forge.yaml` configuration that `npx forgedock init` started. The CLI generates required sections (project, paths, branches) from auto-detection. Your job is to scan the codebase, query GitHub APIs, and produce a complete `forge.yaml` with every applicable optional section filled — using detected values directly, without asking for confirmation.
13
13
 
14
- **Agent model policy**: `model: "sonnet"` (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
14
+ **Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet" (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
15
15
  **NEVER use plan mode (EnterPlanMode).**
16
16
 
17
17
  ---
@@ -16,7 +16,7 @@ install: extras
16
16
 
17
17
  You are the pipeline's incident response coordinator. When production goes down or a critical bug surfaces, this command orchestrates the response: validates the hotfix before deploy, reconstructs the incident timeline, and produces a post-incident analysis.
18
18
 
19
- **Agent model policy**: `model: "sonnet"` (standard tier). Fallback: `model: "opus"` if rate-limited. User can override with `--model <name>`. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
19
+ **Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet" (standard tier). Fallback: `model: "opus"` if rate-limited. User can override with `--model <name>`. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
20
20
 
21
21
  **NEVER use plan mode (EnterPlanMode)** — it breaks execution context.
22
22
 
@@ -109,7 +109,7 @@ gh api repos/{owner}/{repo}/issues/{NUMBER}/comments \
109
109
  Spawn a validation agent to check the proposed fix:
110
110
 
111
111
  ```
112
- Agent(model="sonnet", prompt="
112
+ Agent(model="{SUBAGENT_MODEL}", prompt="
113
113
  You are validating a hotfix for a P0 production incident.
114
114
 
115
115
  Issue: #{NUMBER} — {TITLE}
@@ -242,7 +242,7 @@ gh pr list --state merged --base main --json number,title,mergedAt \
242
242
  Spawn an investigation agent:
243
243
 
244
244
  ```
245
- Agent(model="sonnet", prompt="
245
+ Agent(model="{SUBAGENT_MODEL}", prompt="
246
246
  You are conducting a post-incident root cause analysis.
247
247
 
248
248
  Incident: #{NUMBER} — {TITLE}
package/commands/issue.md CHANGED
@@ -11,7 +11,7 @@ argument-hint: [description of the problem or feature]
11
11
 
12
12
  You create GitHub issues with the exact structure the `/work-on` pipeline expects. Every issue you create must give the investigation agent enough context to find the right files on the first pass — no vague descriptions, no missing domains, no ambiguous scope.
13
13
 
14
- **Agent model policy**: `model: "sonnet"` (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
14
+ **Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet" (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
15
15
  **NEVER use plan mode (EnterPlanMode).**
16
16
 
17
17
  ---
@@ -10,7 +10,7 @@ install: extras
10
10
 
11
11
  Proactively analyze the current repository and generate `.forgedock/scripts/` entries from `forge.yaml → learned:`, git commit history, and pipeline annotations — without waiting for a correction event. This is the proactive half of the learning loop; reactive capture happens in `/work-on` Phase 1D.
12
12
 
13
- **Agent model policy**: `model: "sonnet"` (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
13
+ **Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet" (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
14
14
  **NEVER use plan mode (EnterPlanMode).**
15
15
 
16
16
  ---
@@ -1,5 +1,5 @@
1
1
  ---
2
- install: internal
2
+ install: core
3
3
  ---
4
4
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
5
5
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
@@ -22,7 +22,9 @@ You are the top-level orchestrator. Your job is to take a batch of issues, plan
22
22
 
23
23
  **You have access to ALL tools** — Agent tool (critical), Task tool, Skill tool, Bash, everything. Use the Agent tool aggressively to parallelize work.
24
24
 
25
- **Agent model policy**: `model: "haiku"`, `effort: low` (mechanical — dispatch bookkeeping, lane routing, classification). Fallback: `model: "sonnet"` if rate-limited. User can override with `--model <name>`. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
25
+ **Agent model policy**: `model: "haiku"`, `effort: low` (mechanical — dispatch bookkeeping, lane routing, classification). Fallback: `model: "sonnet"` if rate-limited. User can override with `--model <name>`. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154. **Distinct from the spawned `/work-on` sub-agents** (Phase 4, per-issue and review-finding sweep): those resolve via `model: "{SUBAGENT_MODEL}"` — forge.yaml `agents.subagent_model`, else `agents.default_model`, else `"sonnet"` — not this dispatcher's own `haiku` policy.
26
+
27
+ **Hard exclusion — already enforced by this design, not left to convention**: this `haiku`/`effort: low` tier applies ONLY to the orchestrator's own mechanical bookkeeping (dispatch, lane routing, classification) — never to the spawned `/work-on` sub-agents, which always run at `{SUBAGENT_MODEL}` (standard tier, sonnet by default). Every reasoning-heavy `/work-on` sub-phase file (`investigate.md`, `build/architect.md`, `build/implement.md`, `review.md`) carries its own "Agent model policy" line pinned to `{DEFAULT_MODEL}`/standard tier — see `work-on.md`'s `FORGE:MODEL_TIER_NOTE` for the canonical explanation of why these are never downtiered, and forge#1827 for the explicit correction against adding a `model: "haiku"` claim to any file where mechanical bits are interleaved with reasoning-heavy content. Do not introduce a second, competing deterministic-step-to-cheap-model config — this per-file tiering plus the dispatcher/sub-agent split above is the single source of truth for which steps may use a cheaper model.
26
28
  **NEVER use plan mode (EnterPlanMode).**
27
29
 
28
30
  <!-- FORGE:SPEC_LOADED — orchestrate.md loaded and active. Agent is bound by HARD RULES above. -->
@@ -44,9 +46,17 @@ STAGING_BRANCH=$(yq '.branches.staging' "$CONFIG_FILE")
44
46
  DEFAULT_BRANCH=$(yq '.branches.default' "$CONFIG_FILE")
45
47
  # Build satellite repo map from repos.satellites list
46
48
  # Each satellite: { prefix, repo, staging_branch }
49
+
50
+ # Narration verbosity for Step 4B's per-completion status updates (phase-4-execution.md item 8).
51
+ # "terse" (default when unset): one-line status per completion — matches today's existing
52
+ # behavior, so leaving pipeline.narration unset is a no-op. "verbose": adds a running
53
+ # per-completion recap table. Never affects the Step 6B consolidated report, which always
54
+ # renders full tables once at the end regardless of this value.
55
+ NARRATION_MODE=$(yq '.pipeline.narration // "terse"' "$CONFIG_FILE" 2>/dev/null || echo "terse")
56
+ [ "$NARRATION_MODE" = "null" ] && NARRATION_MODE="terse"
47
57
  ```
48
58
 
49
- All `{GH_REPO}`, `{GH_FLAG}`, `{REPO_PATH}`, `{PROJECT_NAME}`, `{STAGING_BRANCH}`, and `{DEFAULT_BRANCH}` references below are populated from `forge.yaml`.
59
+ All `{GH_REPO}`, `{GH_FLAG}`, `{REPO_PATH}`, `{PROJECT_NAME}`, `{STAGING_BRANCH}`, `{DEFAULT_BRANCH}`, and `{NARRATION_MODE}` references below are populated from `forge.yaml`.
50
60
 
51
61
  ---
52
62
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- install: internal
2
+ install: core
3
3
  ---
4
4
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
5
5
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
@@ -1,5 +1,5 @@
1
1
  ---
2
- install: internal
2
+ install: core
3
3
  ---
4
4
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
5
5
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
@@ -1,5 +1,5 @@
1
1
  ---
2
- install: internal
2
+ install: core
3
3
  ---
4
4
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
5
5
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->