forgedock 1.1.11 → 1.3.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 (109) hide show
  1. package/README.md +9 -3
  2. package/bin/cli-spawn-shared.mjs +25 -0
  3. package/bin/engine/phases.mjs +178 -28
  4. package/bin/engine/state.mjs +39 -7
  5. package/bin/engine-cli.mjs +85 -3
  6. package/bin/engine.mjs +71 -8
  7. package/bin/forgedock.mjs +322 -20
  8. package/bin/hooks/pre-tool-use.mjs +345 -10
  9. package/bin/init-enrich-api.mjs +12 -4
  10. package/bin/init-enrich-cli.mjs +328 -0
  11. package/bin/init-enrich.mjs +137 -0
  12. package/bin/journey.mjs +116 -15
  13. package/bin/runner.mjs +653 -10
  14. package/commands/adopt.md +1 -1
  15. package/commands/analytics.md +10 -7
  16. package/commands/audit-agents.md +29 -6
  17. package/commands/audit.md +9 -20
  18. package/commands/autopilot.md +52 -78
  19. package/commands/changelog.md +1 -1
  20. package/commands/ci-audit.md +19 -10
  21. package/commands/cleanup.md +1 -1
  22. package/commands/compat-audit.md +1 -1
  23. package/commands/deploy-info.md +1 -1
  24. package/commands/deploy-pr.md +1 -1
  25. package/commands/design-bench.md +1 -1
  26. package/commands/design-render-critique-loop.md +1 -1
  27. package/commands/diagnose.md +1 -1
  28. package/commands/forge-stats.md +6 -5
  29. package/commands/forgedock-init.md +1 -1
  30. package/commands/geo-audit.md +9 -6
  31. package/commands/incident-response.md +15 -5
  32. package/commands/issue.md +302 -15
  33. package/commands/milestone.md +45 -18
  34. package/commands/optimize.md +1 -1
  35. package/commands/orchestrate/phase-1-resolve.md +20 -5
  36. package/commands/orchestrate/phase-4-execution.md +8 -1
  37. package/commands/orchestrate/phase-5-cleanup.md +43 -2
  38. package/commands/orchestrate/phase-6-report.md +1 -0
  39. package/commands/orchestrate.md +1 -1
  40. package/commands/pipeline-health/phase-4-proposals.md +40 -10
  41. package/commands/pipeline-health/phase-5-report.md +140 -13
  42. package/commands/pipeline-health.md +1 -1
  43. package/commands/pipeline-resume.md +1 -1
  44. package/commands/pipeline-status.md +1 -1
  45. package/commands/qa-sweep.md +27 -4
  46. package/commands/recover-orphans.md +1 -1
  47. package/commands/replay.md +16 -2
  48. package/commands/review-pr-agents/protocols.md +4 -0
  49. package/commands/review-pr-agents/spec-cli.md +179 -0
  50. package/commands/review-pr-agents.md +7 -4
  51. package/commands/review-pr-staging.md +44 -15
  52. package/commands/review-pr.md +122 -28
  53. package/commands/rollback.md +15 -5
  54. package/commands/security-audit.md +14 -11
  55. package/commands/signal-planner.md +32 -12
  56. package/commands/spec-doctor.md +1 -1
  57. package/commands/sync-ecosystem.md +54 -11
  58. package/commands/test-gate.md +49 -11
  59. package/commands/upgrade-deps.md +518 -0
  60. package/commands/validate.md +16 -5
  61. package/commands/work-on/build/architect.md +1 -1
  62. package/commands/work-on/build/context.md +1 -1
  63. package/commands/work-on/build/implement.md +2 -2
  64. package/commands/work-on/build/validate.md +64 -2
  65. package/commands/work-on/build.md +10 -2
  66. package/commands/work-on/close.md +1 -1
  67. package/commands/work-on/decompose.md +35 -25
  68. package/commands/work-on/investigate.md +2 -2
  69. package/commands/work-on/remediate.md +1 -1
  70. package/commands/work-on/review.md +5 -3
  71. package/commands/work-on-monolithic.md +1 -1
  72. package/commands/work-on.md +28 -7
  73. package/docs/AIDER.md +264 -0
  74. package/docs/AUTOPILOT-CRON.md +130 -0
  75. package/docs/CI.md +18 -0
  76. package/docs/CONFIG.md +41 -2
  77. package/docs/CURSOR.md +135 -0
  78. package/docs/OPENCODE.md +312 -0
  79. package/docs/demo.gif +0 -0
  80. package/docs/eval/README.md +4 -4
  81. package/docs/launch/show-hn.md +3 -3
  82. package/docs/site/annotations-explained.md +2 -2
  83. package/docs/site/architecture.md +180 -0
  84. package/docs/site/command-reference.md +38 -0
  85. package/docs/site/configuration.md +243 -0
  86. package/docs/site/for-companies.md +4 -4
  87. package/docs/site/forge-annotation-protocol.md +1 -1
  88. package/docs/site/github-is-the-memory.md +2 -2
  89. package/docs/site/how-it-works.md +1 -1
  90. package/docs/site/public/llms-full.txt +380 -0
  91. package/docs/site/public/llms.txt +35 -0
  92. package/docs/spec/review-protocol.md +6 -0
  93. package/docs/spec-graph-schema.md +3 -3
  94. package/package.json +2 -2
  95. package/packages/protocol/README.md +75 -0
  96. package/packages/protocol/src/card.js +114 -0
  97. package/packages/protocol/src/cli.js +4 -77
  98. package/packages/protocol/src/emit.js +14 -3
  99. package/packages/protocol/src/index.js +2 -1
  100. package/packages/protocol/src/parse.js +17 -8
  101. package/packages/protocol/src/types.js +1 -1
  102. package/packages/protocol/src/validate.js +11 -0
  103. package/scripts/check-command-side-effects.sh +261 -0
  104. package/scripts/check-spec-markers.sh +273 -0
  105. package/scripts/classify-lane.sh +29 -7
  106. package/scripts/forge-run.sh +322 -0
  107. package/templates/cross-repo-trigger.yml +282 -0
  108. package/templates/cursor/forge.mdc +148 -0
  109. package/templates/github-actions/forgedock-autopilot.yml +113 -0
@@ -39,9 +39,50 @@ Include the audit summary in the final report (Phase 6). Key metrics to surface:
39
39
  - **Resume cycles** — how many times agents had to be resumed
40
40
  - **Stall boundaries** — which phase transitions cause the most stalls
41
41
 
42
- ### Step 5C: Report cleanup results
42
+ ### Step 5C: Close the coordination issue (claims board) <!-- Added: forge#2072 -->
43
43
 
44
- Include the cleanup summary in the final report (Phase 6). If cleanup found problems, call them out — they indicate agent pipeline failures that may need investigation.
44
+ **Why this step exists**: Step 3D.1 (`phase-3-dependency.md`) creates a per-batch coordination issue (the claims board) and exports `FORGE_COORD_ISSUE`/`COORD_ISSUE_NUMBER`. Step 5A's `/cleanup` sweep only closes orphaned issues whose PR merged — the claims board has no PR of its own, so that heuristic never matches it. Without this step, one `automation`-labelled issue leaks per orchestration batch.
45
+
46
+ **Run after Step 5B, before Step 5D.** No-ops cleanly if the claims board was never created this run (`COORD_ISSUE_NUMBER` unset/empty — e.g. Step 3D.1's `gh issue create` failed, or this batch never reached DAG construction). Tolerates GitHub API failures without aborting the rest of Phase 5 — the same tolerant-failure convention (`2>/dev/null || echo ...`, `|| true`) used throughout `phase-3-dependency.md`/`phase-4-execution.md`.
47
+
48
+ ```bash
49
+ if [ -n "${COORD_ISSUE_NUMBER:-}" ]; then
50
+ # Detect any still-active (unreleased) claims — a claim posted with no matching
51
+ # CLAIM_RELEASED comment on the same coordination issue. Adapted from the query
52
+ # phase-4-execution.md's claims-board relaxation sweep uses (Step 4B item 4),
53
+ # simplified to holder-only (a flat string array) since Step 5C only needs
54
+ # holder names for the closure note — unlike phase-4-execution.md's version,
55
+ # which also captures each claim's `files` into an object alongside `holder`.
56
+ ACTIVE_CLAIMS=$(gh api repos/{GH_REPO}/issues/${COORD_ISSUE_NUMBER}/comments \
57
+ --jq '[.[] | select(.body | contains("<!-- FORGE:CLAIM -->")) |
58
+ select(.body | contains("<!-- FORGE:CLAIM_RELEASED -->") | not)] |
59
+ map(.body | capture("\\*\\*Holder\\*\\*: (?P<h>[^\\n]+)").h // "unknown")' 2>/dev/null || echo '[]')
60
+ ACTIVE_CLAIMS_COUNT=$(echo "$ACTIVE_CLAIMS" | jq 'length' 2>/dev/null || echo 0)
61
+
62
+ if [ "$ACTIVE_CLAIMS_COUNT" -gt 0 ] 2>/dev/null; then
63
+ ACTIVE_CLAIMS_NOTE="**Still-active claims at close time** (${ACTIVE_CLAIMS_COUNT}) — not blocking closure, listed for visibility:
64
+ $(echo "$ACTIVE_CLAIMS" | jq -r '.[] | "- " + .' 2>/dev/null)"
65
+ else
66
+ ACTIVE_CLAIMS_NOTE="No active (unreleased) claims at close time."
67
+ fi
68
+
69
+ gh issue close "$COORD_ISSUE_NUMBER" -R {GH_REPO} --comment "Batch complete — closing claims board.
70
+
71
+ ${ACTIVE_CLAIMS_NOTE}
72
+
73
+ Closed automatically by \`/orchestrate\` Phase 5 Step 5C." 2>/dev/null \
74
+ && echo "Closed coordination issue #${COORD_ISSUE_NUMBER}" \
75
+ || echo "WARNING: failed to close coordination issue #${COORD_ISSUE_NUMBER} — non-fatal, continuing Phase 5"
76
+ else
77
+ echo "No coordination issue for this batch (FORGE_COORD_ISSUE unset) — Step 5C is a no-op"
78
+ fi
79
+ ```
80
+
81
+ **Idempotency**: `gh issue close` on an already-closed issue succeeds as a no-op (no error) — safe to re-run this step on a resumed/compacted session without an extra pre-check.
82
+
83
+ ### Step 5D: Report cleanup results
84
+
85
+ Include the cleanup summary in the final report (Phase 6), including whether the coordination issue was closed this run (see Step 5C). If cleanup found problems, call them out — they indicate agent pipeline failures that may need investigation.
45
86
 
46
87
  ---
47
88
 
@@ -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 -->
@@ -36,6 +36,23 @@ See README.md Requirements section for full setup instructions.
36
36
 
37
37
  ---
38
38
 
39
+ ## Playwright MCP Prerequisite (MANDATORY — check before proceeding)
40
+
41
+ Playwright MCP is a **guaranteed ForgeDock dependency** for all browser automation commands. Before running any qa-sweep phase, verify it is available:
42
+
43
+ **Check**: Confirm `mcp__playwright__*` tools are available in your tool context (e.g., `mcp__playwright__browser_navigate`, `mcp__playwright__browser_snapshot`).
44
+
45
+ **If Playwright MCP tools are NOT available**: STOP immediately. Do not attempt to run qa-sweep — all browser automation steps will fail silently.
46
+
47
+ **Fix**:
48
+ 1. Register Playwright MCP: `claude mcp add playwright npx @playwright/mcp@latest`
49
+ 2. Restart Claude Code to reload MCP servers
50
+ 3. Verify with: `npx forgedock doctor` (Check 9 confirms registration)
51
+
52
+ See README.md Requirements section for full setup instructions.
53
+
54
+ ---
55
+
39
56
  ## Browser Tool Reference
40
57
 
41
58
  All browser automation uses Playwright MCP tools (`mcp__playwright__*`). Playwright MCP is a guaranteed ForgeDock dependency — see the Playwright MCP Prerequisite section above.
@@ -274,10 +291,11 @@ Each journey agent: maintain state ledger, verify outcomes cross-step, report da
274
291
  1. Parse all agent findings
275
292
  2. Deduplicate (prefer journey context over page-only findings)
276
293
  3. Reprioritize: found in 3+ pages → bump severity; blocks journey → P0
277
- 4. Create GitHub issues:
294
+ 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
295
 
279
296
  ```bash
280
- gh issue create --title "{fix|feat}: {title} on {page}" --label "qa,{bug_or_enhancement},{priority}" --body "$(cat <<'BODY_EOF'
297
+ QA_BODY_FILE=$(mktemp)
298
+ cat > "$QA_BODY_FILE" <<'BODY_EOF'
281
299
  ## Problem
282
300
 
283
301
  {1-3 sentences: what the QA sweep found. What's broken or missing, with specific observable behavior.}
@@ -318,9 +336,14 @@ Files that need changes:
318
336
 
319
337
  {List other pages where this issue was also observed, or "None"}
320
338
  BODY_EOF
321
- )"
322
339
  ```
323
340
 
341
+ ```
342
+ Skill(skill="issue", args="--title \"{fix|feat}: {title} on {page}\" --body-file \"$QA_BODY_FILE\" --label \"qa\" --label \"{bug_or_enhancement}\" --label \"{priority}\"")
343
+ ```
344
+
345
+ `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.
346
+
324
347
  Issue body includes: Source, Page, Device, Type, Severity, Journey context, Description, Steps to Reproduce, State Context, Cross-Page Impact.
325
348
 
326
349
  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 -->
@@ -95,7 +95,7 @@ fi
95
95
 
96
96
  ```bash
97
97
  # Filter to comments that contain a FORGE pipeline annotation
98
- FORGE_COMMENTS=$(echo "$ALL_COMMENTS" | jq -c '[.[] | select(.body | test("<!-- FORGE:(INVESTIGATOR|CONTRACT|CONTEXT|ARCHITECT|BUILDER|DECOMPOSED|TRAJECTORY)"))]')
98
+ FORGE_COMMENTS=$(echo "$ALL_COMMENTS" | jq -c '[.[] | select(.body | test("<!-- FORGE:(INVESTIGATOR|CONTRACT|CONTEXT|ARCHITECT|BUILDER|DECOMPOSED|SUMMARY|TRAJECTORY)"))]')
99
99
  FORGE_COUNT=$(echo "$FORGE_COMMENTS" | jq 'length')
100
100
 
101
101
  if [ "$FORGE_COUNT" -eq 0 ]; then
@@ -115,7 +115,7 @@ echo ""
115
115
 
116
116
  ## Step 5: Display Each Annotation
117
117
 
118
- Phase mapping (inline): `INVESTIGATOR`→Phase 1, `CONTRACT`→Phase 3C, `CONTEXT`→Phase 3C.5, `ARCHITECT`→Phase 3C.6, `BUILDER`→Phase 3M, `DECOMPOSED`→Phase 2, `TRAJECTORY`→Phase 7.
118
+ Phase mapping (inline): `INVESTIGATOR`→Phase 1, `CONTRACT`→Phase 3C, `CONTEXT`→Phase 3C.5, `ARCHITECT`→Phase 3C.6, `BUILDER`→Phase 3M, `DECOMPOSED`→Phase 2, `SUMMARY`→Phase C4.5, `TRAJECTORY`→Phase 7.
119
119
 
120
120
  For each FORGE comment in chronological order, print a formatted block. Use Python for robust multiline extraction from the comment body.
121
121
 
@@ -136,6 +136,7 @@ echo "$FORGE_COMMENTS" | jq -c '.[]' | while IFS= read -r COMMENT; do
136
136
  "FORGE:ARCHITECT") PHASE_LABEL="Phase 3C.6 — Architecture Plan" ;;
137
137
  "FORGE:BUILDER") PHASE_LABEL="Phase 3M — Implementation" ;;
138
138
  "FORGE:DECOMPOSED") PHASE_LABEL="Phase 2 — Decomposition" ;;
139
+ "FORGE:SUMMARY") PHASE_LABEL="Phase C4.5 — Shareable Summary" ;;
139
140
  "FORGE:TRAJECTORY") PHASE_LABEL="Phase 7 — Pipeline Trajectory" ;;
140
141
  *) PHASE_LABEL="$ANNOTATION" ;;
141
142
  esac
@@ -216,6 +217,19 @@ elif annotation == "FORGE:DECOMPOSED":
216
217
  for s in subs[:5]:
217
218
  print(f"│ {s}")
218
219
 
220
+ elif annotation == "FORGE:SUMMARY":
221
+ lane = extract(r'\*\*Lane\*\*\s*\|\s*([^\|]+)')
222
+ verdict = extract(r'\*\*Investigation verdict\*\*\s*\|\s*([^\|]+)')
223
+ task_type = extract(r'\*\*Task type\*\*\s*\|\s*([^\|]+)')
224
+ review = extract(r'\*\*Review verdict\*\*\s*\|\s*([^\|]+)')
225
+ files = extract(r'\*\*Files changed\*\*\s*\|\s*([^\|]+)')
226
+ pr_link = extract(r'\[View PR #(\d+)\]')
227
+ print(f"│ Lane : {lane.strip()[:60]}")
228
+ print(f"│ Verdict : {verdict.strip()[:60]}")
229
+ print(f"│ Task type : {task_type.strip()[:60]}")
230
+ print(f"│ Review : {review.strip()[:60]}")
231
+ print(f"│ Files : {files.strip()[:30]} | PR: #{pr_link}")
232
+
219
233
  elif annotation == "FORGE:TRAJECTORY":
220
234
  # Print the phase table lines verbatim (they're already formatted)
221
235
  in_table = False
@@ -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 -->