forgedock 1.1.10 → 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.
- package/README.md +1 -1
- package/bin/cli-spawn-shared.mjs +25 -0
- package/bin/engine-cli.mjs +22 -1
- package/bin/engine.mjs +46 -4
- package/bin/forgedock.mjs +166 -20
- package/bin/hooks/pre-tool-use.mjs +331 -10
- package/bin/init-detect.mjs +1 -1
- package/bin/init-enrich-api.mjs +12 -4
- package/bin/init-enrich-cli.mjs +328 -0
- package/bin/init-enrich.mjs +137 -0
- package/bin/journey.mjs +328 -50
- package/bin/runner.mjs +653 -10
- package/commands/adopt.md +1 -1
- package/commands/analytics.md +10 -7
- package/commands/audit-agents.md +29 -6
- package/commands/audit.md +9 -20
- package/commands/autopilot.md +52 -78
- package/commands/changelog.md +1 -1
- package/commands/ci-audit.md +19 -10
- package/commands/cleanup.md +1 -1
- package/commands/compat-audit.md +1 -1
- package/commands/deploy-info.md +1 -1
- package/commands/deploy-pr.md +1 -1
- package/commands/design-bench.md +1 -1
- package/commands/design-render-critique-loop.md +1 -1
- package/commands/diagnose.md +1 -1
- package/commands/forge-stats.md +1 -1
- package/commands/forgedock-init.md +1 -1
- package/commands/geo-audit.md +9 -6
- package/commands/incident-response.md +15 -5
- package/commands/issue.md +298 -15
- package/commands/milestone.md +45 -18
- package/commands/optimize.md +1 -1
- package/commands/orchestrate/phase-1-resolve.md +20 -5
- package/commands/orchestrate/phase-4-execution.md +6 -1
- package/commands/orchestrate/phase-5-cleanup.md +43 -2
- package/commands/orchestrate/phase-6-report.md +1 -0
- package/commands/orchestrate.md +1 -1
- package/commands/pipeline-health/phase-4-proposals.md +40 -10
- package/commands/pipeline-health/phase-5-report.md +140 -13
- package/commands/pipeline-health.md +1 -1
- package/commands/pipeline-resume.md +1 -1
- package/commands/pipeline-status.md +1 -1
- package/commands/qa-sweep.md +10 -4
- package/commands/recover-orphans.md +1 -1
- package/commands/review-pr-agents/protocols.md +4 -0
- package/commands/review-pr-agents.md +7 -4
- package/commands/review-pr-staging.md +44 -15
- package/commands/review-pr.md +73 -28
- package/commands/rollback.md +15 -5
- package/commands/security-audit.md +14 -11
- package/commands/signal-planner.md +32 -12
- package/commands/spec-doctor.md +1 -1
- package/commands/sync-ecosystem.md +54 -11
- package/commands/test-gate.md +49 -11
- package/commands/validate.md +28 -9
- package/commands/work-on/build/architect.md +1 -1
- package/commands/work-on/build/context.md +1 -1
- package/commands/work-on/build/implement.md +2 -2
- package/commands/work-on/build/validate.md +3 -1
- package/commands/work-on/build.md +1 -1
- package/commands/work-on/close.md +1 -1
- package/commands/work-on/decompose.md +27 -24
- package/commands/work-on/investigate.md +2 -2
- package/commands/work-on/remediate.md +11 -3
- package/commands/work-on/review.md +5 -3
- package/commands/work-on-monolithic.md +1 -1
- package/commands/work-on.md +38 -8
- package/docs/CI.md +18 -0
- package/docs/CONFIG.md +5 -2
- package/docs/site/annotations-explained.md +2 -2
- package/docs/site/command-reference.md +21 -0
- package/docs/spec/review-protocol.md +6 -0
- package/package.json +1 -1
- package/scripts/danger-zones.mjs +3 -1
- package/scripts/transition-label.sh +80 -28
|
@@ -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
|
|
package/commands/orchestrate.md
CHANGED
|
@@ -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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
package/commands/qa-sweep.md
CHANGED
|
@@ -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
|
-
|
|
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
|
-
**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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:
|
|
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
|
-
**
|
|
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
|
|
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** |
|
|
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(...)
|
|
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
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
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
|
-
|
|
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).
|