forgedock 1.1.11 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +1 -1
  2. package/bin/cli-spawn-shared.mjs +25 -0
  3. package/bin/engine-cli.mjs +22 -1
  4. package/bin/engine.mjs +46 -4
  5. package/bin/forgedock.mjs +87 -16
  6. package/bin/hooks/pre-tool-use.mjs +331 -10
  7. package/bin/init-enrich-api.mjs +12 -4
  8. package/bin/init-enrich-cli.mjs +328 -0
  9. package/bin/init-enrich.mjs +137 -0
  10. package/bin/journey.mjs +13 -4
  11. package/bin/runner.mjs +653 -10
  12. package/commands/adopt.md +1 -1
  13. package/commands/analytics.md +10 -7
  14. package/commands/audit-agents.md +29 -6
  15. package/commands/audit.md +9 -20
  16. package/commands/autopilot.md +52 -78
  17. package/commands/changelog.md +1 -1
  18. package/commands/ci-audit.md +19 -10
  19. package/commands/cleanup.md +1 -1
  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/design-bench.md +1 -1
  24. package/commands/design-render-critique-loop.md +1 -1
  25. package/commands/diagnose.md +1 -1
  26. package/commands/forge-stats.md +1 -1
  27. package/commands/forgedock-init.md +1 -1
  28. package/commands/geo-audit.md +9 -6
  29. package/commands/incident-response.md +15 -5
  30. package/commands/issue.md +298 -15
  31. package/commands/milestone.md +45 -18
  32. package/commands/optimize.md +1 -1
  33. package/commands/orchestrate/phase-1-resolve.md +20 -5
  34. package/commands/orchestrate/phase-4-execution.md +6 -1
  35. package/commands/orchestrate/phase-5-cleanup.md +43 -2
  36. package/commands/orchestrate/phase-6-report.md +1 -0
  37. package/commands/orchestrate.md +1 -1
  38. package/commands/pipeline-health/phase-4-proposals.md +40 -10
  39. package/commands/pipeline-health/phase-5-report.md +140 -13
  40. package/commands/pipeline-health.md +1 -1
  41. package/commands/pipeline-resume.md +1 -1
  42. package/commands/pipeline-status.md +1 -1
  43. package/commands/qa-sweep.md +10 -4
  44. package/commands/recover-orphans.md +1 -1
  45. package/commands/review-pr-agents/protocols.md +4 -0
  46. package/commands/review-pr-agents.md +7 -4
  47. package/commands/review-pr-staging.md +44 -15
  48. package/commands/review-pr.md +73 -28
  49. package/commands/rollback.md +15 -5
  50. package/commands/security-audit.md +14 -11
  51. package/commands/signal-planner.md +32 -12
  52. package/commands/spec-doctor.md +1 -1
  53. package/commands/sync-ecosystem.md +54 -11
  54. package/commands/test-gate.md +49 -11
  55. package/commands/validate.md +16 -5
  56. package/commands/work-on/build/architect.md +1 -1
  57. package/commands/work-on/build/context.md +1 -1
  58. package/commands/work-on/build/implement.md +2 -2
  59. package/commands/work-on/build/validate.md +3 -1
  60. package/commands/work-on/build.md +1 -1
  61. package/commands/work-on/close.md +1 -1
  62. package/commands/work-on/decompose.md +27 -24
  63. package/commands/work-on/investigate.md +2 -2
  64. package/commands/work-on/remediate.md +1 -1
  65. package/commands/work-on/review.md +5 -3
  66. package/commands/work-on-monolithic.md +1 -1
  67. package/commands/work-on.md +28 -7
  68. package/docs/CI.md +18 -0
  69. package/docs/CONFIG.md +5 -2
  70. package/docs/site/annotations-explained.md +2 -2
  71. package/docs/site/command-reference.md +21 -0
  72. package/docs/spec/review-protocol.md +6 -0
  73. package/package.json +1 -1
@@ -300,6 +300,7 @@ current session's live wake pick them up automatically if it is still running).
300
300
  - Worktrees removed: {N}
301
301
  - Branches pruned: {N}
302
302
  - Milestones closed: {N}
303
+ - Coordination board: {IF `COORD_ISSUE_NUMBER` was set this run: "closed — #{COORD_ISSUE_NUMBER}" ELSE: "n/a (no claims board this batch)"} <!-- Added: forge#2072 -->
303
304
 
304
305
  ### Agent Efficiency (from /audit-agents)
305
306
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Orchestrate parallel work on multiple issues or an entire milestone — spawns sub-agents that each run the full /work-on pipeline
3
- argument-hint: [milestone <slug> | #1 #2 #3 | next <N> | fast-lane | priority:P0]
3
+ argument-hint: "[milestone <slug> | #1 #2 #3 | next <N> | fast-lane | priority:P0]"
4
4
  ---
5
5
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
6
6
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
@@ -38,6 +38,19 @@ while IFS= read -r line; do
38
38
  [ -z "$SLUG" ] && continue
39
39
  [ "$COUNT" -lt 3 ] && continue
40
40
 
41
+ # SECURITY: $SLUG is sourced from a <!-- FORGE:PATTERN: ([^\s>]+) --> tag inside
42
+ # a review-finding issue body — untrusted, externally-influenced text. It is
43
+ # interpolated below into a Skill(skill="issue", args="...") string, and
44
+ # commands/issue.md's Phase 1A resolves that string via `eval "set -- $ARGUMENTS"`.
45
+ # Any shell metacharacter (quote, backtick, $, ;) in $SLUG would break out of the
46
+ # intended --title token and be eval'd as shell. Whitelist-validate at the
47
+ # extraction point — the only legitimate values are check-registry-safe
48
+ # identifiers (also used to build a filename: scripts/check-registry/${SLUG}.sh).
49
+ if ! echo "$SLUG" | grep -qE '^[A-Za-z0-9_-]+$'; then
50
+ echo "WARNING: pattern slug '$SLUG' contains disallowed characters — skipping check-promotion for this pattern (unsafe for interpolation into a downstream eval sink)."
51
+ continue
52
+ fi
53
+
41
54
  # Check if a promotion issue already exists for this slug (avoid duplicates)
42
55
  EXISTING_PROMOTION=$(gh issue list {GH_FLAG} \
43
56
  --label "check-promotion" \
@@ -57,10 +70,13 @@ while IFS= read -r line; do
57
70
  "#\(.number)"
58
71
  ' 2>/dev/null | head -10 | tr '\n' ' ')
59
72
 
60
- gh issue create {GH_FLAG} \
61
- --title "feat(quality-gate): promote pattern '$SLUG' to deterministic registry check (recurred ${COUNT}x)" \
62
- --label "check-promotion,priority:P2" \
63
- --body "$(cat <<CHECK_EOF
73
+ # Route through the /issue programmatic create-hook (#2085) instead of a raw
74
+ # `gh issue create` — gets Phase 2D dedup + Phase 3F body validation for free.
75
+ # The hook's programmatic mode requires the body via --body-file, and expects
76
+ # one repeatable --label flag per label (a single comma-joined --label value
77
+ # would be treated as one malformed label name, not two labels).
78
+ CHECK_BODY_FILE=$(mktemp)
79
+ cat > "$CHECK_BODY_FILE" <<CHECK_EOF
64
80
  ## Problem
65
81
 
66
82
  The review-finding pattern \`$SLUG\` has recurred ${COUNT} times in the analysis window ($(date -u +%Y-%m-%d) window). Each recurrence costs a full investigate→build→review→merge cycle. A static check catches this in milliseconds.
@@ -91,7 +107,9 @@ ${SOURCE_FINDINGS}
91
107
 
92
108
  <!-- AUTO-CREATED: pipeline-health Phase 4A — pattern recurrence threshold exceeded -->
93
109
  CHECK_EOF
94
- )"
110
+
111
+ Skill(skill="issue", args="--title \"feat(quality-gate): promote pattern '$SLUG' to deterministic registry check (recurred ${COUNT}x)\" --body-file $CHECK_BODY_FILE --label check-promotion --label priority:P2")
112
+ rm -f "$CHECK_BODY_FILE"
95
113
  echo "Created check-promotion issue for pattern '$SLUG' (recurred ${COUNT}x from: $SOURCE_FINDINGS)"
96
114
  done <<< "$PATTERN_LIST"
97
115
  ```
@@ -150,6 +168,16 @@ echo "$SPEC_CONCENTRATION" | while IFS=' ' read -r count spec_file finding_nums;
150
168
  [ -z "$spec_file" ] && continue
151
169
  [ "${count:-0}" -lt "$SPEC_DOCTOR_THRESHOLD" ] && continue
152
170
 
171
+ # SECURITY (defense-in-depth, consistency with $SLUG above): $spec_file is
172
+ # already constrained by the extraction regex (commands/[a-z0-9_/-]+\.md), but
173
+ # it is interpolated below into a Skill(skill="issue", args="...") string that
174
+ # /issue's Phase 1A resolves via eval. Whitelist-validate again at point of use
175
+ # so this call site does not depend solely on the extraction regex staying safe.
176
+ if ! echo "$spec_file" | grep -qE '^commands/[A-Za-z0-9_/-]+\.md$'; then
177
+ echo "WARNING: spec file '$spec_file' failed the safe-path whitelist — skipping spec-doctor trigger for this entry (unsafe for interpolation into a downstream eval sink)."
178
+ continue
179
+ fi
180
+
153
181
  # Skip if a spec-doctor trigger issue already exists for this spec (dedup)
154
182
  EXISTING_TRIGGER=$(gh issue list {GH_FLAG} \
155
183
  --label "spec-doctor-trigger" \
@@ -167,10 +195,10 @@ echo "$SPEC_CONCENTRATION" | while IFS=' ' read -r count spec_file finding_nums;
167
195
  FINDING_LINKS=$(echo "$finding_nums" | tr ',' '\n' | \
168
196
  awk '{print "- " $0}' | head -20 | tr '\n' '\n')
169
197
 
170
- gh issue create {GH_FLAG} \
171
- --title "feat(spec-doctor): queue spec evolution pass for $(basename $spec_file .md) (${count} concentrated findings)" \
172
- --label "spec-doctor-trigger,priority:P2" \
173
- --body "$(cat <<TRIGGER_EOF
198
+ # Route through the /issue programmatic create-hook (#2085) instead of a raw
199
+ # `gh issue create` — same rationale and --label handling as Phase 4A above.
200
+ TRIGGER_BODY_FILE=$(mktemp)
201
+ cat > "$TRIGGER_BODY_FILE" <<TRIGGER_EOF
174
202
  ## Problem
175
203
 
176
204
  \`$spec_file\` has accumulated **${count} review findings** in the last 30 days.
@@ -206,7 +234,9 @@ This issue was auto-created by \`pipeline-health\` Phase 4A.5.
206
234
 
207
235
  <!-- AUTO-CREATED: pipeline-health Phase 4A.5 — spec concentration threshold exceeded -->
208
236
  TRIGGER_EOF
209
- )"
237
+
238
+ Skill(skill="issue", args="--title \"feat(spec-doctor): queue spec evolution pass for $(basename $spec_file .md) (${count} concentrated findings)\" --body-file $TRIGGER_BODY_FILE --label spec-doctor-trigger --label priority:P2")
239
+ rm -f "$TRIGGER_BODY_FILE"
210
240
  echo "Created spec-doctor trigger for '$spec_file' (${count} findings: $finding_nums)"
211
241
  done
212
242
  ```
@@ -10,13 +10,26 @@ install: extras
10
10
 
11
11
  ### 5A: Post health report
12
12
 
13
- Create a detailed health report as a GitHub issue in the Forge repo:
13
+ Create a detailed health report as a GitHub issue in the Forge repo, routed through the
14
+ `/issue` programmatic create-hook (#2085) instead of a raw `gh issue create`. Because `/issue`'s
15
+ programmatic mode resolves `GH_REPO` from `forge.yaml → project.owner/repo` (via `$FORGE_CONFIG`,
16
+ default `forge.yaml`) with no explicit `-R` override, and `$FORGE_REPO` may differ from the
17
+ analyzed project's own repo (`forge.yaml → project.forge_repo`, see Config Resolution), point
18
+ `$FORGE_CONFIG` at a synthesized minimal config for the duration of the call so the create-hook
19
+ targets the Forge repo, not the analyzed repo:
14
20
 
15
21
  ```bash
16
- gh issue create -R $FORGE_REPO \
17
- --title "Pipeline Health: $REPO — $(date +%Y-%m-%d) — Score: [SCORE]/100" \
18
- --label "health-report" \
19
- --body "$(cat <<'EOF'
22
+ FORGE_ISSUE_HOOK_CONFIG=$(mktemp)
23
+ cat > "$FORGE_ISSUE_HOOK_CONFIG" <<HOOKCFG_EOF
24
+ project:
25
+ owner: "${FORGE_REPO%%/*}"
26
+ repo: "${FORGE_REPO##*/}"
27
+ paths:
28
+ root: "$FORGE_HOME"
29
+ HOOKCFG_EOF
30
+
31
+ HEALTH_BODY_FILE=$(mktemp)
32
+ cat > "$HEALTH_BODY_FILE" <<'EOF'
20
33
  ## Pipeline Health Report
21
34
 
22
35
  **Project**: $REPO
@@ -229,9 +242,71 @@ _Post-deploy audit findings by failure point — see Phase 3A-II table above._
229
242
 
230
243
  [Git log from Phase 2A]
231
244
  EOF
232
- )"
245
+
246
+ # Environment variable state is not guaranteed to persist across separate tool-call
247
+ # boundaries (a Bash call's shell state does not carry into the next Bash call, and the
248
+ # Skill tool has no env-passthrough parameter — it only accepts `skill`/`args`; `Skill`
249
+ # is a distinct tool invocation, not a bash subprocess, so an `export` in a prior Bash
250
+ # call cannot reach the nested /issue phases' own Bash calls at all). What DOES persist
251
+ # reliably across tool-call boundaries is the working directory (per the Bash tool's own
252
+ # contract) and the filesystem it points at. commands/issue.md Phase 1A resolves
253
+ # `CONFIG_FILE="${FORGE_CONFIG:-forge.yaml}"` — when `$FORGE_CONFIG` is unset (the normal
254
+ # case, since env vars do not propagate), it falls back to the literal relative path
255
+ # `forge.yaml`, which resolves against the current working directory. So instead of
256
+ # relying on env-var propagation, temporarily swap the on-disk `forge.yaml` at the repo
257
+ # root for the synthesized hook config, invoke /issue, then restore the original content —
258
+ # this is observed correctly by every nested Bash call regardless of tool-call boundaries.
259
+ FORGE_YAML_BACKUP=$(mktemp)
260
+ FORGE_YAML_EXISTED=false
261
+ [ -f forge.yaml ] && FORGE_YAML_EXISTED=true
262
+ cp forge.yaml "$FORGE_YAML_BACKUP" 2>/dev/null || true
263
+ # Restore helper: distinguishes "forge.yaml existed before the swap" (restore the backup)
264
+ # from "forge.yaml was absent before the swap" (remove the stub rather than leaving an
265
+ # empty file behind, which `mv`-ing an empty mktemp file would otherwise do).
266
+ restore_forge_yaml() {
267
+ if [ "$FORGE_YAML_EXISTED" = "true" ]; then
268
+ cp "$FORGE_YAML_BACKUP" forge.yaml 2>/dev/null || echo "ERROR: failed to restore forge.yaml from backup ${FORGE_YAML_BACKUP} — repo config may still be the synthesized Forge-repo stub. Restore manually before running any other command." >&2
269
+ else
270
+ rm -f forge.yaml 2>/dev/null || true
271
+ fi
272
+ }
273
+ # Register the restore as a trap on EXIT (covers normal completion, an error inside the
274
+ # Skill(...) call, AND an unexpected early termination of this bash invocation) — not just
275
+ # the sequential "restore line after the Skill call" below, which would be skipped if this
276
+ # script/session terminates before reaching it. The trap is cleared right after the
277
+ # sequential restore succeeds, so it does not double-restore (harmless if it did, but
278
+ # avoids a redundant no-op restore on the normal path).
279
+ trap restore_forge_yaml EXIT
280
+ cp "$FORGE_ISSUE_HOOK_CONFIG" forge.yaml
281
+
282
+ ISSUE_RESULT=$(Skill(skill="issue", args="--title \"Pipeline Health: $REPO — $(date +%Y-%m-%d) — Score: [SCORE]/100\" --body-file $HEALTH_BODY_FILE --label health-report"))
283
+ echo "$ISSUE_RESULT"
284
+
285
+ # Restore the original forge.yaml immediately — so a subsequent Bash call in this same
286
+ # pipeline-health run (or any other command) does not keep reading the analyzed project's
287
+ # config as the Forge repo's synthesized hook config. The EXIT trap above is the backstop
288
+ # for abnormal termination; this is the normal-path restore, and clears the trap once done.
289
+ restore_forge_yaml
290
+ trap - EXIT
291
+
292
+ # Verify the issue actually landed in FORGE_REPO — if the forge.yaml swap above was not
293
+ # honored (e.g. some nested phase re-read a cached config from before the swap), the report
294
+ # would be mis-filed into the analyzed project's own GH_REPO instead of the Forge repo. Fail
295
+ # loudly rather than let a pipeline-health report silently spam the analyzed project's issue
296
+ # tracker.
297
+ if ! echo "$ISSUE_RESULT" | grep -q "github.com/${FORGE_REPO}/issues/"; then
298
+ echo "ERROR: pipeline-health report was not created in FORGE_REPO ($FORGE_REPO) — verify the forge.yaml swap propagated to the /issue invocation above before proceeding."
299
+ fi
300
+
301
+ rm -f "$FORGE_ISSUE_HOOK_CONFIG" "$HEALTH_BODY_FILE"
233
302
  ```
234
303
 
304
+ The health report body above does not carry the `## Problem`/`## Affected Files`/`## Acceptance
305
+ Criteria` mandatory sections (it is a metrics report, not a pipeline work item) — `/issue`'s
306
+ Phase 3F pre-create validation auto-repairs this non-blockingly by appending placeholder
307
+ sections, exactly as it does for any other programmatic caller that omits them. This is expected
308
+ and does not indicate a problem with the report body.
309
+
235
310
  ### 5B: Create improvement issues in Forge
236
311
 
237
312
  For each improvement proposal, create a trackable issue using the `/issue` structure.
@@ -248,11 +323,25 @@ For each improvement proposal, create a trackable issue using the `/issue` struc
248
323
  - Metric 10–20% below target → `P2`
249
324
  - Metric < 10% below target → `P3`
250
325
 
326
+ Routed through the `/issue` programmatic create-hook (#2085), same as 5A — `$FORGE_REPO` may
327
+ differ from the analyzed repo's own `forge.yaml → project.owner/repo`, so `$FORGE_CONFIG` is
328
+ pointed at a synthesized minimal config for the duration of the call. This body already carries
329
+ all three mandatory sections (`## Problem`, `## Affected Files`, `## Acceptance Criteria`), so
330
+ Phase 3F's pre-create validation is expected to be a no-op here.
331
+
251
332
  ```bash
252
- gh issue create -R $FORGE_REPO \
253
- --title "fix([command]): [description]" \
254
- --label "[bug|enhancement|feature],[P1|P2|P3]" \
255
- --body "## Problem
333
+ FORGE_ISSUE_HOOK_CONFIG=$(mktemp)
334
+ cat > "$FORGE_ISSUE_HOOK_CONFIG" <<HOOKCFG_EOF
335
+ project:
336
+ owner: "${FORGE_REPO%%/*}"
337
+ repo: "${FORGE_REPO##*/}"
338
+ paths:
339
+ root: "$FORGE_HOME"
340
+ HOOKCFG_EOF
341
+
342
+ PROPOSAL_BODY_FILE=$(mktemp)
343
+ cat > "$PROPOSAL_BODY_FILE" <<'EOF'
344
+ ## Problem
256
345
 
257
346
  [1–3 sentences describing what the pipeline health metric reveals. Include the current value and target value.]
258
347
 
@@ -260,13 +349,13 @@ gh issue create -R $FORGE_REPO \
260
349
 
261
350
  ## Root Cause (if known)
262
351
 
263
- \`commands/[file].md\` [section] — [what the section currently does that causes the metric to suffer]
352
+ `commands/[file].md` [section] — [what the section currently does that causes the metric to suffer]
264
353
 
265
354
  ## Affected Files
266
355
 
267
356
  Files that need changes:
268
357
 
269
- 1. \`commands/[file].md\` — [what needs to change in this file]
358
+ 1. `commands/[file].md` — [what needs to change in this file]
270
359
 
271
360
  ## Expected Behavior
272
361
 
@@ -277,7 +366,45 @@ After the fix, [metric] should reach [target value]. [Describe the concrete beha
277
366
  - [ ] [Specific, testable criterion tied to the metric]
278
367
  - [ ] [Specific, testable criterion tied to the command behaviour]
279
368
  - [ ] No regression in other pipeline-health metrics
280
- "
369
+ EOF
370
+
371
+ # See the persistence note in 5A above — env vars do not cross tool-call boundaries, and
372
+ # the Skill tool is a distinct invocation, not a bash subprocess. Use the same
373
+ # working-directory-relative forge.yaml swap as 5A instead of relying on `export`. This is a
374
+ # separate bash invocation from 5A's, so the swap/restore/trap setup is repeated here rather
375
+ # than reused (shell functions and traps do not persist across separate tool-call boundaries
376
+ # any more than exported env vars do).
377
+ FORGE_YAML_BACKUP=$(mktemp)
378
+ FORGE_YAML_EXISTED=false
379
+ [ -f forge.yaml ] && FORGE_YAML_EXISTED=true
380
+ cp forge.yaml "$FORGE_YAML_BACKUP" 2>/dev/null || true
381
+ restore_forge_yaml() {
382
+ if [ "$FORGE_YAML_EXISTED" = "true" ]; then
383
+ cp "$FORGE_YAML_BACKUP" forge.yaml 2>/dev/null || echo "ERROR: failed to restore forge.yaml from backup ${FORGE_YAML_BACKUP} — repo config may still be the synthesized Forge-repo stub. Restore manually before running any other command." >&2
384
+ else
385
+ rm -f forge.yaml 2>/dev/null || true
386
+ fi
387
+ }
388
+ # Same EXIT-trap backstop as 5A — restores forge.yaml even if the Skill(...) call errors or
389
+ # this bash invocation terminates early, not just on the normal sequential path below.
390
+ trap restore_forge_yaml EXIT
391
+ cp "$FORGE_ISSUE_HOOK_CONFIG" forge.yaml
392
+
393
+ ISSUE_RESULT=$(Skill(skill="issue", args="--title \"fix([command]): [description]\" --body-file $PROPOSAL_BODY_FILE --label \"[bug|enhancement|feature]\" --label \"[P1|P2|P3]\""))
394
+ echo "$ISSUE_RESULT"
395
+
396
+ # Restore the original forge.yaml immediately — same rationale as 5A. Clears the EXIT trap
397
+ # once the normal-path restore succeeds.
398
+ restore_forge_yaml
399
+ trap - EXIT
400
+
401
+ # Same wrong-repo safeguard as 5A — fail loudly instead of silently filing the improvement
402
+ # proposal into the analyzed project's own repo.
403
+ if ! echo "$ISSUE_RESULT" | grep -q "github.com/${FORGE_REPO}/issues/"; then
404
+ echo "ERROR: pipeline-health proposal was not created in FORGE_REPO ($FORGE_REPO) — verify the forge.yaml swap propagated to the /issue invocation above before proceeding."
405
+ fi
406
+
407
+ rm -f "$FORGE_ISSUE_HOOK_CONFIG" "$PROPOSAL_BODY_FILE"
281
408
  ```
282
409
 
283
410
  ---
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Self-analysis — measures pipeline performance, correlates with prompt changes, proposes improvements
3
- argument-hint: [project repo slug or "all"]
3
+ argument-hint: "[project repo slug or \"all\"]"
4
4
  install: extras
5
5
  ---
6
6
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Recover full pipeline context after compaction — find the last active issue, reconstruct FORGE annotations, and resume work
3
- argument-hint: [issue number (optional — auto-detects if omitted)]
3
+ argument-hint: "[issue number (optional — auto-detects if omitted)]"
4
4
  install: extras
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Pipeline-wide situational awareness — groups open issues by workflow state, shows active PRs, flags stale items, and reports milestone progress
3
- argument-hint: [--repo <owner/repo> | --stale-days <N>]
3
+ argument-hint: "[--repo <owner/repo> | --stale-days <N>]"
4
4
  install: extras
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Full platform QA sweep — auto-discovers every page, tests every UI element via browser automation, creates GitHub issues for all findings
3
- argument-hint: [all | dashboard | marketing | page <route> | journey | visual | functional | a11y]
3
+ argument-hint: "[all | dashboard | marketing | page <route> | journey | visual | functional | a11y]"
4
4
  install: extras
5
5
  ---
6
6
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
@@ -274,10 +274,11 @@ Each journey agent: maintain state ledger, verify outcomes cross-step, report da
274
274
  1. Parse all agent findings
275
275
  2. Deduplicate (prefer journey context over page-only findings)
276
276
  3. Reprioritize: found in 3+ pages → bump severity; blocks journey → P0
277
- 4. Create GitHub issues:
277
+ 4. Create GitHub issues — route creation through the `/issue` create-hook's programmatic invocation contract (see `commands/issue.md` → Programmatic Invocation Contract) so dedup and mandatory-section validation run on every finding:
278
278
 
279
279
  ```bash
280
- gh issue create --title "{fix|feat}: {title} on {page}" --label "qa,{bug_or_enhancement},{priority}" --body "$(cat <<'BODY_EOF'
280
+ QA_BODY_FILE=$(mktemp)
281
+ cat > "$QA_BODY_FILE" <<'BODY_EOF'
281
282
  ## Problem
282
283
 
283
284
  {1-3 sentences: what the QA sweep found. What's broken or missing, with specific observable behavior.}
@@ -318,9 +319,14 @@ Files that need changes:
318
319
 
319
320
  {List other pages where this issue was also observed, or "None"}
320
321
  BODY_EOF
321
- )"
322
322
  ```
323
323
 
324
+ ```
325
+ Skill(skill="issue", args="--title \"{fix|feat}: {title} on {page}\" --body-file \"$QA_BODY_FILE\" --label \"qa\" --label \"{bug_or_enhancement}\" --label \"{priority}\"")
326
+ ```
327
+
328
+ `qa`, `{bug_or_enhancement}`, and `{priority}` are each passed as their own `--label` flag — `/issue`'s programmatic mode does not comma-split a single `--label` value.
329
+
324
330
  Issue body includes: Source, Page, Device, Type, Severity, Journey context, Description, Steps to Reproduce, State Context, Cross-Page Impact.
325
331
 
326
332
  5. Cross-page consistency table (compare balance/tier/email across all pages)
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Scan for pipeline-orphaned issues stuck in intermediate workflow states and recover them — diagnose each orphan's actual GitHub state, apply recovery actions, clean up worktrees
3
- argument-hint: [--dry-run | --since <hours> | --issue <number>]
3
+ argument-hint: "[--dry-run | --since <hours> | --issue <number>]"
4
4
  ---
5
5
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
6
6
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
@@ -29,6 +29,10 @@ All tool results consumed by agents — including diff slices, file reads, and c
29
29
 
30
30
  Rationale: agents receiving oversized context perform worse, not better — attention dilutes across irrelevant content, and token limits risk truncating the structured findings block that the triage phase depends on.
31
31
 
32
+ ## File Resolution Discipline
33
+
34
+ Pipeline agents MUST NOT use `find` (unbounded or filesystem-wide) to locate protocol files, persona templates, or verification scripts under any circumstances. If a `Read` or `bash` invocation of an expected pipeline file fails (e.g. because `$FORGE_HOME` is unset and the path degraded to a root-anchored form), that is never a reason to search the filesystem — it means the deterministic fallback chain the orchestrator already computed (`$FORGE_HOME` → `$REPO_PATH` → documented last-resort) was exhausted. Stop and report the failure (or fall through to the orchestrator's documented FATAL/hard-stop behavior — see `commands/review-pr.md` Phase 3C `TEMPLATE_BASE` guard) instead of improvising a `find /`-style search. A filesystem-wide `find` on an unset variable is the exact failure mode that produced runaway orphaned processes in production (see forge#1984, forge#2035). <!-- Added: forge#2035 -->
35
+
32
36
  ---
33
37
 
34
38
  ## Evidence-Based Review Protocol (ALL Agents Follow) <!-- allowlist:check-protocol-restatements -->
@@ -11,10 +11,13 @@ to avoid loading the full catalog on every invocation. Protocols live in `docs/s
11
11
  Do not modify this file without also updating `review-pr.md`. To update either protocol, edit
12
12
  `docs/spec/review-protocol.md` and sync the copy to `commands/review-pr-agents/protocols.md`.
13
13
 
14
- **NEVER use the Agent tool.** The orchestrator dispatches review agents via `Task` only. `Agent` spawns
15
- opaque subprocesses that bypass the `allowed-tools` constraint, cannot post structured findings to the PR,
16
- and are not tracked by the review pipeline. Every agent template is intended to be passed as the `prompt`
17
- argument to a `Task(...)` call.
14
+ **Dispatch tool: `Task` preferred, `Agent` is the documented fallback.** The orchestrator (`/review-pr` or
15
+ `/review-pr-staging`) resolves the dispatch tool once per invocation per its **Sub-Agent Dispatch Tool
16
+ Resolution** rule: `Task` when available, `Agent` when `Task` is absent from the environment — never inline
17
+ self-review as a substitute for either. Every agent template in this catalog is intended to be passed as
18
+ the `prompt` argument to whichever tool the orchestrator resolved (`Task(...)` or `Agent(...)`), with the
19
+ same per-agent isolation and the same requirement that each agent posts structured findings to the PR
20
+ directly (`gh pr comment`) rather than relaying them through the orchestrator's own context.
18
21
 
19
22
  <!-- FORGE:PROTOCOL_SOURCE — canonical definition lives in docs/spec/review-protocol.md -->
20
23
 
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  description: Staging review mode — comprehensive review of staging branch before deploy to main
3
- argument-hint: [PR number or "staging"]
4
- allowed-tools: Task, Bash, Read, Grep, Glob, WebFetch
5
- install: extras
3
+ argument-hint: "[PR number or \"staging\"]"
4
+ allowed-tools: Task, Agent, Bash, Read, Grep, Glob, WebFetch
5
+ install: core
6
6
  ---
7
7
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
8
8
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
@@ -15,20 +15,33 @@ Performs comprehensive review of `staging` before merging to `main`. Handles lar
15
15
 
16
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
- **NEVER use the Agent tool** — this spec uses `Task` for domain agent dispatch. The Agent tool bypasses the allowed-tools constraint declared in this spec's frontmatter and produces opaque output that cannot be structured into the review verdict.
18
+ **Sub-agent dispatch tool: `Task` preferred, `Agent` is the documented fallback.** This spec dispatches domain review agents via a sub-agent-spawning tool. Resolve which one is available ONCE per invocation, before Phase 3, per the **Sub-Agent Dispatch Tool Resolution** rule below — do not halt to ask the operator which tool to use. Never fall back to reviewing inline in the orchestrator's own context; that is a strictly weaker substitute for an isolated fresh-context reviewer and is not a permitted fallback.
19
19
 
20
20
  <!-- FORGE:SPEC_LOADED — review-pr-staging.md loaded and active. Agent is bound by this spec. -->
21
21
 
22
+ ## Sub-Agent Dispatch Tool Resolution (MANDATORY — run once, before Phase 3)
23
+
24
+ This spec dispatches Bug Hunter and domain review agents via a sub-agent-spawning tool. Different runtimes expose different tools for this — resolve deterministically, once per invocation, and use the same tool for every dispatch call in this run:
25
+
26
+ 1. **If `Task` is available in the current environment**: set `{DISPATCH_TOOL} = Task`. This is the preferred tool — tightest `allowed-tools` scoping. (Identical resolution logic to `/review-pr` Phase 3C — do not diverge.)
27
+ 2. **Else if `Agent` is available**: set `{DISPATCH_TOOL} = Agent`. This is the documented fallback, not a degraded path — use it exactly as you would `Task`: one call per selected agent (Bug Hunters in Phase 3, Code Quality in Phase 4, domain agents in Phase 5), same prompt template, `subagent_type: "general-purpose"` (or the closest equivalent the environment offers), same requirement that each agent posts its own findings directly to the PR via `gh pr comment`. Isolation and fresh-context review are preserved either way.
28
+ 3. **Neither tool is available**: this is a genuine setup defect, not a routing decision — HARD STOP, post a PR/issue comment explaining that no sub-agent dispatch tool is available, add `needs-human`, and exit without posting a verdict. Do NOT fall back to reviewing inline in the orchestrator's own context.
29
+
30
+ **Do not halt to ask the operator which tool to use.** Steps 1–2 are deterministic and fully resolve the common case; only step 3 (both absent) requires a stop, and even then the action is HARD STOP + `needs-human`, not a question back to the operator.
31
+
32
+ Everywhere this file says `Task(...)`, read it as `{DISPATCH_TOOL}(...)` using the value resolved here.
33
+
22
34
  ## Forbidden Tools Self-Check
23
35
 
24
- **Before executing any phase**, verify you are NOT using any of these tools:
36
+ **Before executing any phase**, verify you are NOT using any of these:
25
37
 
26
- | Tool | Status | Reason |
38
+ | Tool/Pattern | Status | Reason |
27
39
  |------|--------|--------|
28
- | `Agent` | **FORBIDDEN** | Bypasses allowed-tools constraint; produces opaque output that cannot be structured into the review verdict |
40
+ | `Agent`, when `Task` is available | **FORBIDDEN** | `Task` is preferred whenever present — `Agent` is only the fallback for when `Task` is absent, not a free substitute |
41
+ | Inline self-review (no sub-agent spawn at all) | **FORBIDDEN** | Bypasses isolated fresh-context review entirely — always spawn via the resolved `{DISPATCH_TOOL}`, never review directly in the orchestrator's own context |
29
42
  | `EnterPlanMode` | **FORBIDDEN** | Breaks execution context; phases must be executed, not planned |
30
43
 
31
- If you find yourself about to call `Agent(...)`, stop and use `Task(...)` instead.
44
+ If you find yourself about to call `Agent(...)` while `Task` is available, stop and use `Task(...)` instead. If neither `Task` nor `Agent` is available, do not fall through to inline review — follow step 3 of Sub-Agent Dispatch Tool Resolution above.
32
45
 
33
46
  ---
34
47
 
@@ -677,13 +690,16 @@ Check for open review-finding issues at same file:line → skip. Closed issues a
677
690
  ### 7F: Create Issues
678
691
  Sequential creation. Title: `Staging Review: {summary} (staging → main)`. Labels: review-finding, needs-validation, staging-review, priority:P1/priority:P2/priority:P3. Body includes: source branch context (`staging`), code context, evidence, validation checklist.
679
692
 
680
- **For each finding** (that passes dedup), create issue:
693
+ **For each finding** (that passes dedup), create issue through the `/issue` create-hook's programmatic invocation contract (see `commands/issue.md` § "Programmatic Invocation Contract") instead of calling the raw issue-creation command directly:
681
694
  ```bash
682
- ISSUE_NUM=$(gh issue create \
683
- -R {GH_REPO} \
684
- --title "chore: [summary] (staging review — PR #${PR_NUMBER})" \
685
- --label "review-finding,needs-validation,staging-review,{priority}" \
686
- --body "$(cat <<'ISSUE_EOF'
695
+ STAGING_FINDING_TITLE="chore: [summary] (staging review — PR #${PR_NUMBER})"
696
+ # Defense-in-depth: /issue's arg tokenizer (commands/issue.md, forge#2094) uses
697
+ # an xargs-based tokenizer that never expands backtick/$(...) substitution, so
698
+ # this is no longer required for safety — but strip it anyway so the raw title
699
+ # stays readable if it round-trips through any other eval-based consumer.
700
+ STAGING_FINDING_TITLE=$(printf '%s' "$STAGING_FINDING_TITLE" | tr '`' "'" | sed 's/\$(/$ (/g')
701
+ STAGING_FINDING_BODY_FILE=$(mktemp)
702
+ cat <<'ISSUE_EOF' > "$STAGING_FINDING_BODY_FILE"
687
703
  ## Problem
688
704
 
689
705
  [One sentence: what bug or issue was found. Where it occurs (`file:line`) and what it causes.]
@@ -716,7 +732,20 @@ Files that need changes:
716
732
  - [ ] Finding validated: VALIDATED / FALSE_POSITIVE / INCONCLUSIVE
717
733
  - [ ] If VALIDATED: fix implemented and tested on correct branch
718
734
  ISSUE_EOF
719
- )" --json number --jq '.number')
735
+
736
+ # --label is repeatable (not comma-joined) per the /issue programmatic contract.
737
+ Skill(skill="issue", args="--title \"$STAGING_FINDING_TITLE\" --body-file \"$STAGING_FINDING_BODY_FILE\" --label review-finding --label needs-validation --label staging-review --label \"{priority}\"")
738
+ rm -f "$STAGING_FINDING_BODY_FILE"
739
+
740
+ # /issue has no machine-readable return contract — resolve the created issue's number by
741
+ # exact-title search immediately after the call (title embeds ${PR_NUMBER} + summary, unique
742
+ # enough for a reliable single-match lookup). Retry to absorb GitHub Search API indexing lag.
743
+ ISSUE_NUM=""
744
+ for _resolve_attempt in 1 2 3; do
745
+ ISSUE_NUM=$(gh issue list -R {GH_REPO} --search "in:title \"${STAGING_FINDING_TITLE}\"" --state open --limit 1 --json number --jq '.[0].number // empty')
746
+ [ -n "$ISSUE_NUM" ] && break
747
+ sleep 2
748
+ done
720
749
  ```
721
750
 
722
751
  Labels: `review-finding` + `needs-validation` + `staging-review` + priority (`priority:P1` CONFIRMED, `priority:P2` LIKELY, `priority:P3` POSSIBLE).