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
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
---
|
|
2
|
+
install: internal
|
|
3
|
+
---
|
|
4
|
+
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
5
|
+
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
6
|
+
|
|
7
|
+
# Agent: Spec/CLI Auditor
|
|
8
|
+
|
|
9
|
+
> Read `review-pr-agents/protocols.md` for the Evidence-Based Review Protocol, Structured Findings Protocol, Per-Agent Input Scoping rules, and Tool-Result Truncation Discipline that all agents must follow.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
**Trigger**: SPEC_CLI domain detected (PR touches `commands/**` or `bin/**`)
|
|
14
|
+
**Type**: `security-exploit-auditor` | **Model**: `sonnet`
|
|
15
|
+
|
|
16
|
+
**Prompt template:**
|
|
17
|
+
```
|
|
18
|
+
You are auditing PR #[PR_NUMBER] for spec-as-code and CLI correctness in [PROJECT_NAME].
|
|
19
|
+
|
|
20
|
+
## Context
|
|
21
|
+
- PR title: [TITLE]
|
|
22
|
+
- Files changed: [FILE_LIST]
|
|
23
|
+
[PROJECT_CONTEXT]
|
|
24
|
+
|
|
25
|
+
## Spec/CLI Conventions
|
|
26
|
+
|
|
27
|
+
[DOMAIN_CONTEXT]
|
|
28
|
+
|
|
29
|
+
If no spec/CLI context is configured above, derive conventions from the changed files: read sibling `.mjs` files for the established exec pattern and sibling `commands/*.md` files for the established side-effect gating pattern.
|
|
30
|
+
|
|
31
|
+
## Why This Agent Exists
|
|
32
|
+
`commands/**` (the pipeline's spec-as-code prompts) and `bin/**` (the Node CLI) ARE the product — every `commands/*.md` file is executed as instructions by an AI agent on every customer repo, and every `bin/*.mjs` file runs with the user's local `gh` credentials. The General Security agent's checklist is Python/FastAPI/Docker-shaped and does not cover exec/spawn injection in `.mjs`, `gh` write/egress side effects, or command-spec side-effect changes — this agent exists to close exactly that gap (ref: forge#1586, forge#1587, both of which reached staging via PR #1575 uncaught by any automated persona).
|
|
33
|
+
|
|
34
|
+
## Step 1: Read the Diff
|
|
35
|
+
|
|
36
|
+
The diff is pre-supplied as `[DOMAIN_DIFF_SLICE]` and is capped at ~100K chars. Do NOT re-fetch the full diff — use what was provided.
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
gh pr diff [PR_NUMBER] --name-only
|
|
40
|
+
# Use the pre-supplied diff: [DOMAIN_DIFF_SLICE]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Build the two file subsets this agent scans (note: `gh pr diff` does NOT accept path arguments — always filter the name-only list with grep):
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
CLI_FILES=$(gh pr diff [PR_NUMBER] --name-only | grep -E '^bin/.*\.(mjs|js|ts)$')
|
|
47
|
+
SPEC_FILES=$(gh pr diff [PR_NUMBER] --name-only | grep -E '^commands/.*\.md$')
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Step 2: Shell-String Exec/Spawn Interpolation (CLI files)
|
|
51
|
+
|
|
52
|
+
Node's `child_process.exec()`/`execSync()` run their argument through `/bin/sh -c` (or `cmd.exe` on Windows) — any interpolated string becomes shell-interpretable. `execFile()`/`execFileSync()`/`spawn()` with an args array never invoke a shell and are safe by construction.
|
|
53
|
+
|
|
54
|
+
Scan the added lines of the pre-supplied diff for exec calls with template-string or concatenated arguments (attribute each hit to its file via the nearest preceding `diff --git` header):
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Added lines with exec/execSync taking a template string or concatenation
|
|
58
|
+
echo "[DOMAIN_DIFF_SLICE]" | grep -E '^\+' | grep -E '\bexecSync?\(`|\bexecSync?\([^)]*\+' | grep -v '^\+\+\+'
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
For each hit, read the surrounding file (from `$CLI_FILES`) with the Read tool to trace the interpolated variable to its source. Classify:
|
|
62
|
+
- `execSync(\`gh ... ${variable} ...\`)` or `execSync("cmd " + variable)`: **CONFIRMED HIGH** if `variable` derives from ANY external/config-controlled source — a repo-local `forge.yaml` field, CLI argv, an environment variable, or a `gh api` response body. This is the exact shape of forge#1586 (`ghJson()` in `bin/report.mjs` interpolated `forge.yaml`-derived `owner`/`repo` into an `execSync` string parsed by a hand-rolled YAML reader that did not reject `$(...)`, backticks, `;`, or `&`).
|
|
63
|
+
- `execFileSync("gh", [...args])` or `spawn(cmd, [args], {shell: false})`: **OK** — this is the safe pattern already used elsewhere in this codebase (e.g. `watch()` in `bin/forgedock.mjs`).
|
|
64
|
+
- `execSync`/`exec` called with a **fully static string literal** (no interpolation, no concatenation): **OK** — no injectable surface.
|
|
65
|
+
|
|
66
|
+
**Do not stop at the first call site** — scan every `exec(`/`execSync(` occurrence in the diff, not just the one nearest an obviously-tainted variable. A file can fix one call site and leave a sibling one unconverted.
|
|
67
|
+
|
|
68
|
+
## Step 3: `gh` Write/Egress Side Effects (all files)
|
|
69
|
+
|
|
70
|
+
Any new or modified `gh` invocation that **writes** (mutates GitHub state) or **publishes** (makes something world-readable) is a side-effect change and must be justified by the PR's stated purpose.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# gh write/publish operations in added lines
|
|
74
|
+
echo "[DOMAIN_DIFF_SLICE]" | grep -E '^\+' | grep -E \
|
|
75
|
+
'gh (gist create|pr merge|pr close|issue create|issue close|issue comment|repo edit|label create|release create|workflow run)' | \
|
|
76
|
+
grep -v '^\+\+\+'
|
|
77
|
+
|
|
78
|
+
# Newly-added --public / visibility flags
|
|
79
|
+
echo "[DOMAIN_DIFF_SLICE]" | grep -E '^\+' | grep -E '\-\-public\b|visibility.*public|make.*public' | grep -v '^\+\+\+'
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
For each hit:
|
|
83
|
+
- A new `gh gist create ... --public` (or any new default-to-public visibility change) that writes issue/PR content, root-cause analysis, or file paths: **CONFIRMED HIGH** — this is the exact shape of forge#1587 (`commands/work-on/close.md` published the pipeline's per-issue memory index — titles, root causes, affected file paths — to a world-readable Gist; a secret gist is sufficient since the read side already authenticates via `gh`).
|
|
84
|
+
- A new `gh pr merge`, `gh issue close`, or `gh label` mutation added to a command spec (`commands/*.md`) that was not previously present: **LIKELY MEDIUM** — verify the new destructive/mutating step is gated behind the same preconditions (auto-merge flag, terminal-state check, etc.) as sibling mutations in the same file. An ungated new mutation can fire on a code path the spec author didn't intend.
|
|
85
|
+
- Read-only `gh` calls (`gh pr view`, `gh issue list`, `gh api ... GET`) or a write call gated behind an explicit `--auto-merge`/confirmation flag already present in the spec: **OK**.
|
|
86
|
+
|
|
87
|
+
## Step 4: Command-Spec Side-Effect Changes (spec files)
|
|
88
|
+
|
|
89
|
+
`commands/*.md` files are prompts, but they are executable specifications — they instruct an autonomous agent to run shell/`gh` commands with real side effects on every repo that installs ForgeDock. Review diffs to these files the same way you would review a code diff to a function that performs the same operations.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Added lines in the pre-supplied diff that introduce/alter gh, git-destructive, delete, or network commands
|
|
93
|
+
echo "[DOMAIN_DIFF_SLICE]" | grep -E '^\+' | grep -E '\bgh \w|\bgit (push|reset|clean|rm)|\brm -rf|\bcurl\b|\bwget\b' | grep -v '^\+\+\+'
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Attribute each hit to its `commands/*.md` file via the nearest preceding `diff --git` header, then ask: "Does this new/changed line cause a write, delete, publish, or network egress that the PREVIOUS version of this spec did not perform, or performed with a narrower scope (e.g. `gist create` without `--public` → with `--public`; `git push` → `git push --force`)?" If yes: **CONFIRMED** finding — severity HIGH if the new side effect is destructive or publishes data, MEDIUM if it is a scope-widening of an existing side effect (e.g. a new label added to an existing bulk-edit loop). <!-- allowlist:check-command-side-effects — the `git push` above is a cited example of a scope-widening diff this auditor looks for, not a command this spec runs -->
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
**Do NOT treat `commands/*.md` diffs as "just docs".** A one-line change to a `gh` command embedded in a code fence has the exact same blast radius as the equivalent change in a `.py`/`.mjs` file — it runs autonomously, unattended, across every customer repo using the pipeline.
|
|
100
|
+
|
|
101
|
+
## Step 5: Secret Handling (CLI files)
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# Added lines touching secret-like identifiers
|
|
105
|
+
echo "[DOMAIN_DIFF_SLICE]" | grep -E '^\+' | grep -iE 'token|secret|api[_-]?key|password|credential' | grep -v '^\+\+\+'
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
For each hit attributed to a file in `$CLI_FILES`:
|
|
109
|
+
- A token/secret/key passed as a CLI argument to a child process (visible to any other process via `ps`/Task Manager) rather than via an env var or stdin: **CONFIRMED HIGH**.
|
|
110
|
+
- A token/secret logged via `console.log`/`console.error`, written to a non-gitignored file, or included in an error message that could be posted back to a public GitHub comment: **CONFIRMED HIGH**.
|
|
111
|
+
- A token/secret read from `process.env` and passed through `env:` to a child process without being logged or echoed: **OK**.
|
|
112
|
+
|
|
113
|
+
## Step 6: Network/Publish Calls (CLI files)
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# Added lines with network or publish calls
|
|
117
|
+
echo "[DOMAIN_DIFF_SLICE]" | grep -E '^\+' | grep -iE '\bfetch\(|\bhttps?\.request\(|\baxios\.|npm publish|npm.*publish' | grep -v '^\+\+\+'
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
For each hit: identify the destination host. A call to `api.github.com`/`github.com` via the already-authenticated `gh` CLI wrapper is expected. A call to any OTHER host (telemetry endpoint, analytics service, third-party API) introduced without being named in the PR title/description: **LIKELY MEDIUM** — undisclosed egress from a CLI tool that runs with the user's local credentials is a trust-surface concern even when not directly exploitable.
|
|
121
|
+
|
|
122
|
+
## Step 7: Post Findings
|
|
123
|
+
```bash
|
|
124
|
+
gh pr comment [PR_NUMBER] --body "$(cat <<'EOF'
|
|
125
|
+
## Spec/CLI Audit
|
|
126
|
+
|
|
127
|
+
### Risk Level: [LOW/MEDIUM/HIGH/CRITICAL]
|
|
128
|
+
|
|
129
|
+
### Shell/Exec Injection Findings
|
|
130
|
+
| File:Line | Pattern | Tainted Source | Confidence | Evidence |
|
|
131
|
+
|-----------|---------|-----------------|------------|----------|
|
|
132
|
+
| ... | execSync template string | forge.yaml field / argv / env | CONFIRMED | [code path] |
|
|
133
|
+
|
|
134
|
+
### gh Write/Egress Side Effects
|
|
135
|
+
| File:Line | Operation | New or Widened? | Confidence | Evidence |
|
|
136
|
+
|-----------|-----------|------------------|------------|----------|
|
|
137
|
+
| ... | gh gist create --public | New | CONFIRMED | [explanation] |
|
|
138
|
+
|
|
139
|
+
### Command-Spec Side-Effect Changes
|
|
140
|
+
| File:Line | Old Behavior | New Behavior | Confidence |
|
|
141
|
+
|-----------|--------------|--------------|------------|
|
|
142
|
+
| ... | ... | ... | ... |
|
|
143
|
+
|
|
144
|
+
### Secret Handling
|
|
145
|
+
[Any secrets passed as argv, logged, or written to disk — or "None found"]
|
|
146
|
+
|
|
147
|
+
### Network/Publish Calls
|
|
148
|
+
[Any new external network destinations — or "None found — all egress via gh CLI to github.com"]
|
|
149
|
+
|
|
150
|
+
### Files Reviewed
|
|
151
|
+
[List all files checked]
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
*Spec/CLI audit*
|
|
155
|
+
|
|
156
|
+
<!-- REVIEW-FINDINGS-START -->
|
|
157
|
+
<!-- FINDING:SPEC-1|CONFIDENCE|SEVERITY|file.mjs:line|Summary -->
|
|
158
|
+
<!-- (add one FINDING line per issue found — include ALL confidence levels) -->
|
|
159
|
+
<!-- REVIEW-FINDINGS-END -->
|
|
160
|
+
EOF
|
|
161
|
+
)"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Structured Findings**: Include the structured findings block above at the end of your comment. Your prefix: `SPEC`. See the Structured Findings Protocol in `protocols.md` for format rules.
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Coverage Matrix — SPEC Agent
|
|
168
|
+
|
|
169
|
+
| Defect Category | Check Item(s) | Status | Ref |
|
|
170
|
+
|----------------|---------------|--------|-----|
|
|
171
|
+
| Shell-string exec/spawn interpolation in `.mjs` | Step 2 | COVERED | forge#1586 |
|
|
172
|
+
| `gh` write/egress side effects (gist --public, pr merge, issue create) | Step 3 | COVERED | forge#1587 |
|
|
173
|
+
| Command-spec side-effect changes in `commands/*.md` | Step 4 | COVERED | |
|
|
174
|
+
| Secret handling in `.mjs` (argv exposure, logging) | Step 5 | COVERED | |
|
|
175
|
+
| Undisclosed network/publish calls | Step 6 | COVERED | |
|
|
176
|
+
| Prototype pollution / unsafe `eval`/`new Function` in `.mjs` | — | GAP | |
|
|
177
|
+
| Path traversal via CLI-supplied file paths | — | GAP | |
|
|
178
|
+
|
|
179
|
+
---
|
|
@@ -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).
|
package/commands/review-pr.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Context-aware PR review — analyzes what the PR touches, spawns domain-specific agents with project conventions. Supports staging reviews.
|
|
3
|
-
argument-hint: [PR number, URL, "open", or "staging" for feature→main review]
|
|
4
|
-
allowed-tools: Task, Bash, Read, Grep, Glob, WebFetch, Skill
|
|
3
|
+
argument-hint: "[PR number, URL, \"open\", or \"staging\" for feature→main review]"
|
|
4
|
+
allowed-tools: Task, Agent, Bash, Read, Grep, Glob, WebFetch, Skill
|
|
5
5
|
---
|
|
6
6
|
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
7
7
|
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
@@ -11,7 +11,7 @@ allowed-tools: Task, Bash, Read, Grep, Glob, WebFetch, Skill
|
|
|
11
11
|
**Input**: $ARGUMENTS
|
|
12
12
|
|
|
13
13
|
**NEVER use plan mode (EnterPlanMode)** during review — it breaks execution context.
|
|
14
|
-
**
|
|
14
|
+
**Sub-agent dispatch tool: `Task` preferred, `Agent` is the documented fallback.** review-pr dispatches domain review agents via a sub-agent-spawning tool. Resolve which one is available ONCE per invocation, before Phase 3C, 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; inline self-review is a strictly weaker substitute for an isolated fresh-context reviewer and is not a permitted fallback.
|
|
15
15
|
|
|
16
16
|
**Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet" (standard tier); the General Security & Quality reviewer spawned as always-runs Task uses `effort: xhigh` (deep tier). Fallback: `model: "opus"` if rate-limited. User can override with `--model <name>`. Pass model and effort explicitly in every `Task` tool call. Feature gate: pass `effort` only on Claude Code >= 2.1.154. **The domain-review Task agents this file dispatches** (per `commands/review-pr-agents/*.md`) resolve via `model: "{SUBAGENT_MODEL}"` — forge.yaml `agents.subagent_model`, else `agents.default_model`, else `"sonnet"` — not `{DEFAULT_MODEL}` directly.
|
|
17
17
|
|
|
@@ -19,7 +19,7 @@ allowed-tools: Task, Bash, Read, Grep, Glob, WebFetch, Skill
|
|
|
19
19
|
|
|
20
20
|
## HARD RULES — READ BEFORE ANYTHING ELSE
|
|
21
21
|
|
|
22
|
-
1. **Use `
|
|
22
|
+
1. **Use the resolved `{DISPATCH_TOOL}(...)` for ALL domain agent launches.** Resolve `{DISPATCH_TOOL}` once, per the **Sub-Agent Dispatch Tool Resolution** section below: `Task` when available, `Agent` as the documented fallback when `Task` is absent from the environment. Every dispatch call in this run uses the same resolved tool — do not mix. Whichever tool is used, each agent MUST run in an isolated, fresh-context sub-agent (not inline in the orchestrator's own context) and post findings to the PR via `gh pr comment` in structured form.
|
|
23
23
|
|
|
24
24
|
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
25
|
|
|
@@ -29,16 +29,29 @@ allowed-tools: Task, Bash, Read, Grep, Glob, WebFetch, Skill
|
|
|
29
29
|
|
|
30
30
|
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
31
|
|
|
32
|
+
## Sub-Agent Dispatch Tool Resolution (MANDATORY — run once, before Phase 3C)
|
|
33
|
+
|
|
34
|
+
This spec dispatches 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:
|
|
35
|
+
|
|
36
|
+
1. **If `Task` is available in the current environment**: set `{DISPATCH_TOOL} = Task`. This is the preferred tool — tightest `allowed-tools` scoping.
|
|
37
|
+
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 domain agent, 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.
|
|
38
|
+
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 — inline self-review is strictly weaker than an isolated fresh-context reviewer and is never a substitute for a missing dispatch tool.
|
|
39
|
+
|
|
40
|
+
**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.
|
|
41
|
+
|
|
42
|
+
Everywhere this file (and `review-pr-agents.md` / the `review-pr-agents/*.md` persona files) says `Task(...)`, read it as `{DISPATCH_TOOL}(...)` using the value resolved here.
|
|
43
|
+
|
|
32
44
|
## Forbidden Tools Self-Check
|
|
33
45
|
|
|
34
|
-
**Before executing any phase**, verify you are NOT using any of these
|
|
46
|
+
**Before executing any phase**, verify you are NOT using any of these:
|
|
35
47
|
|
|
36
|
-
| Tool | Status | Reason |
|
|
48
|
+
| Tool/Pattern | Status | Reason |
|
|
37
49
|
|------|--------|--------|
|
|
38
|
-
| `Agent` | **FORBIDDEN** |
|
|
50
|
+
| `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 |
|
|
51
|
+
| 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 |
|
|
39
52
|
| `EnterPlanMode` | **FORBIDDEN** | Breaks execution context; must run phases, not plan them |
|
|
40
53
|
|
|
41
|
-
If you find yourself about to call `Agent(...)
|
|
54
|
+
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. If you find yourself about to use `EnterPlanMode`, stop and execute the next phase directly.
|
|
42
55
|
|
|
43
56
|
## Architecture — How This Command Works
|
|
44
57
|
|
|
@@ -48,11 +61,11 @@ This is the **orchestrator**. It routes to the right review mode, runs automated
|
|
|
48
61
|
|
|
49
62
|
| File | What | How to invoke |
|
|
50
63
|
|------|------|---------------|
|
|
51
|
-
| `$FORGE_HOME/commands/review-pr-agents/protocols.md` | Shared review protocols (Evidence-Based + Structured Findings + Input Scoping) | `Read` tool during Phase 3C (always) |
|
|
52
|
-
| `$FORGE_HOME/commands/review-pr-agents/<persona>.md` | Per-persona agent prompt templates (9 files) | `Read` tool during Phase 3C (selected agents only) |
|
|
53
|
-
| `$FORGE_HOME/commands/review-pr-staging.md` | Full staging→main review pipeline | `Skill("review-pr-staging", ...)` during Phase 0 |
|
|
64
|
+
| `${FORGE_HOME:-$REPO_PATH}/commands/review-pr-agents/protocols.md` | Shared review protocols (Evidence-Based + Structured Findings + Input Scoping) | `Read` tool during Phase 3C (always) |
|
|
65
|
+
| `${FORGE_HOME:-$REPO_PATH}/commands/review-pr-agents/<persona>.md` | Per-persona agent prompt templates (9 files) | `Read` tool during Phase 3C (selected agents only) |
|
|
66
|
+
| `${FORGE_HOME:-$REPO_PATH}/commands/review-pr-staging.md` | Full staging→main review pipeline | `Skill("review-pr-staging", ...)` during Phase 0 |
|
|
54
67
|
|
|
55
|
-
`$FORGE_HOME` defaults to `~/.claude` (the directory where `npx forgedock` symlinks commands).
|
|
68
|
+
`$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`.
|
|
56
69
|
|
|
57
70
|
**Invocation flow:**
|
|
58
71
|
```
|
|
@@ -239,6 +252,14 @@ done
|
|
|
239
252
|
|
|
240
253
|
# Resolve repo name early — used in Phases 5, 6, 8B, 9A (clean-review skip path bypasses Phase 6A) <!-- Added: forge#820 -->
|
|
241
254
|
REPO=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')
|
|
255
|
+
|
|
256
|
+
# First-time contributor detection <!-- Added: forge#960 -->
|
|
257
|
+
# Requires REPO to be set (line above). Checks whether the PR author has any prior merged PRs in this repo.
|
|
258
|
+
# IS_FIRST_TIME_CONTRIBUTOR is consumed in Phase 9 to conditionally post an onboarding welcome comment.
|
|
259
|
+
PR_AUTHOR=$(gh pr view "$ARGUMENTS" -R "$REPO" --json author --jq '.author.login')
|
|
260
|
+
PRIOR_MERGED_COUNT=$(gh pr list -R "$REPO" --author "$PR_AUTHOR" --state merged --limit 1 --json number --jq 'length' 2>/dev/null || echo "1")
|
|
261
|
+
IS_FIRST_TIME_CONTRIBUTOR="false"
|
|
262
|
+
[ "$PRIOR_MERGED_COUNT" -eq 0 ] && IS_FIRST_TIME_CONTRIBUTOR="true"
|
|
242
263
|
```
|
|
243
264
|
|
|
244
265
|
### 1B: Classify
|
|
@@ -580,7 +601,7 @@ Map each changed file to its activation requirements.
|
|
|
580
601
|
|
|
581
602
|
### Step 2.5B: Run Verification
|
|
582
603
|
|
|
583
|
-
For each changed file, execute the relevant checks using the standalone verification scripts in `$FORGE_HOME/scripts/`. These scripts can also be run independently outside the review context (e.g., from `/quality-gate` or `/work-on` builder steps).
|
|
604
|
+
For each changed file, execute the relevant checks using the standalone verification scripts in `${FORGE_HOME:-$SCRIPTS_HOME}/scripts/`. These scripts can also be run independently outside the review context (e.g., from `/quality-gate` or `/work-on` builder steps).
|
|
584
605
|
|
|
585
606
|
**Platform note**: The verify-*.sh scripts require bash and standard POSIX tools. On Windows without bash (Git Bash / WSL / MSYS2), these checks are skipped with an explicit message — the review continues without them.
|
|
586
607
|
|
|
@@ -588,6 +609,20 @@ For each changed file, execute the relevant checks using the standalone verifica
|
|
|
588
609
|
CHANGED_FILES=$(gh pr diff $ARGUMENTS --name-only)
|
|
589
610
|
REPO_ROOT="." # Assumes cwd is the repo root
|
|
590
611
|
|
|
612
|
+
# Resolve the verify-*.sh scripts source directory with the same deterministic
|
|
613
|
+
# fallback as the Phase 3C TEMPLATE_BASE guard: $FORGE_HOME first (the installed
|
|
614
|
+
# ForgeDock location), then this repo's own root (forge.yaml -> paths.root, or
|
|
615
|
+
# git top-level). A bare/unset $FORGE_HOME must never be used directly in a path —
|
|
616
|
+
# that degrades to a root-anchored path (/scripts/verify-*.sh) and silently skips
|
|
617
|
+
# every check below. Never fall back to a filesystem-wide `find`.
|
|
618
|
+
# <!-- Added: forge#2035 -->
|
|
619
|
+
if [ -n "$FORGE_HOME" ] && [ -f "$FORGE_HOME/scripts/verify-route-registration.sh" ]; then
|
|
620
|
+
SCRIPTS_HOME="$FORGE_HOME"
|
|
621
|
+
else
|
|
622
|
+
FORGE_YAML="${FORGE_CONFIG:-$(git rev-parse --show-toplevel 2>/dev/null)/forge.yaml}"
|
|
623
|
+
SCRIPTS_HOME=$(yq '.paths.root' "$FORGE_YAML" 2>/dev/null || git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
624
|
+
fi
|
|
625
|
+
|
|
591
626
|
# --- Platform / bash capability guard ---
|
|
592
627
|
# The verify-*.sh scripts require bash. Detect availability before invoking.
|
|
593
628
|
# On Windows without Git Bash/WSL, skip gracefully rather than crash.
|
|
@@ -627,11 +662,11 @@ if [ "$BASH_AVAILABLE" = "true" ]; then
|
|
|
627
662
|
[ -n "$_API_MIDDLEWARE" ] && export FORGE_API_MIDDLEWARE_DIR="$_API_MIDDLEWARE"
|
|
628
663
|
fi
|
|
629
664
|
echo "=== Running: verify-route-registration.sh ==="
|
|
630
|
-
bash "$
|
|
665
|
+
bash "$SCRIPTS_HOME/scripts/verify-route-registration.sh" "$CHANGED_FILES_TMP" "$REPO_ROOT" || true
|
|
631
666
|
|
|
632
667
|
# 2. Environment variable wiring (checks .env.example, docker-compose, env_validation, SOPS mapping)
|
|
633
668
|
echo "=== Running: verify-env-vars.sh ==="
|
|
634
|
-
bash "$
|
|
669
|
+
bash "$SCRIPTS_HOME/scripts/verify-env-vars.sh" "$DIFF_TMP" "$REPO_ROOT" || true
|
|
635
670
|
|
|
636
671
|
# 3. Host headers in shell scripts + client-side proxy bypass check
|
|
637
672
|
# Read project-specific internal service patterns from forge.yaml (if present)
|
|
@@ -645,11 +680,11 @@ if [ "$BASH_AVAILABLE" = "true" ]; then
|
|
|
645
680
|
fi
|
|
646
681
|
export FORGE_INTERNAL_PATTERNS
|
|
647
682
|
echo "=== Running: verify-host-headers.sh ==="
|
|
648
|
-
bash "$
|
|
683
|
+
bash "$SCRIPTS_HOME/scripts/verify-host-headers.sh" "$CHANGED_FILES_TMP" "$REPO_ROOT" || true
|
|
649
684
|
|
|
650
685
|
# 4. SOPS deploy chain (ENV_MAPPING consistency, deploy path drift, hotfix sync)
|
|
651
686
|
echo "=== Running: verify-sops-chain.sh ==="
|
|
652
|
-
bash "$
|
|
687
|
+
bash "$SCRIPTS_HOME/scripts/verify-sops-chain.sh" "$DIFF_TMP" "$CHANGED_FILES_TMP" "$REPO_ROOT" || true
|
|
653
688
|
|
|
654
689
|
# Cleanup temp files
|
|
655
690
|
rm -f "$CHANGED_FILES_TMP" "$DIFF_TMP"
|
|
@@ -1485,9 +1520,9 @@ The `protocols.md` file contains the Evidence-Based Review Protocol, Structured
|
|
|
1485
1520
|
6. Substitute code index slice: `[INDEX_SLICE]` → the matching `$INDEX_SLICE_{DOMAIN}` variable for this agent (e.g., `$INDEX_SLICE_AUTH` for the auth agent). Agents MUST query index data first; fall back to grep only when index slice is empty or unavailable.
|
|
1486
1521
|
7. Substitute per-agent diff slice: `[DOMAIN_DIFF_SLICE]` → the matching `$DIFF_SLICE_*` variable (e.g., `$DIFF_SLICE_AUTH` for the auth agent, `$DIFF_SLICE_SECURITY` for the security agent). This replaces any `gh pr diff [PR_NUMBER]` call inside the agent template — the agent works from the pre-computed slice, not the full changeset.
|
|
1487
1522
|
8. If Phase 2.5 found broken assumptions, append them to the agent's prompt as "Pre-found integration issues to verify"
|
|
1488
|
-
9. Launch via `
|
|
1523
|
+
9. Launch via the resolved `{DISPATCH_TOOL}` (see Sub-Agent Dispatch Tool Resolution above) with `model: "{SUBAGENT_MODEL}"` (forge.yaml `agents.subagent_model`, else `agents.default_model`, else `"sonnet"`; fallback `"opus"` if rate-limited)
|
|
1489
1524
|
|
|
1490
|
-
**CRITICAL**: Launch ALL selected agents in a SINGLE message using multiple
|
|
1525
|
+
**CRITICAL**: Launch ALL selected agents in a SINGLE message using multiple `{DISPATCH_TOOL}` calls. Each agent posts findings directly to the PR via `gh pr comment`.
|
|
1491
1526
|
|
|
1492
1527
|
#### Domain Diff Slicing
|
|
1493
1528
|
|
|
@@ -1629,7 +1664,7 @@ if [ "$DEDUP_EXIT" -eq 1 ]; then
|
|
|
1629
1664
|
# Add a comment on the existing issue referencing this recurrence in PR #${PR_NUMBER}
|
|
1630
1665
|
elif [ "$DEDUP_EXIT" -eq 2 ]; then
|
|
1631
1666
|
echo "DEDUP: Usage error — $DEDUP_RESULT"
|
|
1632
|
-
# Skip this finding — do NOT fall through to
|
|
1667
|
+
# Skip this finding — do NOT fall through to issue creation on a usage error
|
|
1633
1668
|
fi
|
|
1634
1669
|
```
|
|
1635
1670
|
|
|
@@ -1688,13 +1723,17 @@ fi
|
|
|
1688
1723
|
- Open `review-finding` issue at same file with similar title (3+ shared keywords) → **skip** (likely same finding despite line drift)
|
|
1689
1724
|
- Closed `review-finding` at same file within ±5 lines → create with regression warning, elevate to `priority:P1`
|
|
1690
1725
|
|
|
1691
|
-
**For each finding** (that passes dedup), create issue:
|
|
1726
|
+
**For each finding** (that passes dedup), create issue through the `/issue` create-hook's programmatic invocation contract (see `commands/issue.md` § "Programmatic Invocation Contract") — this preserves the bespoke line-range/title dedup above as a precise pre-check, while `/issue`'s own Phase 2D dedup runs as a coarser second pass:
|
|
1692
1727
|
```bash
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1728
|
+
FINDING_ISSUE_TITLE="fix: [summary] (review finding — PR #${PR_NUMBER})"
|
|
1729
|
+
# Defense-in-depth: /issue's arg tokenizer (commands/issue.md Argument Parsing,
|
|
1730
|
+
# forge#2094) uses an xargs-based tokenizer that never expands backtick/$(...)
|
|
1731
|
+
# substitution, so this is no longer required for safety — but strip it anyway
|
|
1732
|
+
# so the raw title stays readable if it round-trips through any other
|
|
1733
|
+
# eval-based consumer.
|
|
1734
|
+
FINDING_ISSUE_TITLE=$(printf '%s' "$FINDING_ISSUE_TITLE" | tr '`' "'" | sed 's/\$(/$ (/g')
|
|
1735
|
+
FINDING_ISSUE_BODY_FILE=$(mktemp)
|
|
1736
|
+
cat <<'ISSUE_EOF' > "$FINDING_ISSUE_BODY_FILE"
|
|
1698
1737
|
## Problem
|
|
1699
1738
|
|
|
1700
1739
|
[One sentence: what bug or issue was found. Where it occurs (`file:line`) and what it causes.]
|
|
@@ -1745,7 +1784,21 @@ Files that need changes:
|
|
|
1745
1784
|
- [ ] Reproduce or construct proof-of-concept
|
|
1746
1785
|
[BATCHABLE_ANNOTATION]
|
|
1747
1786
|
ISSUE_EOF
|
|
1748
|
-
|
|
1787
|
+
|
|
1788
|
+
# --label is repeatable (not comma-joined) per the /issue programmatic contract.
|
|
1789
|
+
Skill(skill="issue", args="--title \"$FINDING_ISSUE_TITLE\" --body-file \"$FINDING_ISSUE_BODY_FILE\" --label review-finding --label needs-validation --label \"{priority}\" ${MILESTONE_FLAG}")
|
|
1790
|
+
rm -f "$FINDING_ISSUE_BODY_FILE"
|
|
1791
|
+
|
|
1792
|
+
# /issue has no machine-readable return contract (it's a user-facing command, not a work-on
|
|
1793
|
+
# subcommand) — resolve the created issue's number by exact-title search immediately after
|
|
1794
|
+
# the call. The title embeds ${PR_NUMBER} and the finding summary, making it unique enough
|
|
1795
|
+
# for a reliable single-match lookup. Retry to absorb GitHub Search API indexing lag.
|
|
1796
|
+
ISSUE_NUM=""
|
|
1797
|
+
for _resolve_attempt in 1 2 3; do
|
|
1798
|
+
ISSUE_NUM=$(gh issue list -R ${REPO} --search "in:title \"${FINDING_ISSUE_TITLE}\"" --state open --limit 1 --json number --jq '.[0].number // empty')
|
|
1799
|
+
[ -n "$ISSUE_NUM" ] && break
|
|
1800
|
+
sleep 2
|
|
1801
|
+
done
|
|
1749
1802
|
```
|
|
1750
1803
|
|
|
1751
1804
|
Labels: `review-finding` + `needs-validation` + priority (`priority:P1` CONFIRMED, `priority:P2` LIKELY, `priority:P3` POSSIBLE).
|
|
@@ -1888,7 +1941,7 @@ ATTRIBUTION_FOOTER_LINE=""
|
|
|
1888
1941
|
if [ "$ATTRIBUTION_PR_FOOTER" = "true" ]; then
|
|
1889
1942
|
ATTRIBUTION_FOOTER_LINE="
|
|
1890
1943
|
---
|
|
1891
|
-
> Orchestrated with [ForgeDock](https://github.com/RapierCraftStudios/ForgeDock) — state, scheduling, review, and memory on GitHub."
|
|
1944
|
+
> ⚒️ Orchestrated with [ForgeDock](https://github.com/RapierCraftStudios/ForgeDock) — state, scheduling, review, and memory on GitHub."
|
|
1892
1945
|
fi
|
|
1893
1946
|
|
|
1894
1947
|
# Build trust annotation line for verdict body — always included so the decision is auditable.
|
|
@@ -2165,6 +2218,47 @@ REVIEW_IS_STALE="false"
|
|
|
2165
2218
|
if [ "$CURRENT_SHA" != "$REVIEW_SHA" ]; then REVIEW_IS_STALE="true"; fi
|
|
2166
2219
|
```
|
|
2167
2220
|
|
|
2221
|
+
### Phase 9 — New Contributor Welcome (Conditional) <!-- Added: forge#960 -->
|
|
2222
|
+
|
|
2223
|
+
Post a welcome comment when all three conditions are true:
|
|
2224
|
+
1. `IS_FIRST_TIME_CONTRIBUTOR="true"` (set in Phase 1A — no prior merged PRs from this author in this repo)
|
|
2225
|
+
2. `community.welcome_new_contributors` is not `false` in `forge.yaml` (default: enabled)
|
|
2226
|
+
3. This is a single-PR review (MODE 3) — not a staging→main or multi-PR review
|
|
2227
|
+
|
|
2228
|
+
```bash
|
|
2229
|
+
# Read config — default enabled when key is absent
|
|
2230
|
+
WELCOME_ENABLED=$(yq '.community.welcome_new_contributors // true' forge.yaml 2>/dev/null || echo 'true')
|
|
2231
|
+
|
|
2232
|
+
if [ "$IS_FIRST_TIME_CONTRIBUTOR" = "true" ] && [ "$WELCOME_ENABLED" != "false" ]; then
|
|
2233
|
+
# Read optional custom values from forge.yaml community section
|
|
2234
|
+
CONTRIBUTING_URL=$(yq '.community.contributing_url // "CONTRIBUTING.md"' forge.yaml 2>/dev/null || echo 'CONTRIBUTING.md')
|
|
2235
|
+
CUSTOM_CONVENTIONS=$(yq '.community.conventions // ""' forge.yaml 2>/dev/null || echo '')
|
|
2236
|
+
PROJECT_NAME_WELCOME=$(yq '.project.name // "this project"' forge.yaml 2>/dev/null || echo 'this project')
|
|
2237
|
+
|
|
2238
|
+
gh pr comment "$ARGUMENTS" --body "## Welcome to ${PROJECT_NAME_WELCOME}! 🎉
|
|
2239
|
+
|
|
2240
|
+
Hi @${PR_AUTHOR} — this looks like your first merged contribution here. Thanks for opening a PR!
|
|
2241
|
+
|
|
2242
|
+
### What just happened
|
|
2243
|
+
|
|
2244
|
+
The automated review pipeline (/review-pr) ran against your PR. It spawned domain-specific agents to review security, integration assumptions, and code quality. Any findings were created as separate GitHub issues — not merge blockers unless they are CONFIRMED at HIGH/CRITICAL severity.
|
|
2245
|
+
|
|
2246
|
+
### Key conventions
|
|
2247
|
+
|
|
2248
|
+
$([ -n "\$CUSTOM_CONVENTIONS" ] && echo "\$CUSTOM_CONVENTIONS" || echo "- **Conventional commits**: prefix your commit messages with \`fix(scope):\`, \`feat(scope):\`, \`refactor(scope):\`, etc.
|
|
2249
|
+
- **DCO sign-off**: all commits must be signed off with \`git commit -s\` (Developer Certificate of Origin — required for the dual-license model).
|
|
2250
|
+
Fix unsigned commits: \`git commit --amend -s --no-edit && git push --force-with-lease\`
|
|
2251
|
+
- **Issues, not inline fixes**: when you spot a bug or improvement opportunity, open a GitHub issue rather than fixing it inline — so it flows through the pipeline with full traceability.")
|
|
2252
|
+
|
|
2253
|
+
### Getting started
|
|
2254
|
+
|
|
2255
|
+
- Contributing guide: [\`${CONTRIBUTING_URL}\`](${CONTRIBUTING_URL})
|
|
2256
|
+
- Run \`npx forgedock\` to install the pipeline commands locally if you want to run /review-pr or /work-on yourself.
|
|
2257
|
+
|
|
2258
|
+
Welcome aboard!"
|
|
2259
|
+
fi
|
|
2260
|
+
```
|
|
2261
|
+
|
|
2168
2262
|
**Verifiable agent count (MANDATORY — do not report a self-asserted `Agents: [N]` figure)** <!-- Added: forge#1849 -->
|
|
2169
2263
|
|
|
2170
2264
|
A degraded run that skipped Task-based agent dispatch must be visible from this summary alone, without interrogating the agent afterward. Compute the actual launched-agent count from the `<!-- FORGE:REVIEW-AGENT:{domain} -->` comments each agent is required to post (Phase 3C), rather than trusting a free-text tally:
|
package/commands/rollback.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Create a revert PR to roll back a shipped feature or fix that caused a production incident
|
|
3
|
-
argument-hint: [PR number to revert, or "last" for most recent deploy]
|
|
3
|
+
argument-hint: "[PR number to revert, or \"last\" for most recent deploy]"
|
|
4
4
|
install: extras
|
|
5
5
|
---
|
|
6
6
|
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
@@ -213,9 +213,10 @@ fi
|
|
|
213
213
|
### Step 4B: Create a follow-up issue
|
|
214
214
|
|
|
215
215
|
```bash
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
216
|
+
FOLLOWUP_TITLE="fix: {original_title} (reverted — needs new approach)"
|
|
217
|
+
FOLLOWUP_BODY_TMPFILE=$(mktemp)
|
|
218
|
+
trap 'rm -f "$FOLLOWUP_BODY_TMPFILE"' EXIT
|
|
219
|
+
cat > "$FOLLOWUP_BODY_TMPFILE" <<'BODY_EOF'
|
|
219
220
|
## Problem
|
|
220
221
|
|
|
221
222
|
PR #{PR_NUMBER} ({original_title}) was merged and deployed but caused a production incident. It was reverted in PR #{REVERT_PR_NUMBER}. The original fix needs to be re-implemented with a different approach that avoids the production issue.
|
|
@@ -249,7 +250,16 @@ Files that need changes (same as original fix, different approach):
|
|
|
249
250
|
|
|
250
251
|
Regression of #{ORIGINAL_ISSUE}. Should investigate why the original approach caused issues before implementing.
|
|
251
252
|
BODY_EOF
|
|
252
|
-
)
|
|
253
|
+
# Route through the /issue create-hook (canonical dedup + body validation) instead of a raw
|
|
254
|
+
# `gh issue create`.
|
|
255
|
+
Skill(skill="issue", args="--title \"$FOLLOWUP_TITLE\" --body-file \"$FOLLOWUP_BODY_TMPFILE\" --label bug --label \"priority:P1\"")
|
|
256
|
+
rm -f "$FOLLOWUP_BODY_TMPFILE"
|
|
257
|
+
trap - EXIT
|
|
258
|
+
FOLLOWUP_ISSUE_NUMBER=$(gh issue list \
|
|
259
|
+
--state open \
|
|
260
|
+
--search "$FOLLOWUP_TITLE" \
|
|
261
|
+
--json number,title \
|
|
262
|
+
--jq --arg t "$FOLLOWUP_TITLE" '.[] | select(.title == $t) | .number' 2>/dev/null | head -1)
|
|
253
263
|
```
|
|
254
264
|
|
|
255
265
|
### Step 4C: Clean up worktree
|