forgedock 1.8.1 → 1.8.3

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 (90) hide show
  1. package/bin/engine/admission.mjs +57 -1
  2. package/bin/engine/admission.test.mjs +60 -0
  3. package/commands/analytics.md +3 -1
  4. package/commands/audit-agents.md +3 -1
  5. package/commands/audit.md +10 -8
  6. package/commands/autopilot.md +16 -14
  7. package/commands/ci-audit.md +4 -2
  8. package/commands/cleanup.md +1 -1
  9. package/commands/deploy-pr.md +31 -11
  10. package/commands/design-bench.md +1 -0
  11. package/commands/design-render-critique-loop.md +1 -0
  12. package/commands/design.md +1 -0
  13. package/commands/forge-stats.md +1 -0
  14. package/commands/geo-audit.md +3 -1
  15. package/commands/incident-response.md +3 -1
  16. package/commands/issue.md +4 -2
  17. package/commands/milestone.md +8 -6
  18. package/commands/orchestrate/config.md +3 -2
  19. package/commands/orchestrate/phase-1-resolve.md +57 -12
  20. package/commands/orchestrate/phase-2-triage.md +1 -0
  21. package/commands/orchestrate/phase-2.5-synthesis.md +1 -0
  22. package/commands/orchestrate/phase-3-dependency.md +17 -5
  23. package/commands/orchestrate/phase-4-execution.md +625 -78
  24. package/commands/orchestrate/phase-5-cleanup.md +5 -2
  25. package/commands/orchestrate/phase-6-report.md +8 -3
  26. package/commands/orchestrate/safety.md +1 -0
  27. package/commands/orchestrate.md +13 -9
  28. package/commands/pipeline-health/config.md +1 -0
  29. package/commands/pipeline-health/phase-1-context.md +1 -0
  30. package/commands/pipeline-health/phase-2-metrics.md +1 -0
  31. package/commands/pipeline-health/phase-3-analyze.md +1 -0
  32. package/commands/pipeline-health/phase-4-proposals.md +7 -4
  33. package/commands/pipeline-health/phase-5-report.md +5 -2
  34. package/commands/pipeline-health/phase-6-summary.md +1 -0
  35. package/commands/pipeline-health.md +9 -7
  36. package/commands/pipeline-resume.md +4 -2
  37. package/commands/qa-sweep.md +3 -1
  38. package/commands/quality-gate.md +4 -3
  39. package/commands/recover-orphans.md +219 -13
  40. package/commands/review-pr-agents/api.md +1 -0
  41. package/commands/review-pr-agents/auth.md +1 -0
  42. package/commands/review-pr-agents/billing.md +1 -0
  43. package/commands/review-pr-agents/concurrency.md +1 -0
  44. package/commands/review-pr-agents/database.md +1 -0
  45. package/commands/review-pr-agents/frontend.md +1 -0
  46. package/commands/review-pr-agents/infra.md +1 -0
  47. package/commands/review-pr-agents/protocols.md +1 -0
  48. package/commands/review-pr-agents/scraper.md +1 -0
  49. package/commands/review-pr-agents/security.md +1 -0
  50. package/commands/review-pr-agents/spec-cli.md +1 -0
  51. package/commands/review-pr-agents.md +10 -4
  52. package/commands/review-pr-staging.md +14 -5
  53. package/commands/review-pr.md +187 -19
  54. package/commands/rollback.md +3 -1
  55. package/commands/security-audit.md +3 -1
  56. package/commands/signal-planner.md +6 -4
  57. package/commands/sync-ecosystem.md +4 -2
  58. package/commands/test-gate.md +4 -2
  59. package/commands/upgrade-deps.md +3 -1
  60. package/commands/validate.md +3 -1
  61. package/commands/work-on/build/architect.md +82 -19
  62. package/commands/work-on/build/context.md +62 -18
  63. package/commands/work-on/build/implement.md +63 -18
  64. package/commands/work-on/build/validate.md +241 -60
  65. package/commands/work-on/build.md +345 -146
  66. package/commands/work-on/close.md +618 -246
  67. package/commands/work-on/decompose.md +240 -41
  68. package/commands/work-on/investigate.md +378 -61
  69. package/commands/work-on/remediate.md +43 -9
  70. package/commands/work-on/review.md +241 -29
  71. package/commands/work-on.md +180 -2014
  72. package/docs/CODEX.md +2 -0
  73. package/docs/CONFIG.md +11 -0
  74. package/package.json +1 -1
  75. package/scripts/build-spec-graph.mjs +11 -4
  76. package/scripts/check-spec-markers.sh +21 -1
  77. package/scripts/checkbox-sections.test.sh +8 -23
  78. package/scripts/extract-affected-files.sh +5 -2
  79. package/scripts/extract-affected-files.test.sh +27 -0
  80. package/scripts/forge-root.test.sh +176 -0
  81. package/scripts/is-docs-only.sh +31 -0
  82. package/scripts/is-docs-only.test.sh +64 -0
  83. package/scripts/lint-dispatch-prompt.sh +217 -0
  84. package/scripts/lint-dispatch-prompt.test.sh +307 -0
  85. package/scripts/validate-spec-graph.sh +3 -2
  86. package/scripts/verify-phase-trail.sh +282 -0
  87. package/scripts/verify-phase-trail.test.sh +298 -0
  88. package/scripts/wait-ci-green.sh +98 -0
  89. package/scripts/wait-ci-green.test.sh +99 -0
  90. package/commands/work-on-monolithic.md +0 -326
@@ -8,6 +8,8 @@ allowed-tools: Task, Agent, Bash, Read, Grep, Glob, WebFetch, Skill
8
8
 
9
9
  # PR Review — Orchestrator
10
10
 
11
+ > **Skill names**: `{FORGE_SKILL_PREFIX}` is `forgedock:` (plugin install) or empty (`install.sh`), resolved once per run by `commands/work-on.md` § Skill Name Resolution. If the skill is not found under either name, STOP and report "skill not found" — never run the phase inline.
12
+
11
13
  **Input**: $ARGUMENTS
12
14
 
13
15
  **NEVER use plan mode (EnterPlanMode)** during review — it breaks execution context.
@@ -23,9 +25,9 @@ allowed-tools: Task, Agent, Bash, Read, Grep, Glob, WebFetch, Skill
23
25
 
24
26
  2. **Post the FORGE:REVIEW verdict regardless of finding severity.** A review that completes but posts no `<!-- FORGE:REVIEW -->` comment is invisible to the pipeline. Even a PASS verdict must be posted.
25
27
 
26
- 3. **Review findings do NOT block merge UNLESS they meet the Blocking Criteria in §7B** (a CONFIRMED HIGH/CRITICAL finding, a purpose regression, a merge conflict, or a build/type/test failure) **or the calibration threshold check in §7B.5 sets `CALIBRATION_NEEDS_HUMAN=true`** (HIGH-confidence task type with historical survival < 80%). File every finding as a GitHub issue with the `review-finding` label regardless of severity. Minor/style findings never block; §7B's and §7B.5's blocking conditions always do — including under `--auto-merge`. <!-- forge#1741 -->
28
+ 3. **Review findings do NOT block merge UNLESS they meet the Blocking Criteria in §7B** (a CONFIRMED HIGH/CRITICAL finding, a purpose regression, a merge conflict, or a build/type/test failure) **or the calibration threshold check in §7B.5 sets `CALIBRATION_NEEDS_HUMAN=true`** (HIGH-confidence task type with historical survival < 80%). File every finding that survives the §6B.5 note disposition (MEDIUM+ severity, or CONFIRMED/LIKELY above LOW; security/billing always) as a GitHub issue with the `review-finding` label. LOW/POSSIBLE notes are fixed in-PR, listed in the PR body, or dropped — never filed as standalone issues. Minor/style findings never block; §7B's and §7B.5's blocking conditions always do — including under `--auto-merge`. <!-- forge#1741 -->
27
29
 
28
- 4. **Route correctly at Phase 0.** If the input is "staging" or the PR targets `main`, invoke `Skill("review-pr-staging", ...)` — do NOT run the standard PR review pipeline against a staging→main PR.
30
+ 4. **Route correctly at Phase 0.** If the input is "staging" or the PR targets `main`, invoke `Skill("{FORGE_SKILL_PREFIX}review-pr-staging", ...)` — do NOT run the standard PR review pipeline against a staging→main PR.
29
31
 
30
32
  5. **`spec-evolution` PRs are NEVER auto-merged.** When a PR carries the `spec-evolution` label (created by `/spec-doctor`), Phase -1 MUST set `AUTO_MERGE=false` and add `needs-human` before any other processing. This cannot be overridden by the caller — the eval gate plus human review are the only permitted merge path. See Phase -1 `spec-evolution guard` block. <!-- Added: forge#1742 -->
31
33
 
@@ -80,17 +82,17 @@ This is the **orchestrator**. It routes to the right review mode, runs automated
80
82
 
81
83
  | File | What | How to invoke |
82
84
  |------|------|---------------|
83
- | `${FORGE_HOME:-$REPO_PATH}/commands/review-pr-agents/protocols.md` | Shared review protocols (Evidence-Based + Structured Findings + Input Scoping) | `Read` tool during Phase 3C (always) |
84
- | `${FORGE_HOME:-$REPO_PATH}/commands/review-pr-agents/<persona>.md` | Per-persona agent prompt templates (9 files) | `Read` tool during Phase 3C (selected agents only) |
85
- | `${FORGE_HOME:-$REPO_PATH}/commands/review-pr-staging.md` | Full staging→main review pipeline | `Skill("review-pr-staging", ...)` during Phase 0 |
85
+ | `${CLAUDE_PLUGIN_ROOT}/commands/review-pr-agents/protocols.md` | Shared review protocols (Evidence-Based + Structured Findings + Input Scoping) | `Read` tool during Phase 3C (always) |
86
+ | `${CLAUDE_PLUGIN_ROOT}/commands/review-pr-agents/<persona>.md` | Per-persona agent prompt templates (9 files) | `Read` tool during Phase 3C (selected agents only) |
87
+ | `${CLAUDE_PLUGIN_ROOT}/commands/review-pr-staging.md` | Full staging→main review pipeline | `Skill("{FORGE_SKILL_PREFIX}review-pr-staging", ...)` during Phase 0 |
86
88
 
87
- `$FORGE_HOME` defaults to `~/.claude` (the directory where `npx forgedock` symlinks commands). When unset, every resolution in this file falls back to `$REPO_PATH` (the repo root, from `forge.yaml → paths.root`) rather than degrading to a bare root-anchored path — see the `TEMPLATE_BASE` tiered guard in Phase 3C and the verification-script resolution in Step 2.5B for the actual fallback chains. Never resolve a missing file via a filesystem-wide `find` — see the guardrail in `commands/review-pr-agents/protocols.md`.
89
+ Paths above are rooted at `${CLAUDE_PLUGIN_ROOT}`, the running plugin's install (filled in by Claude Code). If that path does not start with `/` (install.sh, Codex, OpenCode), use the tiered resolution below instead. `$FORGE_HOME` defaults to `~/.claude` (the directory where `npx forgedock` symlinks commands). When unset, every resolution in this file falls back to `$REPO_PATH` (the repo root, from `forge.yaml → paths.root`) rather than degrading to a bare root-anchored path — see the `TEMPLATE_BASE` tiered guard in Phase 3C and the verification-script resolution in Step 2.5B for the actual fallback chains. Never resolve a missing file via a filesystem-wide `find` — see the guardrail in `commands/review-pr-agents/protocols.md`.
88
90
 
89
91
  **Invocation flow:**
90
92
  ```
91
93
  /review-pr 5428 → Phase 0 detects single PR → runs Phases 1-9 inline
92
- /review-pr staging → Phase 0 detects staging mode → Skill("review-pr-staging", "staging")
93
- /review-pr 5500 → Phase 0 auto-detects staging→main PR → Skill("review-pr-staging", "5500")
94
+ /review-pr staging → Phase 0 detects staging mode → Skill("{FORGE_SKILL_PREFIX}review-pr-staging", "staging")
95
+ /review-pr 5500 → Phase 0 auto-detects staging→main PR → Skill("{FORGE_SKILL_PREFIX}review-pr-staging", "5500")
94
96
  /review-pr 3126 --auto-merge --issue 3124 --base staging → single PR + auto-merge after approval
95
97
  ```
96
98
 
@@ -106,6 +108,8 @@ Example: "3126 --auto-merge --issue 3124 --base staging --gh-flag -R $GH_REPO --
106
108
 
107
109
  Extract: `PR_NUMBER`, `AUTO_MERGE=true`, `MERGE_ISSUE`, `MERGE_BASE`, `MERGE_GH_FLAG`, `MERGE_WORKTREE` (optional — the absolute path to the git worktree to clean up after merge)
108
110
 
111
+ **`--auto-merge` requires `--issue`** <!-- Added: forge#3102, forge#3124 -->: the phase-trail gate in Phase 8 verifies the trail of the linked issue, so `--issue` is mandatory whenever `AUTO_MERGE=true`. Do NOT infer `MERGE_ISSUE` from the PR: the PR body's closing reference and the head-branch suffix are both author-controlled (and the closing-issue reference list is empty for PRs based on a non-default branch such as `staging`), so neither is a trustworthy binding. If `AUTO_MERGE=true` and `MERGE_ISSUE` is empty, Phase 8 does not merge and returns the documented `REVIEW_RESULT: status: BLOCKED`, blocker: "auto-merge requires --issue". The PR stays open for a human or a re-run with `--issue`.
112
+
109
113
  If `--auto-merge` is NOT present, `AUTO_MERGE=false` — Phase 8 (Auto-Merge) will be skipped.
110
114
 
111
115
  ### Thoroughness Flag
@@ -189,7 +193,7 @@ if [ "$REVIEW_MODE" != "staging-keyword" ] && [ "$REVIEW_MODE" != "multi-pr" ];
189
193
  fi
190
194
  ```
191
195
 
192
- **Invariant**: After this phase, `REVIEW_MODE` and (where applicable) `ROUTE_PR_NUMBER` are set. Any sub-invocation of `Skill("review-pr-staging", ...)` should immediately post its own `FORGE:REVIEW_ROUTE` marker scoped to the PR it resolves.
196
+ **Invariant**: After this phase, `REVIEW_MODE` and (where applicable) `ROUTE_PR_NUMBER` are set. Any sub-invocation of `Skill("{FORGE_SKILL_PREFIX}review-pr-staging", ...)` should immediately post its own `FORGE:REVIEW_ROUTE` marker scoped to the PR it resolves.
193
197
 
194
198
  ---
195
199
 
@@ -204,7 +208,7 @@ Check input to determine which mode:
204
208
  If `$ARGUMENTS` is "staging", "feature", or "staging:feature":
205
209
 
206
210
  ```
207
- >>> INVOKE: Skill("review-pr-staging", "$ARGUMENTS")
211
+ >>> INVOKE: Skill("{FORGE_SKILL_PREFIX}review-pr-staging", "$ARGUMENTS")
208
212
  >>> THEN STOP — the staging command handles the full flow.
209
213
  ```
210
214
 
@@ -227,7 +231,7 @@ BASE=$(echo $PR_INFO | jq -r '.baseRefName')
227
231
 
228
232
  If `HEAD = "staging" AND BASE = "main"` OR `HEAD = "feature" AND BASE = "main"`:
229
233
  ```
230
- >>> INVOKE: Skill("review-pr-staging", "$ARGUMENTS")
234
+ >>> INVOKE: Skill("{FORGE_SKILL_PREFIX}review-pr-staging", "$ARGUMENTS")
231
235
  >>> THEN STOP.
232
236
  ```
233
237
 
@@ -635,7 +639,10 @@ REPO_ROOT="." # Assumes cwd is the repo root
635
639
  # that degrades to a root-anchored path (/scripts/verify-*.sh) and silently skips
636
640
  # every check below. Never fall back to a filesystem-wide `find`.
637
641
  # <!-- Added: forge#2035 -->
638
- if [ -n "$FORGE_HOME" ] && [ -f "$FORGE_HOME/scripts/verify-route-registration.sh" ]; then
642
+ _PLUGIN_ROOT='${CLAUDE_PLUGIN_ROOT}'; case "$_PLUGIN_ROOT" in /*) ;; *) _PLUGIN_ROOT="" ;; esac
643
+ if [ -n "$_PLUGIN_ROOT" ] && [ -f "$_PLUGIN_ROOT/scripts/verify-route-registration.sh" ]; then
644
+ SCRIPTS_HOME="$_PLUGIN_ROOT" # running plugin's own root first (see Phase 3C Tier 0)
645
+ elif [ -n "$FORGE_HOME" ] && [ -f "$FORGE_HOME/scripts/verify-route-registration.sh" ]; then
639
646
  SCRIPTS_HOME="$FORGE_HOME"
640
647
  else
641
648
  FORGE_YAML="${FORGE_CONFIG:-$(git rev-parse --show-toplevel 2>/dev/null)/forge.yaml}"
@@ -1318,7 +1325,13 @@ Missing persona templates are a fatal setup error, not permission to skip multi-
1318
1325
  ```bash
1319
1326
  # Tier 1: $FORGE_HOME (the installed location — the common case)
1320
1327
  TEMPLATE_BASE=""
1321
- if [[ -f "$FORGE_HOME/commands/review-pr-agents/protocols.md" ]]; then
1328
+ # Tier 0: the running plugin's own root (Claude Code substitutes this exact spelling; elsewhere it stays a
1329
+ # literal that the /* check rejects). Wins over an exported FORGE_HOME, which may name an older checkout.
1330
+ _PLUGIN_ROOT='${CLAUDE_PLUGIN_ROOT}'; case "$_PLUGIN_ROOT" in /*) ;; *) _PLUGIN_ROOT="" ;; esac
1331
+ if [[ -n "$_PLUGIN_ROOT" && -f "$_PLUGIN_ROOT/commands/review-pr-agents/protocols.md" ]]; then
1332
+ TEMPLATE_BASE="$_PLUGIN_ROOT/commands/review-pr-agents"
1333
+ TEMPLATE_SOURCE="plugin_root"
1334
+ elif [[ -f "$FORGE_HOME/commands/review-pr-agents/protocols.md" ]]; then
1322
1335
  TEMPLATE_BASE="$FORGE_HOME/commands/review-pr-agents"
1323
1336
  TEMPLATE_SOURCE="forge_home"
1324
1337
  else
@@ -1353,7 +1366,7 @@ fi
1353
1366
 
1354
1367
  **If `TEMPLATE_SOURCE` is `none`**: HARD STOP. Post a PR comment explaining the setup is broken (`gh pr comment $ARGUMENTS --body "..."`) instructing the user to run `npx forgedock update` to repair the install, add `needs-human`, and exit Phase 3 without posting any findings or a `FORGE:REVIEW` verdict. **NEVER perform the review inline in the main agent context as a substitute.** A degraded solo review that presents itself as complete is worse than no review — missing templates must fail loudly, not silently.
1355
1368
 
1356
- **If `TEMPLATE_SOURCE` is `forge_home` or `repo_path`** (the normal cases — behavior unchanged from before this guard existed):
1369
+ **If `TEMPLATE_SOURCE` is `plugin_root`, `forge_home` or `repo_path`** (the normal cases — behavior unchanged from before this guard existed):
1357
1370
  ```
1358
1371
  Read: $TEMPLATE_BASE/protocols.md
1359
1372
  Read: $TEMPLATE_BASE/<persona>.md (one per selected agent from Phase 3B)
@@ -1674,6 +1687,25 @@ If still 0: review is clean — skip to Phase 7.
1674
1687
  - Also dedup by title similarity: if two findings share the same file and 3+ title keywords, keep the higher confidence one
1675
1688
  - Sort: CONFIRMED first, then LIKELY, then POSSIBLE; within group by severity
1676
1689
 
1690
+ ### 6B.5: Non-blocking note disposition (MANDATORY before 6C — forge#3060)
1691
+
1692
+ Filing a standalone `review-finding` issue for every LOW/POSSIBLE reviewer note makes the cascade amplify (each merged fix PR spawns its own P3 issues, which spawn more). Classify each deduped finding before 6C:
1693
+
1694
+ - **NOTE** (does NOT become an issue): `**Severity**: LOW`, OR `**Confidence**: POSSIBLE` with Severity below HIGH.
1695
+ - **ISSUE** (continues to 6C unchanged): everything else — MEDIUM+ severity and CONFIRMED/LIKELY findings above LOW, i.e. P0/P1/P2 behaviour is exactly as before.
1696
+ - **Safety exemption**: a finding is never demoted to a NOTE (it is filed as before) if it came from the Security, Auth, Billing, Concurrency or Database review agent, OR its file path / title / body matches `\b(security|auth|authz|authn|billing|payment|stripe|charge|invoice|injection|xss|csrf|ssrf|idor|secrets?|credentials?|permissions?|sql)\b` (matched case-insensitively on word or underscore-separated parts, so `auth_service` and `billing_handler` match but `author`/`tokenizer` do not; also exempt: `token`, `password`, `redact`). Domain of origin is checked first so a generically titled auth/IDOR/injection finding cannot be dropped.
1697
+ - **Precedence**: the safety exemption above ALWAYS wins over the P3-lineage rule below and over every other NOTE rule. A finding with missing or unparseable severity defaults to ISSUE.
1698
+ - **Stricter rule on P3-lineage PRs** (applies only to findings not covered by the safety exemption): when the PR's linked issue (`MERGE_ISSUE`/`Closes #N`) carries the `review-finding` label and `priority:P3`, only CONFIRMED findings of MEDIUM+ severity, or any finding of HIGH+ severity, become issues. Everything else is a NOTE. A fix for a polish finding must not mint new polish findings.
1699
+
1700
+ Each NOTE gets exactly one disposition, in this preference order:
1701
+ 1. **Fix in this PR** — when the fix is cheap (a few lines, same files already in the diff, no new behaviour) and in scope, apply it as a follow-up commit on the PR branch before merge — ONLY for comment, documentation or test-only changes. Any change to executable code is not a note fix: it must be filed as an issue or fixed with a full re-review of the new HEAD before merge (never push unreviewed code under `--auto-merge`).
1702
+ 2. **List in the PR body** — append the remaining notes under a `## Non-blocking notes` section of the PR body (edit the existing body, never replace it; use a `mktemp` body file named for the PR number and read the body back to confirm the section is present).
1703
+ 3. **Drop** — duplicates, stale-comment/docstring nits, and speculation with no actionable evidence.
1704
+
1705
+ **Metrics note (forge#3106)**: because NOTEs are not filed as issues, `review-finding` issue volume and any metric derived from it (findings per PR, `/pipeline-health` finding rates, amplification ratio) drop relative to pre-#3060 history. Compare against the `notes_*` counts in the review summary, not issue counts alone, when judging review depth across the change.
1706
+
1707
+ Every NOTE disposition must be recorded (never silently dropped): list each NOTE (id, file:line, one-line reason) with its disposition in the review summary comment, including dropped ones, so a reviewer can audit the decision. NOTES are never passed to `Skill(issue)`. Record counts in the review summary (`notes_fixed`, `notes_listed`, `notes_dropped`, `findings_filed`). If no finding is an ISSUE after this step, skip 6C and continue to Phase 7.
1708
+
1677
1709
  ### 6C: Create Issues
1678
1710
 
1679
1711
  ```bash
@@ -1894,7 +1926,7 @@ if [ "$FINDING_PRIORITY_EXIT" -ne 0 ]; then
1894
1926
  else
1895
1927
 
1896
1928
  # --label is repeatable (not comma-joined) per the /issue programmatic contract.
1897
- ISSUE_SKILL_OUTPUT=$(Skill(skill="issue", args="--title \"$FINDING_ISSUE_TITLE\" --body-file \"$FINDING_ISSUE_BODY_FILE\" --label review-finding --label needs-validation --label \"$FINDING_PRIORITY\" ${MILESTONE_FLAG}"))
1929
+ ISSUE_SKILL_OUTPUT=$(Skill(skill="{FORGE_SKILL_PREFIX}issue", args="--title \"$FINDING_ISSUE_TITLE\" --body-file \"$FINDING_ISSUE_BODY_FILE\" --label review-finding --label needs-validation --label \"$FINDING_PRIORITY\" ${MILESTONE_FLAG}"))
1898
1930
  # /issue re-reads the created issue and hard-fails unless this exact marker is present.
1899
1931
  rm -f "$FINDING_ISSUE_BODY_FILE"
1900
1932
 
@@ -2170,6 +2202,87 @@ fi
2170
2202
 
2171
2203
  **Skip if** `AUTO_MERGE=false`.
2172
2204
 
2205
+ **Phase-trail preflight (MANDATORY before any merge attempt)** <!-- Added: forge#3061 -->: a reviewer verdict alone must not merge work whose earlier pipeline phases were skipped. Run the deterministic verifier against the linked issue; do NOT run the merge block below unless it exits 0.
2206
+
2207
+ ```bash
2208
+ if [ -z "${MERGE_ISSUE:-}" ]; then
2209
+ echo "PHASE TRAIL: auto-merge requested without --issue — the issue is never inferred from PR-author-controlled text, so the phase trail cannot be verified; the phase trail; refusing to merge (fail closed)" >&2
2210
+ gh pr comment {PR_NUMBER} {MERGE_GH_FLAG} --body "Auto-merge skipped: no --issue was given, so the phase trail cannot be verified. Re-run \`/review-pr {PR_NUMBER} --auto-merge --issue <N>\` or merge manually." 2>/dev/null || true # allowlist:check-command-side-effects
2211
+ # STOP — return REVIEW_RESULT: status: BLOCKED, blocker: "auto-merge requires --issue". Not a PHASE_TRAIL_FAILED (nothing to re-run).
2212
+ exit 1
2213
+ else
2214
+ # Same resolution as work-on/review.md Phase R1.5: the verifier ships with ForgeDock, not the consumer repo.
2215
+ # FORGE_ROOT bootstrap (canonical; keep byte-identical across specs, guarded by scripts/forge-root.test.sh)
2216
+ FORGE_ROOT=""
2217
+ if [ -n "${FORGEDOCK_HOME:-}" ]; then case "$FORGEDOCK_HOME" in /*) FORGE_ROOT="$FORGEDOCK_HOME" ;; esac; else
2218
+ # Portable to bash 3.2 (macOS), BSD/GNU coreutils and zsh: no mapfile, no sort -V, no bare globs (zsh aborts on no match).
2219
+ _l="$HOME/.claude/commands/work-on.md"; _l="$(readlink -f "$_l" 2>/dev/null || readlink "$_l" 2>/dev/null)"; [ -n "$_l" ] && _l="$(dirname "$(dirname "$_l")")"
2220
+ # newest cached version first: numeric major.minor.patch of the version dir name only; a release outranks its pre-release (1.10.0 > 1.9.0 > 1.9.0-rc1)
2221
+ _v="$(find -L "$HOME/.claude/plugins/cache" -mindepth 3 -maxdepth 3 -type d 2>/dev/null | awk -F/ '$(NF-1)=="forgedock"{v=$NF;p=index(v,"-");r=1;if(p){v=substr(v,1,p-1);r=0};split(v,a,".");printf "%d %d %d %d %s\n",a[1],a[2],a[3],r,$0}' | sort -k1,1nr -k2,2nr -k3,3nr -k4,4nr | cut -d' ' -f5-)"
2222
+ _m="$(find -L "$HOME/.claude/plugins/marketplaces" -mindepth 1 -maxdepth 1 -type d -iname '*forgedock*' 2>/dev/null)"
2223
+ # '${CLAUDE_PLUGIN_ROOT}' is substituted by Claude Code when it loads a plugin spec (the exact spelling only, never as an env var), so a running plugin resolves to its own root first; unsubstituted (other runtimes) it stays a literal that the /* check rejects.
2224
+ _k="$(printf '%s\n' '${CLAUDE_PLUGIN_ROOT}' "${FORGE_HOME:-}" "$_l" "$_v" "$_m")"
2225
+ while IFS= read -r _c; do
2226
+ case "$_c" in /*) [ -z "$FORGE_ROOT" ] && [ -f "$_c/scripts/verify-phase-trail.sh" ] && [ -f "$_c/scripts/lint-dispatch-prompt.sh" ] && [ -f "$_c/scripts/is-docs-only.sh" ] && [ -f "$_c/bin/engine/resolve.mjs" ] && [ -f "$_c/bin/engine/orchestrate-canary.mjs" ] && [ -f "$_c/bin/engine/admission.mjs" ] && FORGE_ROOT="$_c" ;; esac
2227
+ done <<< "$_k"
2228
+ fi
2229
+ TRAIL_SCRIPT="$FORGE_ROOT/scripts/verify-phase-trail.sh"
2230
+ # Docs-only predicate: ONE shared copy (scripts/is-docs-only.sh, forge#3134), same as work-on/review.md R1.5. Fail closed: unresolved script or empty diff -> no flag.
2231
+ DOCS_ONLY_FLAG=""
2232
+ # Both sides of a rename (filename + previous_filename): `gh pr diff --name-only` collapses a rename to its destination (forge#3145).
2233
+ PR_FILES_ALL=$(gh api --paginate "repos/{GH_REPO}/pulls/{PR_NUMBER}/files" --jq '.[] | .filename, (.previous_filename // empty)' 2>/dev/null) || PR_FILES_ALL=""
2234
+ # Fail closed on truncation: the files API caps at 3000 files, so a PR at/over the cap (or an unreadable count) never yields a docs-only flag.
2235
+ PR_CHANGED_N=$(gh api "repos/{GH_REPO}/pulls/{PR_NUMBER}" --jq .changed_files 2>/dev/null) || PR_CHANGED_N=""
2236
+ case "$PR_CHANGED_N" in ''|*[!0-9]*) PR_FILES_ALL="" ;; *) [ "$PR_CHANGED_N" -ge 3000 ] && PR_FILES_ALL="" ;; esac
2237
+ if [ -n "$PR_FILES_ALL" ] && [ -n "$FORGE_ROOT" ] && [ -f "$FORGE_ROOT/scripts/is-docs-only.sh" ] && echo "$PR_FILES_ALL" | bash "$FORGE_ROOT/scripts/is-docs-only.sh"; then DOCS_ONLY_FLAG="--docs-only"; fi
2238
+ # Band cross-check (forge#3149): a non-docs PR diff means an agent-chosen INVESTIGATION band must not waive requirements.
2239
+ # No --head-tree here: review auto-fix commits legitimately advance the head after the gate ran; the tree binding is enforced pre-PR (work-on/review.md R1.5).
2240
+ # Fail closed: anything not positively docs-only (including an empty/unreadable diff) is treated as a code diff.
2241
+ CODE_DIFF_FLAG="--code-diff"
2242
+ if [ -n "$DOCS_ONLY_FLAG" ]; then CODE_DIFF_FLAG=""; fi
2243
+ if [ -z "$FORGE_ROOT" ] || [ ! -f "$TRAIL_SCRIPT" ]; then
2244
+ # Fail closed: an unresolvable verifier is NOT a pass (plugin installs set no FORGE_HOME; never fall back to the consumer repo).
2245
+ echo "PHASE TRAIL: verify-phase-trail.sh not resolvable (set FORGEDOCK_HOME to the ForgeDock install) — refusing to merge" >&2
2246
+ TRAIL="PHASE_TRAIL: ERROR (verifier not resolvable)"; TRAIL_RC=127
2247
+ else
2248
+ TRAIL=$(bash "$TRAIL_SCRIPT" "$MERGE_ISSUE" -R {GH_REPO} $DOCS_ONLY_FLAG $CODE_DIFF_FLAG); TRAIL_RC=$?
2249
+ fi
2250
+ if [ "$TRAIL_RC" -ge 2 ]; then
2251
+ # rc 2 (trail unreadable) / 127 (verifier not resolvable) is an infrastructure failure, NOT missing phases:
2252
+ # there are no MISSING lines to re-run, so never report PHASE_TRAIL_FAILED here (same routing as work-on/review.md R1.5, forge#3147).
2253
+ TRAIL_ERR_BODY="<!-- FORGE:PHASE_TRAIL_ERROR -->
2254
+ Auto-merge refused for PR #{PR_NUMBER}: the phase-trail verifier could not run (rc=${TRAIL_RC}). This is an infrastructure problem, not a missing phase. Do not re-run phases.
2255
+
2256
+ \`\`\`
2257
+ ${TRAIL}
2258
+ \`\`\`
2259
+
2260
+ Fix the cause (gh auth/API outage, \`FORGE_TRAIL_QG_SINCE\`/runner clock, or set \`FORGEDOCK_HOME\` when the verifier is unresolvable), then re-run /review-pr."
2261
+ gh issue comment "$MERGE_ISSUE" {MERGE_GH_FLAG} --body "$TRAIL_ERR_BODY" # <!-- allowlist:check-command-side-effects -->
2262
+ # Nothing can resume this automatically, so park it for a human instead of leaving workflow:in-review (standalone /review-pr and remediate callers too).
2263
+ gh issue edit "$MERGE_ISSUE" {MERGE_GH_FLAG} --add-label "needs-human" 2>/dev/null || true # <!-- allowlist:check-command-side-effects -->
2264
+ # STOP — return REVIEW_RESULT: status: BLOCKED, blocker: "phase trail unreadable (rc=${TRAIL_RC})". The PR stays open and unmerged; callers must NOT fall back to a manual merge.
2265
+ exit 1
2266
+ elif [ "$TRAIL_RC" -ne 0 ]; then
2267
+ TRAIL_FAIL_BODY="<!-- FORGE:PHASE_TRAIL_FAILED -->
2268
+ Auto-merge refused for PR #{PR_NUMBER}: the issue's phase trail is incomplete.
2269
+
2270
+ \`\`\`
2271
+ ${TRAIL}
2272
+ \`\`\`
2273
+
2274
+ Re-run each missing phase via its Skill (see the \`->\` action on each MISSING line), then re-run /review-pr."
2275
+ gh issue comment "$MERGE_ISSUE" {MERGE_GH_FLAG} --body "$TRAIL_FAIL_BODY" # <!-- allowlist:check-command-side-effects -->
2276
+ # STOP — return REVIEW_RESULT: status: PHASE_TRAIL_FAILED with the MISSING lines (rc 1 only; rc >= 2 is handled above as BLOCKED).
2277
+ # The /work-on router (work-on/review.md Phase R4), the work-on.md router (Phase 4 → work-on/review.md) and remediate.md Phase M6 consume this status and re-dispatch the named phases once (forge#3102); this is NOT an immediate needs-human escalation.
2278
+ exit 1
2279
+ fi
2280
+ fi
2281
+ [ "${TRAIL_RC:-1}" -eq 0 ] || exit 1 # hard guard: nothing below runs unless the trail verified
2282
+ ```
2283
+
2284
+ If the preflight failed, skip the rest of Phase 8. On exit code 1, return `REVIEW_RESULT: status: PHASE_TRAIL_FAILED` listing the missing markers. On exit code ≥2 (unreadable trail, or 127 when the verifier is unresolvable), return `REVIEW_RESULT: status: BLOCKED`, blocker: "phase trail unreadable (rc=N)". This is an infrastructure failure with no MISSING lines, so callers neither re-run phases nor merge manually. (`DOCS_ONLY_FLAG` is computed in the block above: `--docs-only` when `scripts/is-docs-only.sh` accepts the PR diff, fed both sides of every rename: every file is an allowlisted `*.md` (`docs/**` or a root README/CHANGELOG/CONTRIBUTING/SECURITY/GOVERNANCE), outside the instruction directories (`commands/`, `devdocs/`, `templates/`, `skills/`, `agents/`, `hooks/`, `.claude/`, `.claude-plugin/`, `.agents/`, `.codex/`, `.cursor/`, `.github/`, `.opencode/`, `.gemini/`, `.kiro/`) at any depth, and not named `AGENTS.md`/`CLAUDE.md`/`SKILL.md`/`GEMINI.md` (or a dotted variant).)
2285
+
2173
2286
  ```bash
2174
2287
  # §7B verdict + purpose-regression + calibration + trust-escalation guard — check before any merge attempt <!-- Added: forge#1601, forge#1741, forge#1745 -->
2175
2288
  # HARD RULE 3 requires that VERDICT=CHANGES REQUESTED, HAS_PURPOSE_REGRESSION=true,
@@ -2250,21 +2363,76 @@ if [ "$PREVIOUSLY_ESCALATED" = "true" ]; then
2250
2363
  # `main`/deploy-gate base, or to remediate.md Phase M7's base-scoped auto-land bar for a
2251
2364
  # non-`main` base (forge#2570). Either way this guard only parks at workflow:awaiting-merge.
2252
2365
  else
2366
+ # CI gate (MANDATORY before any autonomous merge): merge only when every check on the PR is
2367
+ # green. Field test: PRs merged to staging with checks pending or red (#3165), because branch
2368
+ # protection required none and an auto-merge waits only for *required* checks.
2369
+ # Shell state does not persist between Bash calls: everything this block needs is resolved here.
2370
+ _l="$(readlink -f "$HOME/.claude/commands/work-on.md" 2>/dev/null || true)"; _l="${_l%/commands/work-on.md}"
2371
+ CI_GATE_SCRIPT=""; DOCS_ONLY_SCRIPT=""
2372
+ for _c in '${CLAUDE_PLUGIN_ROOT}' "${FORGE_ROOT:-}" "${FORGEDOCK_HOME:-}" "${FORGE_HOME:-}" "$_l"; do
2373
+ case "$_c" in /*)
2374
+ [ -z "$CI_GATE_SCRIPT" ] && [ -f "$_c/scripts/wait-ci-green.sh" ] && CI_GATE_SCRIPT="$_c/scripts/wait-ci-green.sh"
2375
+ [ -z "$DOCS_ONLY_SCRIPT" ] && [ -f "$_c/scripts/is-docs-only.sh" ] && DOCS_ONLY_SCRIPT="$_c/scripts/is-docs-only.sh" ;;
2376
+ esac
2377
+ done
2378
+ if [ -n "$CI_GATE_SCRIPT" ]; then CI_GATE_OUT=$(bash "$CI_GATE_SCRIPT" {PR_NUMBER} {MERGE_GH_FLAG}); CI_GATE_RC=$?
2379
+ else CI_GATE_OUT="CI_GATE: ERROR — scripts/wait-ci-green.sh not resolvable (fail closed)"; CI_GATE_RC=2; fi
2380
+ echo "$CI_GATE_OUT"
2381
+ GATED_HEAD=$(printf '%s\n' "$CI_GATE_OUT" | sed -n 's/^CI_GATE_HEAD: //p' | head -1)
2382
+ # rc 2 "head moved" is a stale review, not a human problem; rc 3 TIMEOUT means CI is still running.
2383
+ printf '%s\n' "$CI_GATE_OUT" | grep -q 'PR head moved during the wait' && CI_GATE_RC=4
2384
+ if [ "$CI_GATE_RC" -eq 3 ]; then
2385
+ echo "CI_GATE_TIMEOUT: CI still running — re-run this whole block (up to 3 more times) before treating it as a failure."
2386
+ fi
2387
+ # Reviewed-head guard: merge only the exact commit this review approved. A commit pushed after
2388
+ # the verdict (field test: post-review "fix" commits merged unreviewed on #3156 and #3158) needs a
2389
+ # fresh full review, not a merge. Checked after the CI wait so a push during the wait is caught.
2390
+ # A docs-only delta (this review's own §6B.5 note fixes) is allowed; any other change is stale.
2391
+ # REVIEW_SHA is re-derived from this review's FORGE:REVIEW_ROUTE marker (sha=<7 chars>), never
2392
+ # assumed from an earlier shell.
2393
+ REVIEWED_SHORT=$(gh api "repos/{GH_REPO}/issues/{PR_NUMBER}/comments" --paginate --jq '.[] | select(.body | contains("FORGE:REVIEW_ROUTE")) | .body' 2>/dev/null \
2394
+ | sed -n 's/.*FORGE:REVIEW_ROUTE[^>]* sha=\([0-9a-f]\{7,\}\).*/\1/p' | tail -1)
2395
+ if [ "$CI_GATE_RC" -eq 0 ]; then
2396
+ if [ -z "$REVIEWED_SHORT" ] || [ -z "$GATED_HEAD" ] || [ "$GATED_HEAD" = "unknown" ]; then
2397
+ CI_GATE_RC=4; CI_GATE_OUT="STALE_REVIEW: cannot bind the merge to a reviewed commit (route sha='${REVIEWED_SHORT}', gated head='${GATED_HEAD}')"
2398
+ elif [ "${GATED_HEAD#"$REVIEWED_SHORT"}" = "$GATED_HEAD" ]; then
2399
+ REVIEW_BASE_SHA=$(gh api "repos/{GH_REPO}/commits/${REVIEWED_SHORT}" --jq '.sha' 2>/dev/null)
2400
+ POST_REVIEW_FILES=$(gh api "repos/{GH_REPO}/compare/${REVIEW_BASE_SHA:-$REVIEWED_SHORT}...${GATED_HEAD}" --jq '.files[] | .filename, (.previous_filename // empty)' 2>/dev/null)
2401
+ if [ -n "$POST_REVIEW_FILES" ] && [ -n "$DOCS_ONLY_SCRIPT" ] && printf '%s\n' "$POST_REVIEW_FILES" | bash "$DOCS_ONLY_SCRIPT"; then
2402
+ echo "Post-review commits are docs-only (note fixes) — reviewed code unchanged."
2403
+ else
2404
+ CI_GATE_RC=4; CI_GATE_OUT="STALE_REVIEW: PR head ${GATED_HEAD:0:7} is not the reviewed commit ${REVIEWED_SHORT} and the delta is not docs-only"
2405
+ fi
2406
+ fi
2407
+ fi
2408
+ if [ "$CI_GATE_RC" -eq 4 ]; then
2409
+ # Not a human problem: the caller re-runs /review-pr on the new head (work-on/review.md R4).
2410
+ echo "REVIEW_RESULT: status: BLOCKED, blocker: stale review — ${CI_GATE_OUT}; re-run /review-pr on the new head"
2411
+ elif [ "$CI_GATE_RC" -ne 0 ]; then
2412
+ CI_MSG="⛔ Auto-merge refused for PR #{PR_NUMBER}: CI is not green (ci gate rc=${CI_GATE_RC}). The PR stays open; fix the failing checks on its branch (remediation treats this as FIXABLE) or re-run flaky ones.
2413
+ \`\`\`
2414
+ ${CI_GATE_OUT}
2415
+ \`\`\`"
2416
+ gh issue comment {MERGE_ISSUE} {MERGE_GH_FLAG} --body "$CI_MSG" 2>/dev/null || true # allowlist:check-command-side-effects
2417
+ gh issue edit {MERGE_ISSUE} {MERGE_GH_FLAG} --add-label "needs-human" 2>/dev/null || true # allowlist:check-command-side-effects
2418
+ echo "REVIEW_RESULT: status: BLOCKED, blocker: ci gate not green (rc=${CI_GATE_RC})"
2419
+ else
2253
2420
  # Checkpoint comment on issue
2254
- gh issue comment {MERGE_ISSUE} {MERGE_GH_FLAG} --body "Review complete for PR #{PR_NUMBER}. Verdict: ${VERDICT:-APPROVED}. Proceeding to merge."
2421
+ gh issue comment {MERGE_ISSUE} {MERGE_GH_FLAG} --body "Review complete for PR #{PR_NUMBER}. Verdict: ${VERDICT:-APPROVED}. CI green. Proceeding to merge."
2255
2422
 
2256
- # Merge
2257
- gh pr merge {PR_NUMBER} {MERGE_GH_FLAG} --merge
2423
+ # Merge exactly the commit the CI gate (and the reviewed-head guard) checked.
2424
+ gh pr merge {PR_NUMBER} {MERGE_GH_FLAG} --merge --match-head-commit "$GATED_HEAD"
2258
2425
 
2259
2426
  # Verify
2260
2427
  MERGE_STATE=$(gh pr view {PR_NUMBER} {MERGE_GH_FLAG} --json state --jq '.state')
2261
2428
  [ "$MERGE_STATE" != "MERGED" ] && gh issue comment {MERGE_ISSUE} {MERGE_GH_FLAG} --body "PR #{PR_NUMBER} merge failed. State: $MERGE_STATE."
2429
+ fi
2262
2430
  fi
2263
2431
  fi
2264
2432
  fi
2265
2433
  ```
2266
2434
 
2267
- **Important**: Phase 8 ONLY merges the PR. It does NOT close the issue, update labels, or clean up worktrees. When invoked via `/work-on`, those responsibilities belong to `work-on/close.md` (work-on.md Phase 6 — triggered when PR is merged and issue is still open). Doing them here would cause the issue to be closed with `workflow:merged` before Phase 6 runs, skipping the close phase entirely.
2435
+ **Important**: Phase 8 ONLY merges the PR. It does NOT close the issue, update labels, or clean up worktrees. When invoked via `/work-on`, those responsibilities belong to `work-on/close.md` (the router's Phase 5 — triggered when the PR is merged and the issue is still open). Doing them here would cause the issue to be closed with `workflow:merged` before Phase 6 runs, skipping the close phase entirely.
2268
2436
 
2269
2437
  ### 8B: Post-Merge Review Finding Demilestoning (Milestone PRs Only)
2270
2438
 
@@ -8,6 +8,8 @@ install: extras
8
8
 
9
9
  # /rollback — Automated Revert PR Creation
10
10
 
11
+ > **Skill names**: `{FORGE_SKILL_PREFIX}` is `forgedock:` (plugin install) or empty (`install.sh`), resolved once per run by `commands/work-on.md` § Skill Name Resolution. If the skill is not found under either name, STOP and report "skill not found" — never run the phase inline.
12
+
11
13
  **Input**: $ARGUMENTS
12
14
 
13
15
  **Config variables used by this command** (set in `forge.yaml`):
@@ -252,7 +254,7 @@ Regression of #{ORIGINAL_ISSUE}. Should investigate why the original approach ca
252
254
  BODY_EOF
253
255
  # Route through the /issue create-hook (canonical dedup + body validation) instead of a raw
254
256
  # `gh issue create`.
255
- Skill(skill="issue", args="--title \"$FOLLOWUP_TITLE\" --body-file \"$FOLLOWUP_BODY_TMPFILE\" --label bug --label \"priority:P1\"")
257
+ Skill(skill="{FORGE_SKILL_PREFIX}issue", args="--title \"$FOLLOWUP_TITLE\" --body-file \"$FOLLOWUP_BODY_TMPFILE\" --label bug --label \"priority:P1\"")
256
258
  rm -f "$FOLLOWUP_BODY_TMPFILE"
257
259
  trap - EXIT
258
260
  FOLLOWUP_ISSUE_NUMBER=$(gh issue list \
@@ -8,6 +8,8 @@ install: extras
8
8
 
9
9
  # /security-audit — Periodic Security Posture Audit
10
10
 
11
+ > **Skill names**: `{FORGE_SKILL_PREFIX}` is `forgedock:` (plugin install) or empty (`install.sh`), resolved once per run by `commands/work-on.md` § Skill Name Resolution. If the skill is not found under either name, STOP and report "skill not found" — never run the phase inline.
12
+
11
13
  **Input**: $ARGUMENTS
12
14
 
13
15
  You are the security posture auditor. Run a scripted 4-phase checklist against the **current state** of the target repository (not a PR diff). Each check uses bash/grep against repo files. Confirmed findings are posted as GitHub issues in the target repo, labeled `security,audit-finding,priority:P1/priority:P2/priority:P3`.
@@ -553,7 +555,7 @@ ISSUE_EOF
553
555
  ```
554
556
 
555
557
  ```
556
- Skill(skill="issue", args="--title \"{ISSUE_TITLE}\" --body-file \"$SECURITY_BODY_FILE\" --label \"security\" --label \"audit-finding\" --label \"{SEVERITY}\"")
558
+ Skill(skill="{FORGE_SKILL_PREFIX}issue", args="--title \"{ISSUE_TITLE}\" --body-file \"$SECURITY_BODY_FILE\" --label \"security\" --label \"audit-finding\" --label \"{SEVERITY}\"")
557
559
  ```
558
560
 
559
561
  `security`, `audit-finding`, and `{SEVERITY}` are each passed as their own `--label` flag — `/issue`'s programmatic mode does not comma-split a single `--label` value. Note: `/issue` resolves the target repo from `forge.yaml → project.owner/repo` (or the satellite-prefix table), not from a caller-supplied `-R`/`--repo` flag — this is correct for the common case where `{GH_REPO}` matches `forge.yaml`'s project repo.
@@ -8,6 +8,8 @@ install: extras
8
8
 
9
9
  # /signal-planner — Closed-Loop Production Signal to Verified Resolution
10
10
 
11
+ > **Skill names**: `{FORGE_SKILL_PREFIX}` is `forgedock:` (plugin install) or empty (`install.sh`), resolved once per run by `commands/work-on.md` § Skill Name Resolution. If the skill is not found under either name, STOP and report "skill not found" — never run the phase inline.
12
+
11
13
  **Input**: $ARGUMENTS
12
14
 
13
15
  **Config variables used by this command** (set in `forge.yaml`):
@@ -17,7 +19,7 @@ install: extras
17
19
  - `{GOVERNOR_MAX_COST_USD}` ← `governor.max_cost_usd` (optional, default `5.00`) — hard cap on estimated LLM spend per run
18
20
 
19
21
  **NEVER use plan mode (EnterPlanMode).**
20
- **NEVER use the Agent tool** for implementation work — dispatch issues via `Skill(skill="work-on", ...)` or via `/orchestrate`.
22
+ **NEVER use the Agent tool** for implementation work — dispatch issues via `Skill(skill="{FORGE_SKILL_PREFIX}work-on", ...)` or via `/orchestrate`.
21
23
 
22
24
  <!-- FORGE:SPEC_LOADED — signal-planner.md loaded and active. Agent is bound by this spec. -->
23
25
 
@@ -251,7 +253,7 @@ TRACKER_EOF
251
253
  # `gh issue create`. This body uses "## Acceptance" rather than the mandatory "## Acceptance
252
254
  # Criteria"/"## Affected Files"/"## Problem" sections — /issue's Phase 3F will non-blockingly
253
255
  # append placeholder mandatory sections; this is expected for a coordination tracker issue.
254
- Skill(skill="issue", args="--title \"$TRACKER_TITLE\" --body-file \"$TRACKER_BODY_TMPFILE\" --label signal-planner --label \"priority:{SIGNAL.severity}\"")
256
+ Skill(skill="{FORGE_SKILL_PREFIX}issue", args="--title \"$TRACKER_TITLE\" --body-file \"$TRACKER_BODY_TMPFILE\" --label signal-planner --label \"priority:{SIGNAL.severity}\"")
255
257
  rm -f "$TRACKER_BODY_TMPFILE"
256
258
  trap - EXIT
257
259
  TRACKER_ISSUE=$(gh issue list -R {GH_REPO} \
@@ -354,7 +356,7 @@ Signal context: {relevant subset of SIGNAL.context for this work unit}
354
356
  ISSUE_EOF
355
357
  # Route through the /issue create-hook (canonical dedup + body validation) instead of a raw
356
358
  # `gh issue create`.
357
- Skill(skill="issue", args="--title \"$WORK_UNIT_TITLE\" --body-file \"$WORK_UNIT_BODY_TMPFILE\" --label \"priority:{work_unit.priority}\" --label \"{work_unit.type}\"")
359
+ Skill(skill="{FORGE_SKILL_PREFIX}issue", args="--title \"$WORK_UNIT_TITLE\" --body-file \"$WORK_UNIT_BODY_TMPFILE\" --label \"priority:{work_unit.priority}\" --label \"{work_unit.type}\"")
358
360
  rm -f "$WORK_UNIT_BODY_TMPFILE"
359
361
  trap - EXIT
360
362
  ISSUE_NUM=$(gh issue list -R {GH_REPO} \
@@ -412,7 +414,7 @@ Wait for user response. If they adjust (e.g., drop an issue, change priority), u
412
414
  Pass `DAG_ISSUES` as an ordered issue set to `/orchestrate`:
413
415
 
414
416
  ```
415
- Skill(skill: "orchestrate", args: "{DAG_ISSUES joined by space — e.g. '42 43 45'}")
417
+ Skill(skill: "{FORGE_SKILL_PREFIX}orchestrate", args: "{DAG_ISSUES joined by space — e.g. '42 43 45'}")
416
418
  ```
417
419
 
418
420
  `/orchestrate` handles parallel execution where dependencies allow — signal-planner does not re-implement execution logic.
@@ -8,6 +8,8 @@ install: extras
8
8
 
9
9
  # /sync-ecosystem — Cross-Project Sync & Publish
10
10
 
11
+ > **Skill names**: `{FORGE_SKILL_PREFIX}` is `forgedock:` (plugin install) or empty (`install.sh`), resolved once per run by `commands/work-on.md` § Skill Name Resolution. If the skill is not found under either name, STOP and report "skill not found" — never run the phase inline.
12
+
11
13
  **Input**: $ARGUMENTS
12
14
 
13
15
  When the `{project.name}` API surface changes (new parameters, endpoints, modes, pricing), satellite projects need updates. This command detects what changed, creates tracking issues, checks publication status, and publishes releases.
@@ -263,7 +265,7 @@ BODY_EOF
263
265
  # the satellite repo ({TARGET_REPO}) from forge.yaml → repos.satellites, matching how a human
264
266
  # would invoke /issue for the same satellite (e.g. "mcp: add list_schemas tool"). The prefix
265
267
  # is stripped by /issue's own routing — it does not become part of the created issue title.
266
- Skill(skill="issue", args="\"{COMPONENT}: $SYNC_TITLE\" --title \"$SYNC_TITLE\" --body-file \"$SYNC_BODY_TMPFILE\" --label feature --label \"priority:P2\"")
268
+ Skill(skill="{FORGE_SKILL_PREFIX}issue", args="\"{COMPONENT}: $SYNC_TITLE\" --title \"$SYNC_TITLE\" --body-file \"$SYNC_BODY_TMPFILE\" --label feature --label \"priority:P2\"")
267
269
  rm -f "$SYNC_BODY_TMPFILE"
268
270
  trap - EXIT
269
271
  SYNC_ISSUE_NUMBER=$(gh issue list -R {TARGET_REPO} \
@@ -321,7 +323,7 @@ Files that need changes:
321
323
  BODY_EOF
322
324
  # Route through the /issue create-hook (canonical dedup + body validation) instead of a raw
323
325
  # `gh issue create`. No prefix needed — this targets the default/monorepo repo.
324
- Skill(skill="issue", args="--title \"$MONOREPO_SYNC_TITLE\" --body-file \"$MONOREPO_SYNC_BODY_TMPFILE\" --label feature --label \"priority:P2\"")
326
+ Skill(skill="{FORGE_SKILL_PREFIX}issue", args="--title \"$MONOREPO_SYNC_TITLE\" --body-file \"$MONOREPO_SYNC_BODY_TMPFILE\" --label feature --label \"priority:P2\"")
325
327
  rm -f "$MONOREPO_SYNC_BODY_TMPFILE"
326
328
  trap - EXIT
327
329
  MONOREPO_SYNC_ISSUE_NUMBER=$(gh issue list \
@@ -9,6 +9,8 @@ install: extras
9
9
 
10
10
  # /test-gate — Deterministic Deploy-Gate Testing
11
11
 
12
+ > **Skill names**: `{FORGE_SKILL_PREFIX}` is `forgedock:` (plugin install) or empty (`install.sh`), resolved once per run by `commands/work-on.md` § Skill Name Resolution. If the skill is not found under either name, STOP and report "skill not found" — never run the phase inline.
13
+
12
14
  **Input**: `$ARGUMENTS` — optional `--prs "<space-separated PR numbers>"` and `--base <branch>`.
13
15
 
14
16
  Verifies a staging→main bundle's acceptance criteria against running code before deploy. Called by `/review-pr-staging` (Phase 6.5) with the bundle PRs, or run standalone when `--prs` is absent (computes the bundle itself). Returns a machine-readable BLOCK / PASS / SKIP verdict for the caller to consume.
@@ -579,7 +581,7 @@ ISSUE_EOF
579
581
 
580
582
  # Route through the /issue create-hook's programmatic invocation contract (see
581
583
  # commands/issue.md § "Programmatic Invocation Contract") instead of the raw issue-creation call.
582
- Skill(skill="issue", args="--title \"$TESTGATE_FAIL_TITLE\" --body-file \"$TESTGATE_FAIL_BODY_FILE\" --label test-failure --label priority:P1")
584
+ Skill(skill="{FORGE_SKILL_PREFIX}issue", args="--title \"$TESTGATE_FAIL_TITLE\" --body-file \"$TESTGATE_FAIL_BODY_FILE\" --label test-failure --label priority:P1")
583
585
  rm -f "$TESTGATE_FAIL_BODY_FILE"
584
586
 
585
587
  # /issue has no machine-readable return contract — resolve the created issue's number by
@@ -849,7 +851,7 @@ TGAP_EOF
849
851
 
850
852
  # Route through the /issue create-hook's programmatic invocation contract (see
851
853
  # commands/issue.md § "Programmatic Invocation Contract") instead of the raw issue-creation call.
852
- Skill(skill="issue", args="--title \"$TESTGAP_TITLE\" --body-file \"$TESTGAP_BODY_FILE\" --label test-gap")
854
+ Skill(skill="{FORGE_SKILL_PREFIX}issue", args="--title \"$TESTGAP_TITLE\" --body-file \"$TESTGAP_BODY_FILE\" --label test-gap")
853
855
  rm -f "$TESTGAP_BODY_FILE"
854
856
 
855
857
  # /issue has no machine-readable return contract — resolve the created issue's number by
@@ -7,6 +7,8 @@ argument-hint: "[--dry-run | --ecosystem npm|pip|cargo | --allow-major | --limit
7
7
 
8
8
  # /upgrade-deps — Autonomous Dependency Upgrade Pipeline
9
9
 
10
+ > **Skill names**: `{FORGE_SKILL_PREFIX}` is `forgedock:` (plugin install) or empty (`install.sh`), resolved once per run by `commands/work-on.md` § Skill Name Resolution. If the skill is not found under either name, STOP and report "skill not found" — never run the phase inline.
11
+
10
12
  **Input**: $ARGUMENTS
11
13
 
12
14
  You are a dependency upgrade agent. Your job is to detect outdated packages, triage them by semver risk level, create GitHub issues for eligible upgrades, and run them through the full `/work-on` pipeline — so each upgrade gets investigation, compatibility verification, quality gate, review, and merge — rather than a blind version bump PR.
@@ -380,7 +382,7 @@ For each issue in `CREATED_ISSUES`, invoke `/work-on` via the Skill tool:
380
382
  if [ "$DRY_RUN" = "true" ]; then
381
383
  echo "[DRY RUN] Would invoke: Skill(skill: \"work-on\", args: \"{ISSUE_NUMBER}\")"
382
384
  else
383
- Skill(skill: "work-on", args: "{ISSUE_NUMBER}")
385
+ Skill(skill: "{FORGE_SKILL_PREFIX}work-on", args: "{ISSUE_NUMBER}")
384
386
  fi
385
387
  ```
386
388
 
@@ -8,6 +8,8 @@ install: extras
8
8
 
9
9
  # /validate — Independent Issue Verification
10
10
 
11
+ > **Skill names**: `{FORGE_SKILL_PREFIX}` is `forgedock:` (plugin install) or empty (`install.sh`), resolved once per run by `commands/work-on.md` § Skill Name Resolution. If the skill is not found under either name, STOP and report "skill not found" — never run the phase inline.
12
+
11
13
  **Input**: $ARGUMENTS
12
14
 
13
15
  Verify whether a reported issue is real before anyone writes code. This command exists for reports that come from outside the pipeline — user complaints, DevOps alerts, monitoring triggers, gut feelings. It is the checkpoint before creating a GitHub issue.
@@ -133,7 +135,7 @@ BODY_EOF
133
135
 
134
136
  # [priority] and [bug|enhancement] are two separate labels — passed as repeated --label flags,
135
137
  # never comma-joined (the /issue programmatic contract's --label is repeatable, not CSV).
136
- Skill(skill="issue", args="--title \"$VALIDATE_ISSUE_TITLE\" --body-file \"$VALIDATE_ISSUE_BODY_FILE\" --label \"[priority]\" --label \"[bug|enhancement]\"")
138
+ Skill(skill="{FORGE_SKILL_PREFIX}issue", args="--title \"$VALIDATE_ISSUE_TITLE\" --body-file \"$VALIDATE_ISSUE_BODY_FILE\" --label \"[priority]\" --label \"[bug|enhancement]\"")
137
139
  rm -f "$VALIDATE_ISSUE_BODY_FILE"
138
140
  ```
139
141