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.
- package/README.md +9 -3
- package/bin/cli-spawn-shared.mjs +25 -0
- package/bin/engine/phases.mjs +178 -28
- package/bin/engine/state.mjs +39 -7
- package/bin/engine-cli.mjs +85 -3
- package/bin/engine.mjs +71 -8
- package/bin/forgedock.mjs +322 -20
- package/bin/hooks/pre-tool-use.mjs +345 -10
- 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 +116 -15
- 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 +6 -5
- 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 +302 -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 +8 -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 +27 -4
- package/commands/recover-orphans.md +1 -1
- package/commands/replay.md +16 -2
- package/commands/review-pr-agents/protocols.md +4 -0
- package/commands/review-pr-agents/spec-cli.md +179 -0
- package/commands/review-pr-agents.md +7 -4
- package/commands/review-pr-staging.md +44 -15
- package/commands/review-pr.md +122 -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/upgrade-deps.md +518 -0
- package/commands/validate.md +16 -5
- 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 +64 -2
- package/commands/work-on/build.md +10 -2
- package/commands/work-on/close.md +1 -1
- package/commands/work-on/decompose.md +35 -25
- package/commands/work-on/investigate.md +2 -2
- package/commands/work-on/remediate.md +1 -1
- package/commands/work-on/review.md +5 -3
- package/commands/work-on-monolithic.md +1 -1
- package/commands/work-on.md +28 -7
- package/docs/AIDER.md +264 -0
- package/docs/AUTOPILOT-CRON.md +130 -0
- package/docs/CI.md +18 -0
- package/docs/CONFIG.md +41 -2
- package/docs/CURSOR.md +135 -0
- package/docs/OPENCODE.md +312 -0
- package/docs/demo.gif +0 -0
- package/docs/eval/README.md +4 -4
- package/docs/launch/show-hn.md +3 -3
- package/docs/site/annotations-explained.md +2 -2
- package/docs/site/architecture.md +180 -0
- package/docs/site/command-reference.md +38 -0
- package/docs/site/configuration.md +243 -0
- package/docs/site/for-companies.md +4 -4
- package/docs/site/forge-annotation-protocol.md +1 -1
- package/docs/site/github-is-the-memory.md +2 -2
- package/docs/site/how-it-works.md +1 -1
- package/docs/site/public/llms-full.txt +380 -0
- package/docs/site/public/llms.txt +35 -0
- package/docs/spec/review-protocol.md +6 -0
- package/docs/spec-graph-schema.md +3 -3
- package/package.json +2 -2
- package/packages/protocol/README.md +75 -0
- package/packages/protocol/src/card.js +114 -0
- package/packages/protocol/src/cli.js +4 -77
- package/packages/protocol/src/emit.js +14 -3
- package/packages/protocol/src/index.js +2 -1
- package/packages/protocol/src/parse.js +17 -8
- package/packages/protocol/src/types.js +1 -1
- package/packages/protocol/src/validate.js +11 -0
- package/scripts/check-command-side-effects.sh +261 -0
- package/scripts/check-spec-markers.sh +273 -0
- package/scripts/classify-lane.sh +29 -7
- package/scripts/forge-run.sh +322 -0
- package/templates/cross-repo-trigger.yml +282 -0
- package/templates/cursor/forge.mdc +148 -0
- 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:
|
|
42
|
+
### Step 5C: Close the coordination issue (claims board) <!-- Added: forge#2072 -->
|
|
43
43
|
|
|
44
|
-
|
|
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
|
|
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 -->
|
|
@@ -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
|
-
|
|
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 -->
|
package/commands/replay.md
CHANGED
|
@@ -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 -->
|