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