forgedock 1.0.16 → 1.0.17
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 +21 -3
- package/bin/forgedock.mjs +1992 -540
- package/bin/github-app-manifest.json.license +2 -0
- package/bin/hooks/session-start.mjs +4 -5
- package/bin/registry.mjs +20 -0
- package/bin/tui.mjs +172 -96
- package/commands/adopt.md +433 -0
- package/commands/analytics.md +2 -0
- package/commands/audit-agents.md +2 -0
- package/commands/audit.md +2 -0
- package/commands/changelog.md +354 -0
- package/commands/cleanup.md +2 -0
- package/commands/compat-audit.md +330 -0
- package/commands/deploy-info.md +6 -5
- package/commands/diagnose.md +483 -0
- package/commands/explain.md +349 -0
- package/commands/forge-stats.md +2 -0
- package/commands/forgedock-init.md +2 -0
- package/commands/geo-audit.md +2 -0
- package/commands/incident-response.md +2 -0
- package/commands/issue.md +2 -0
- package/commands/milestone.md +2 -0
- package/commands/optimize.md +411 -0
- package/commands/orchestrate.md +118 -0
- package/commands/pipeline-health.md +60 -1
- package/commands/qa-sweep.md +2 -0
- package/commands/quality-gate.md +37 -0
- package/commands/replay.md +263 -0
- package/commands/resume.md +264 -0
- package/commands/review-pr-agents.md +5 -1
- package/commands/review-pr-staging.md +2 -0
- package/commands/review-pr.md +85 -3
- package/commands/rollback.md +2 -0
- package/commands/scope.md +280 -0
- package/commands/security-audit.md +2 -0
- package/commands/status.md +262 -0
- package/commands/sync-ecosystem.md +2 -0
- package/commands/validate.md +2 -0
- package/commands/work-on/build/architect.md +128 -18
- package/commands/work-on/build/context.md +130 -17
- package/commands/work-on/build/implement.md +2 -0
- package/commands/work-on/build/validate.md +2 -0
- package/commands/work-on/build.md +32 -7
- package/commands/work-on/close.md +85 -0
- package/commands/work-on/decompose.md +2 -0
- package/commands/work-on/investigate.md +30 -3
- package/commands/work-on/review.md +13 -1
- package/commands/work-on-monolithic.md +2 -0
- package/commands/work-on.md +624 -36
- package/docs/BRAND.md +51 -0
- package/docs/CONFIG.md +94 -0
- package/docs/articles/per-repo-adaptive-scripts-token-savings.md +174 -0
- package/docs/claude-breakpoints.json +47 -0
- package/docs/site/getting-started.md +21 -0
- package/package.json +3 -3
- package/scripts/classify-lane.sh +145 -0
- package/scripts/transition-label.sh +161 -0
- package/scripts/validate-pr-target.sh +30 -0
- package/templates/devdocs/README.md +72 -0
- package/templates/devdocs/agent/using-forgedock.md +3 -0
- package/templates/devdocs/agent/using-github.md +3 -0
- package/templates/devdocs/index.yaml +162 -0
- package/templates/devdocs/project/architecture.md +3 -0
- package/templates/devdocs/project/conventions.md +3 -0
- package/templates/devdocs/project/custom-instructions.md +3 -0
- package/templates/devdocs/project/glossary.md +3 -0
- package/templates/devdocs/project/stack.md +3 -0
- package/templates/devdocs/templates/commit-convention.md +3 -0
- package/templates/devdocs/templates/issue.md +3 -0
- package/templates/devdocs/templates/pull-request.md +3 -0
- package/templates/devdocs/templates/review-finding.md +3 -0
package/commands/work-on.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
description: Pick up a GitHub issue and run the full investigate-build-review-merge pipeline
|
|
3
3
|
argument-hint: [issue number or "next" to pick highest priority]
|
|
4
4
|
---
|
|
5
|
+
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
6
|
+
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
5
7
|
|
|
6
8
|
# /work-on — Full Issue Pipeline
|
|
7
9
|
|
|
@@ -37,9 +39,11 @@ Orchestrator for the full issue lifecycle: investigate → decompose (if needed)
|
|
|
37
39
|
| 8 | Phase 6: Close & Cleanup | PR merged | No |
|
|
38
40
|
| 9 | Phase 7: Trajectory | Issue closed | Yes |
|
|
39
41
|
|
|
40
|
-
**Phase 3 sub-phase sequence** (
|
|
42
|
+
**Phase 3 sub-phase sequence** (execute in order; 3C.5 and 3C.6 are conditional — see Phase 3B):
|
|
41
43
|
|
|
42
|
-
3A → 3B → 3C → 3C.5 → 3C.6 → 3D → 3E → 3F → 3F.5 → 3G → 3H → 3I → 3I.5 → 3J → 3K → 3L → 3M
|
|
44
|
+
3A → 3B → [3C → 3C.5* → 3C.6*] → 3D → 3E → 3F → 3F.5 → 3G → 3H → 3I → 3I.5 → 3J → 3K → 3L → 3M
|
|
45
|
+
|
|
46
|
+
*3C.5 and 3C.6 are skipped for TRIVIAL tasks; 3C (Builder Contract) is still required. Investigation tasks exit at 3B before 3C.
|
|
43
47
|
|
|
44
48
|
**Universal continuation rule**: After ANY phase or sub-phase completes, check whether a terminal state has been reached. Terminal states are:
|
|
45
49
|
- `workflow:merged` label is set
|
|
@@ -95,20 +99,158 @@ Satellite repos (those without a `staging` branch) receive fast-lane PRs directl
|
|
|
95
99
|
### 0A: Parse input
|
|
96
100
|
Extract project prefix and issue number. If `next`/`pick`: list open issues sorted by priority, skip `needs-human` and `workflow:decomposed`, pick highest priority.
|
|
97
101
|
|
|
102
|
+
**Optional pre-flight**: Before committing to the full pipeline, run `/scope {NUMBER}` to get a complexity estimate (affected files, blast radius, risk flags, and decomposition recommendation). Especially useful for large or ambiguous issues.
|
|
103
|
+
|
|
104
|
+
### 0A.5: Post Heartbeat Annotation
|
|
105
|
+
|
|
106
|
+
Post a lightweight activity signal immediately after resolving the issue number. This gives the stall detector (orchestrate Step 4B.5) a fresh timestamp to compare against `STALL_TIMEOUT`. Without this, the stall detector can only see the last structured comment (INVESTIGATOR, BUILDER, etc.) which may be hours old during a valid long-running phase.
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
PHASE_START_TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
110
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:HEARTBEAT -->
|
|
111
|
+
**Phase**: Phase 0 — starting pipeline
|
|
112
|
+
**Timestamp**: ${PHASE_START_TIMESTAMP}
|
|
113
|
+
**Issue**: #{NUMBER}"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Also post at major phase entry points** (Phases 1, 3, and 5) — replace `Phase 0` with the correct phase name in each case. These mid-pipeline heartbeats ensure the stall detector sees recent activity during long phases (e.g., a build phase running for 20 minutes is not falsely classified as stalled). Inline snippets are embedded at Phase 1A, Phase 3A, and Phase 5A — agents resuming mid-pipeline encounter them without reading this section. <!-- Added: forge#740 -->
|
|
117
|
+
|
|
118
|
+
**Skip if**: Issue already has a terminal label (`workflow:merged`, `workflow:invalid`, `needs-human`) — no heartbeat needed on a completed issue.
|
|
119
|
+
|
|
98
120
|
### 0B: Load issue + existing context
|
|
99
121
|
```bash
|
|
100
122
|
gh issue view {NUMBER} {GH_FLAG} --json number,title,body,labels,state,comments,milestone
|
|
101
123
|
gh api repos/{GH_REPO}/issues/{NUMBER}/comments --jq '.[] | {id: .id, author: .user.login, body: .body}'
|
|
102
124
|
```
|
|
103
125
|
|
|
104
|
-
**Check**: state (closed → STOP), terminal labels (`workflow:merged`/`workflow:invalid` → STOP), existing agent comments (`FORGE:INVESTIGATOR`, `FORGE:DECOMPOSED`, `FORGE:CONTRACT`, `FORGE:BUILDER`, `FORGE:TRAJECTORY`), parent tracker status, sub-issue status.
|
|
126
|
+
**Check**: state (closed → STOP), terminal labels (`workflow:merged`/`workflow:invalid` → STOP), existing agent comments (`FORGE:INVESTIGATOR`, `FORGE:DECOMPOSED`, `FORGE:CONTRACT`, `FORGE:BUILDER`, `FORGE:TRAJECTORY`, `FORGE:DECISION_RECORD`), parent tracker status, sub-issue status.
|
|
105
127
|
|
|
106
128
|
**Determine resume point**: No comments → Phase 1. Investigation exists + ready-to-build → Phase 3. Builder + no PR → Phase 4. Builder + PR open → Phase 5. PR merged + issue open → Phase 6.
|
|
107
129
|
|
|
130
|
+
### 0B.5: Read Phase Checkpoint (MANDATORY — executes before any phase-skip decision)
|
|
131
|
+
|
|
132
|
+
Query for the latest `<!-- FORGE:CHECKPOINT -->` comment. This is the machine-readable source of truth for the pipeline's current phase position — it takes priority over all prose-based resume heuristics above.
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
CHECKPOINT=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
136
|
+
--jq '[.[] | select(.body | contains("FORGE:CHECKPOINT"))] | last | .body // ""')
|
|
137
|
+
|
|
138
|
+
if [ -n "$CHECKPOINT" ]; then
|
|
139
|
+
# Extract next_phase from the JSON block inside the comment
|
|
140
|
+
NEXT_PHASE=$(echo "$CHECKPOINT" | grep -A5 '```json' | grep '"next_phase"' \
|
|
141
|
+
| sed -n 's/.*"next_phase": "\([^"]*\)".*/\1/p')
|
|
142
|
+
echo "Checkpoint found: next_phase=${NEXT_PHASE}"
|
|
143
|
+
fi
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Routing from checkpoint** (overrides prose heuristics above when a checkpoint exists):
|
|
147
|
+
|
|
148
|
+
| `next_phase` value | Resume at |
|
|
149
|
+
|--------------------|-----------|
|
|
150
|
+
| `BUILD` | Phase 3 (skip Phase 1 investigation) |
|
|
151
|
+
| `DECOMPOSE` | Phase 2 (skip Phase 1 investigation) |
|
|
152
|
+
| `REVIEW` | Phase 4 (skip Phase 1–3) |
|
|
153
|
+
| `CLOSE` | Phase 6 (skip Phase 1–5) |
|
|
154
|
+
| *(absent or unrecognized)* | Fall back to prose heuristics above |
|
|
155
|
+
|
|
156
|
+
**If no checkpoint exists**: fall back to prose resume heuristics in Phase 0B above — treat as fresh start at Phase 1.
|
|
157
|
+
|
|
108
158
|
**Classify lane**: Milestone → feature lane (`milestone/{slug}`). No milestone → fast lane (`staging`).
|
|
109
159
|
|
|
110
160
|
**Source branch for review-findings**: Parse `**Code branch**: \`{branch}\`` from body. Branch from there, not main.
|
|
111
161
|
|
|
162
|
+
**Script resolution** — Use the following `resolve_script()` function whenever calling a pipeline script. It enforces the 4-level precedence hierarchy (see `devdocs/project/architecture.md → Script Precedence`):
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
ADAPTIVE_DIR="${REPO_PATH}/$(yq '.adaptive_scripts.directory // ".forgedock/scripts"' forge.yaml 2>/dev/null || echo '.forgedock/scripts')"
|
|
166
|
+
ADAPTIVE_ENABLED=$(yq '.adaptive_scripts.enabled // "true"' forge.yaml 2>/dev/null || echo 'true')
|
|
167
|
+
UNIVERSAL_DIR="${FORGEDOCK_HOME:-$(dirname "$(which classify-lane.sh 2>/dev/null || echo 'scripts')")}/scripts"
|
|
168
|
+
|
|
169
|
+
resolve_script() {
|
|
170
|
+
local operation="$1"
|
|
171
|
+
# Tier 2: per-repo adaptive (skip if disabled)
|
|
172
|
+
if [ "$ADAPTIVE_ENABLED" != "false" ] && [ -f "${ADAPTIVE_DIR}/${operation}.sh" ]; then
|
|
173
|
+
echo "adaptive:${ADAPTIVE_DIR}/${operation}.sh"
|
|
174
|
+
return
|
|
175
|
+
fi
|
|
176
|
+
# Tier 3: universal script
|
|
177
|
+
if [ -f "${UNIVERSAL_DIR}/${operation}.sh" ]; then
|
|
178
|
+
echo "universal:${UNIVERSAL_DIR}/${operation}.sh"
|
|
179
|
+
return
|
|
180
|
+
fi
|
|
181
|
+
# Tier 4: prose fallback
|
|
182
|
+
echo "prose:"
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
# Canonical tier-dispatch usage pattern — inline at every resolve_script() call site:
|
|
186
|
+
#
|
|
187
|
+
# There is no centralised run_script() function. The pattern below is inlined
|
|
188
|
+
# directly at each call site because each operation has a different prose
|
|
189
|
+
# fallback. Copy and adapt this block wherever resolve_script() is called.
|
|
190
|
+
#
|
|
191
|
+
# Usage pattern at each call site:
|
|
192
|
+
# RESOLUTION=$(resolve_script 'op')
|
|
193
|
+
# TIER="${RESOLUTION%%:*}"
|
|
194
|
+
# SCRIPT_PATH="${RESOLUTION#*:}"
|
|
195
|
+
# case "$TIER" in
|
|
196
|
+
# adaptive|universal) bash "$SCRIPT_PATH" ARGS ;;
|
|
197
|
+
# prose) # inline fallback here ;;
|
|
198
|
+
# esac
|
|
199
|
+
#
|
|
200
|
+
# The case pattern is inlined at every call site (rather than centralised here)
|
|
201
|
+
# because each operation has a different prose fallback — transition-label falls
|
|
202
|
+
# back to inline gh issue edit; classify-lane has no valid prose fallback and
|
|
203
|
+
# must exit 1; validate-pr-target emits a WARNING and continues (the PR review
|
|
204
|
+
# step catches any mismatch before merge). <!-- Added: forge#822 -->
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
When invoking a resolved script, log the tier in the FORGE annotation: `Script tier: {adaptive|universal|prose} ({path})`. This provides full pipeline observability. <!-- Added: forge#670 -->
|
|
208
|
+
|
|
209
|
+
### 0B.1: Apply learned overrides (MANDATORY — run after 0B, before any routing)
|
|
210
|
+
|
|
211
|
+
Read `forge.yaml → learned:` and override runtime variables. If the `learned:` key is absent or empty, all steps below are no-ops — continue to 0C.
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
# Read learned section — all reads use // "" fallback so absent keys are silent no-ops
|
|
215
|
+
LEARNED_STAGING=$(yq '.learned.branch_targets.staging // ""' forge.yaml 2>/dev/null || echo '')
|
|
216
|
+
LEARNED_TEST_COMMANDS=$(yq '.learned.test_commands // []' forge.yaml 2>/dev/null || echo '[]')
|
|
217
|
+
LEARNED_LABEL_MAP=$(yq '.learned.label_map // {}' forge.yaml 2>/dev/null || echo '{}')
|
|
218
|
+
LEARNED_COMMIT_STYLE=$(yq '.learned.commit_style // ""' forge.yaml 2>/dev/null || echo '')
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Apply overrides**:
|
|
222
|
+
|
|
223
|
+
1. **Branch target override** — If `LEARNED_STAGING` is non-empty, replace `STAGING_BRANCH` with its value:
|
|
224
|
+
```bash
|
|
225
|
+
[ -n "$LEARNED_STAGING" ] && STAGING_BRANCH="$LEARNED_STAGING" && \
|
|
226
|
+
echo "Learned override: STAGING_BRANCH → $STAGING_BRANCH (from learned.branch_targets.staging)"
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
2. **Test commands** — Store `LEARNED_TEST_COMMANDS` for use in Phase 3H (validate). These are appended to the `verification.commands` runs, not replaced:
|
|
230
|
+
```bash
|
|
231
|
+
# Pass LEARNED_TEST_COMMANDS to Phase 3H as additional commands to run after verification.commands
|
|
232
|
+
# (consumed in 3H — store as env var or carry forward in context)
|
|
233
|
+
echo "Learned test commands: $LEARNED_TEST_COMMANDS"
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
3. **Label map** — If `LEARNED_LABEL_MAP` is non-empty, export it as `FORGE_LABEL_MAP` so that all subsequent `resolve_script 'transition-label'` invocations (which are child processes) can read it. The script performs the substitution internally: if the canonical label (e.g. `workflow:investigating`) appears as a key in the map, it uses the mapped value instead.
|
|
237
|
+
```bash
|
|
238
|
+
# Export as FORGE_LABEL_MAP so child processes (resolve_script 'transition-label') can read it.
|
|
239
|
+
# All 8 resolve_script 'transition-label' call sites in this command inherit this env var automatically.
|
|
240
|
+
# The script substitutes the canonical workflow:* label with the mapped value when found.
|
|
241
|
+
export FORGE_LABEL_MAP="$LEARNED_LABEL_MAP"
|
|
242
|
+
[ -n "$LEARNED_LABEL_MAP" ] && [ "$LEARNED_LABEL_MAP" != "{}" ] && \
|
|
243
|
+
echo "Learned override: FORGE_LABEL_MAP active — label_map will be applied by resolve_script 'transition-label'"
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
4. **Commit style** — If `LEARNED_COMMIT_STYLE` is non-empty, use it in Phase 3M:
|
|
247
|
+
```bash
|
|
248
|
+
[ -n "$LEARNED_COMMIT_STYLE" ] && COMMIT_STYLE="$LEARNED_COMMIT_STYLE" && \
|
|
249
|
+
echo "Learned override: COMMIT_STYLE → $COMMIT_STYLE"
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
<!-- Added: forge#667 — learned section reader -->
|
|
253
|
+
|
|
112
254
|
### 0C: Sync to Project board
|
|
113
255
|
Add issue to project, set Status=In Progress, Lane, Component, Priority, Workflow=Investigating.
|
|
114
256
|
|
|
@@ -127,8 +269,24 @@ gh api repos/{GH_REPO}/issues/comments/$COMMENT_ID -X DELETE
|
|
|
127
269
|
|
|
128
270
|
### 1A: Set label
|
|
129
271
|
```bash
|
|
130
|
-
|
|
131
|
-
|
|
272
|
+
RESOLUTION=$(resolve_script 'transition-label')
|
|
273
|
+
TIER="${RESOLUTION%%:*}"; SCRIPT_PATH="${RESOLUTION#*:}"
|
|
274
|
+
case "$TIER" in
|
|
275
|
+
adaptive|universal) bash "$SCRIPT_PATH" {NUMBER} {GH_FLAG} investigating ;;
|
|
276
|
+
prose)
|
|
277
|
+
gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:investigating" \
|
|
278
|
+
--remove-label "workflow:ready-to-build,workflow:building,workflow:in-review,workflow:merged,workflow:invalid,workflow:decomposed" 2>/dev/null || true
|
|
279
|
+
;;
|
|
280
|
+
esac
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Post Phase 1 heartbeat** (skip if issue already has a terminal label — `workflow:merged`, `workflow:invalid`, `needs-human`):
|
|
284
|
+
```bash
|
|
285
|
+
PHASE_START_TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
286
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:HEARTBEAT -->
|
|
287
|
+
**Phase**: Phase 1 — Investigation
|
|
288
|
+
**Timestamp**: ${PHASE_START_TIMESTAMP}
|
|
289
|
+
**Issue**: #{NUMBER}"
|
|
132
290
|
```
|
|
133
291
|
|
|
134
292
|
### 1A.5: Normalize Issue Body (MANDATORY)
|
|
@@ -222,7 +380,7 @@ Use `forge.yaml → review.tech_stack` and the issue domain labels to identify e
|
|
|
222
380
|
4.5. **Rogue commit pre-state comparison (conditional)**: If the issue body references a specific commit as rogue, bad, or unintended (e.g., "rogue commit `abc1234`", "bad commit", "this was never intended"), MUST run `git show {commit}^:{file}` to see the file before that commit. Compare the pre-commit state against the current file. Any block present in the current file but absent in the pre-commit state was introduced by that commit chain and is a candidate for full reversion — not just partial editing. Report the delta (pre vs. current) in the investigation report. Do NOT assume surrounding code near a named import/bug is correct simply because the issue only named a specific sub-problem. (Ref: forge#278 — investigator confirmed the broken import but never ran `git show 18a3a2cf3^:batch.py`; the surrounding 50-line feature gate was also rogue and was preserved by the fix PR, causing a P1 access regression for all non-Scale users)
|
|
223
381
|
5. Domain context discovery (narrow scope only, 1–5 files):
|
|
224
382
|
```bash
|
|
225
|
-
git log --oneline --all -30 -- {affected_files} | grep -
|
|
383
|
+
git log --oneline --all -30 -- {affected_files} | grep -oE '#[0-9]+' | sort -u
|
|
226
384
|
gh issue list -R {GH_REPO} --state closed --limit 8 --search "{function_name}"
|
|
227
385
|
```
|
|
228
386
|
Keep only file/function-level overlap. Max 5 related issues.
|
|
@@ -282,28 +440,149 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:INVESTIGATOR -->
|
|
|
282
440
|
<!-- INVESTIGATION:COMPLETE -->"
|
|
283
441
|
```
|
|
284
442
|
|
|
443
|
+
### 1D: Correction capture (MANDATORY — run before label update)
|
|
444
|
+
|
|
445
|
+
Before routing, scan all non-agent comments for correction signals from the repository owner. Correction signals are owner comments that contain phrases like "no, use", "actually use", "use X instead", "not X, use Y", or "wrong branch". If found, write the correction to `forge.yaml → learned:` and emit a `FORGE:LEARNED` annotation.
|
|
446
|
+
|
|
447
|
+
**Scan for correction signals**:
|
|
448
|
+
```bash
|
|
449
|
+
# Get repo owner login for filtering.
|
|
450
|
+
# Tiered resolution — necessary because project.owner is the GitHub org/user NAME,
|
|
451
|
+
# but comment .user.login is always a personal account login. For org-owned repos
|
|
452
|
+
# these are structurally different (e.g. org="RapierCraftStudios", commenter="mrdubey"),
|
|
453
|
+
# so using project.owner directly silently disables correction capture for all org repos.
|
|
454
|
+
#
|
|
455
|
+
# Resolution order:
|
|
456
|
+
# 1. project.owner_login (explicit override — required for org repos where owner ≠ personal login)
|
|
457
|
+
# 2. gh api repos/{GH_REPO} --jq '.owner.login' (auto-resolves correctly for personal repos)
|
|
458
|
+
# 3. project.owner (backward-compat fallback — still broken for org repos, but avoids hard failure)
|
|
459
|
+
REPO_OWNER=$(yq '.project.owner_login // ""' forge.yaml 2>/dev/null || echo '')
|
|
460
|
+
if [ -z "$REPO_OWNER" ]; then
|
|
461
|
+
REPO_OWNER=$(gh api repos/{GH_REPO} --jq '.owner.login' 2>/dev/null || echo '')
|
|
462
|
+
fi
|
|
463
|
+
if [ -z "$REPO_OWNER" ]; then
|
|
464
|
+
REPO_OWNER=$(yq '.project.owner' forge.yaml 2>/dev/null || echo '')
|
|
465
|
+
fi
|
|
466
|
+
|
|
467
|
+
# Fetch all comments, filter to owner-only, look for correction signals
|
|
468
|
+
CORRECTIONS=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
469
|
+
| jq -r --arg owner "$REPO_OWNER" \
|
|
470
|
+
'.[] | select(.user.login == $owner) | select(
|
|
471
|
+
(.body | test("no,? use|actually use|use .+ instead|not .+, use|wrong branch"; "i"))
|
|
472
|
+
) | .body' 2>/dev/null || echo '')
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
**If correction signals found** — extract and write each correction:
|
|
476
|
+
|
|
477
|
+
```bash
|
|
478
|
+
# Example: extract branch target correction "use develop not staging"
|
|
479
|
+
# Adjust regex to the correction pattern detected
|
|
480
|
+
|
|
481
|
+
if [ -n "$CORRECTIONS" ]; then
|
|
482
|
+
echo "Correction signals detected — writing to forge.yaml → learned:"
|
|
483
|
+
echo "$CORRECTIONS"
|
|
484
|
+
|
|
485
|
+
# Write to forge.yaml using yq in-place merge (idempotent — yq merge overwrites existing keys)
|
|
486
|
+
# Always use env variable injection to avoid YAML injection from comment content
|
|
487
|
+
# Example for branch target correction:
|
|
488
|
+
# BRANCH_VALUE="develop"
|
|
489
|
+
# yq eval '.learned.branch_targets.staging = env(BRANCH_VALUE)' -i forge.yaml
|
|
490
|
+
|
|
491
|
+
# After writing, emit FORGE:LEARNED annotation
|
|
492
|
+
LEARNED_KEYS="branch_targets.staging" # replace with actual extracted keys
|
|
493
|
+
CAPTURED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
494
|
+
|
|
495
|
+
# Update captured_at and captured_by metadata
|
|
496
|
+
CAPTURED_AT_VAL="$CAPTURED_AT" yq eval '.learned.captured_at = env(CAPTURED_AT_VAL)' -i forge.yaml
|
|
497
|
+
CAPTURED_BY_VAL="work-on/{NUMBER}" yq eval '.learned.captured_by = env(CAPTURED_BY_VAL)' -i forge.yaml
|
|
498
|
+
|
|
499
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:LEARNED -->
|
|
500
|
+
## Learned Pattern Captured
|
|
501
|
+
|
|
502
|
+
**Source**: Owner correction in comment on issue #{NUMBER}
|
|
503
|
+
**Captured at**: $CAPTURED_AT
|
|
504
|
+
**Keys written**: \`$LEARNED_KEYS\`
|
|
505
|
+
|
|
506
|
+
The following project-specific pattern was detected from owner feedback and written to \`forge.yaml → learned:\`. Future sessions will use this override automatically (read in Phase 0B.1).
|
|
507
|
+
|
|
508
|
+
\`\`\`yaml
|
|
509
|
+
# Written to forge.yaml
|
|
510
|
+
learned:
|
|
511
|
+
# {key}: {value}
|
|
512
|
+
\`\`\`
|
|
513
|
+
|
|
514
|
+
**Idempotency**: yq merge-write — re-running will not duplicate entries."
|
|
515
|
+
|
|
516
|
+
echo "FORGE:LEARNED annotation posted."
|
|
517
|
+
fi
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
**Idempotency guarantee**: Use `yq eval '.learned.key = env(VAR)' -i forge.yaml` — yq overwrites existing keys rather than appending. Re-running the capture step on the same comment produces the same forge.yaml state. <!-- Added: forge#667 -->
|
|
521
|
+
|
|
285
522
|
### 1D: Update labels & route
|
|
286
523
|
|
|
287
524
|
**CONFIRMED or PARTIAL with decompose: NO**:
|
|
288
525
|
```bash
|
|
289
|
-
|
|
526
|
+
RESOLUTION=$(resolve_script 'transition-label')
|
|
527
|
+
TIER="${RESOLUTION%%:*}"; SCRIPT_PATH="${RESOLUTION#*:}"
|
|
528
|
+
case "$TIER" in
|
|
529
|
+
adaptive|universal) bash "$SCRIPT_PATH" {NUMBER} {GH_FLAG} ready-to-build ;;
|
|
530
|
+
prose)
|
|
531
|
+
gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:ready-to-build" \
|
|
532
|
+
--remove-label "workflow:investigating,workflow:building,workflow:in-review,workflow:merged,workflow:invalid,workflow:decomposed" 2>/dev/null || true
|
|
533
|
+
;;
|
|
534
|
+
esac
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
Write machine-readable phase checkpoint (MUST execute immediately after label transition, before continuing):
|
|
538
|
+
```bash
|
|
539
|
+
CHECKPOINT_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
540
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:CHECKPOINT -->
|
|
541
|
+
\`\`\`json
|
|
542
|
+
{\"phase\": \"INVESTIGATION\", \"status\": \"COMPLETE\", \"next_phase\": \"BUILD\", \"timestamp\": \"${CHECKPOINT_TIMESTAMP}\"}
|
|
543
|
+
\`\`\`"
|
|
290
544
|
```
|
|
291
545
|
<!-- FORGE:PHASE_COMPLETE — Investigation routed to build. See Universal Phase Dispatcher: next phase is Phase 3. Not terminal — continue immediately. -->
|
|
292
546
|
→ Continue to Phase 3.
|
|
293
547
|
|
|
294
548
|
**CONFIRMED or PARTIAL with decompose: YES**:
|
|
295
549
|
```bash
|
|
296
|
-
|
|
550
|
+
RESOLUTION=$(resolve_script 'transition-label')
|
|
551
|
+
TIER="${RESOLUTION%%:*}"; SCRIPT_PATH="${RESOLUTION#*:}"
|
|
552
|
+
case "$TIER" in
|
|
553
|
+
adaptive|universal) bash "$SCRIPT_PATH" {NUMBER} {GH_FLAG} decomposed ;;
|
|
554
|
+
prose)
|
|
555
|
+
gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:decomposed" \
|
|
556
|
+
--remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:in-review,workflow:merged,workflow:invalid" 2>/dev/null || true
|
|
557
|
+
;;
|
|
558
|
+
esac
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
Write machine-readable phase checkpoint (MUST execute immediately after label transition, before continuing):
|
|
562
|
+
```bash
|
|
563
|
+
CHECKPOINT_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
564
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:CHECKPOINT -->
|
|
565
|
+
\`\`\`json
|
|
566
|
+
{\"phase\": \"INVESTIGATION\", \"status\": \"COMPLETE\", \"next_phase\": \"DECOMPOSE\", \"timestamp\": \"${CHECKPOINT_TIMESTAMP}\"}
|
|
567
|
+
\`\`\`"
|
|
297
568
|
```
|
|
298
569
|
<!-- FORGE:PHASE_COMPLETE — Investigation routed to decomposition. See Universal Phase Dispatcher: next phase is Phase 2. Not terminal — continue immediately. -->
|
|
299
570
|
→ Continue to Phase 2 (Decomposition).
|
|
300
571
|
|
|
301
572
|
**INVALID**:
|
|
302
573
|
```bash
|
|
303
|
-
|
|
574
|
+
RESOLUTION=$(resolve_script 'transition-label')
|
|
575
|
+
TIER="${RESOLUTION%%:*}"; SCRIPT_PATH="${RESOLUTION#*:}"
|
|
576
|
+
case "$TIER" in
|
|
577
|
+
adaptive|universal) bash "$SCRIPT_PATH" {NUMBER} {GH_FLAG} invalid ;;
|
|
578
|
+
prose)
|
|
579
|
+
gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:invalid" \
|
|
580
|
+
--remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:in-review,workflow:merged,workflow:decomposed" 2>/dev/null || true
|
|
581
|
+
;;
|
|
582
|
+
esac
|
|
304
583
|
gh issue close {NUMBER} {GH_FLAG} --comment "Closing as invalid: {reason from investigation}"
|
|
305
584
|
```
|
|
306
|
-
→ STOP.
|
|
585
|
+
→ STOP. No checkpoint written — INVALID is terminal.
|
|
307
586
|
|
|
308
587
|
---
|
|
309
588
|
|
|
@@ -398,9 +677,15 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:DECOMPOSED -->
|
|
|
398
677
|
|
|
399
678
|
### 2F: Update labels
|
|
400
679
|
```bash
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
680
|
+
RESOLUTION=$(resolve_script 'transition-label')
|
|
681
|
+
TIER="${RESOLUTION%%:*}"; SCRIPT_PATH="${RESOLUTION#*:}"
|
|
682
|
+
case "$TIER" in
|
|
683
|
+
adaptive|universal) bash "$SCRIPT_PATH" {NUMBER} {GH_FLAG} decomposed ;;
|
|
684
|
+
prose)
|
|
685
|
+
gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:decomposed" \
|
|
686
|
+
--remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:in-review,workflow:merged,workflow:invalid" 2>/dev/null || true
|
|
687
|
+
;;
|
|
688
|
+
esac
|
|
404
689
|
```
|
|
405
690
|
|
|
406
691
|
→ STOP. Each sub-issue runs its own `/work-on`.
|
|
@@ -413,9 +698,19 @@ gh issue edit {NUMBER} {GH_FLAG} \
|
|
|
413
698
|
|
|
414
699
|
**Skip if**: `<!-- FORGE:BUILDER -->` exists.
|
|
415
700
|
|
|
416
|
-
**CRITICAL: You MUST execute ALL sub-phases 3A–3M in order.
|
|
701
|
+
**CRITICAL: You MUST execute ALL sub-phases 3A–3M in order. Sub-phases 3C.5 (context) and 3C.6 (architect) are skipped ONLY for TRIVIAL tasks and Investigation tasks — see Phase 3B for classification. For STANDARD and COMPLEX tasks they post mandatory `FORGE:CONTEXT` and `FORGE:ARCHITECT` comments that Phase 3F reads as its primary input. Skipping them without a TRIVIAL/Investigation classification degrades build quality and causes review findings. After each sub-phase, continue to the next — no sub-phase is terminal.**
|
|
417
702
|
|
|
418
703
|
### 3A: Re-read state from GitHub (MANDATORY)
|
|
704
|
+
|
|
705
|
+
**Post Phase 3 heartbeat** (skip if issue already has a terminal label — `workflow:merged`, `workflow:invalid`, `needs-human`):
|
|
706
|
+
```bash
|
|
707
|
+
PHASE_START_TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
708
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:HEARTBEAT -->
|
|
709
|
+
**Phase**: Phase 3 — Build
|
|
710
|
+
**Timestamp**: ${PHASE_START_TIMESTAMP}
|
|
711
|
+
**Issue**: #{NUMBER}"
|
|
712
|
+
```
|
|
713
|
+
|
|
419
714
|
```bash
|
|
420
715
|
gh issue view {NUMBER} {GH_FLAG} --json number,title,body,labels,state,milestone
|
|
421
716
|
|
|
@@ -426,17 +721,23 @@ gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
|
426
721
|
# Check if build already completed
|
|
427
722
|
gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
428
723
|
--jq '.[] | select(.body | contains("FORGE:BUILDER")) | .body'
|
|
724
|
+
|
|
725
|
+
# Check for existing COMPLEXITY_BAND from a prior run (resume path)
|
|
726
|
+
EXISTING_FAST_PATH=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
727
|
+
--jq '.[] | select(.body | contains("FORGE:FAST_PATH")) | .body' 2>/dev/null | head -1)
|
|
429
728
|
```
|
|
430
729
|
|
|
431
730
|
If no investigation comment with `<!-- INVESTIGATION:COMPLETE -->` → STOP (investigation not complete).
|
|
432
731
|
|
|
433
732
|
Extract from investigation: affected files, root cause, recommendation, task type.
|
|
434
733
|
|
|
435
|
-
### 3B: Classify task type
|
|
734
|
+
### 3B: Classify task type and complexity
|
|
735
|
+
|
|
736
|
+
**Step 1 — Task type classification:**
|
|
436
737
|
|
|
437
738
|
| Signal | Type | Approach |
|
|
438
739
|
|--------|------|----------|
|
|
439
|
-
| Title
|
|
740
|
+
| Title starts with "Investigate:"/"Audit:"/"Research:" | Investigation | Produce issues as deliverables |
|
|
440
741
|
| UI/UX, feature + web/ files | UI/UX | `frontend-design` skill |
|
|
441
742
|
| Feature + services/ | Backend Feature | Implement directly |
|
|
442
743
|
| Feature + both | Full-Stack | Backend first, then frontend-design |
|
|
@@ -444,7 +745,49 @@ Extract from investigation: affected files, root cause, recommendation, task typ
|
|
|
444
745
|
| Bug + services/ | Backend Fix | Direct |
|
|
445
746
|
| Refactor/docs | Maintenance | Direct |
|
|
446
747
|
|
|
447
|
-
**Investigation tasks
|
|
748
|
+
**Investigation tasks — early exit (BEFORE Phase 3C):** If task type = Investigation, skip Phases 3C, 3C.5, and 3C.6 entirely. Post `<!-- FORGE:FAST_PATH -->` comment, then jump directly to Phase 3F (implement → issue creation path). Do NOT run the Builder Contract, Context Gathering, or Architecture Plan for investigation tasks.
|
|
749
|
+
|
|
750
|
+
```bash
|
|
751
|
+
# Post fast-path comment for investigation tasks
|
|
752
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:FAST_PATH -->
|
|
753
|
+
## Fast-Path Classification
|
|
754
|
+
|
|
755
|
+
**COMPLEXITY_BAND**: INVESTIGATION
|
|
756
|
+
**Task type**: Investigation
|
|
757
|
+
**Rationale**: Title prefix 'Investigate:' (or task type = Investigation from investigator report) — skipping Builder Contract (3C), Context Gathering (3C.5), and Architecture Plan (3C.6). Jumping directly to Phase 3F (issue creation).
|
|
758
|
+
**Phases skipped**: 3C, 3C.5, 3C.6"
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
→ Jump to Phase 3F immediately. Do not continue to Phase 3C.
|
|
762
|
+
|
|
763
|
+
**Step 2 — Complexity classification (for non-Investigation tasks):**
|
|
764
|
+
|
|
765
|
+
Classify COMPLEXITY_BAND based on affected file count and task nature:
|
|
766
|
+
|
|
767
|
+
| Condition | COMPLEXITY_BAND |
|
|
768
|
+
|-----------|-----------------|
|
|
769
|
+
| Single file, doc/config/markdown only, no logic changes expected | TRIVIAL |
|
|
770
|
+
| 1–5 files, existing patterns, no cross-service impact | STANDARD |
|
|
771
|
+
| 6+ files, new abstractions, cross-service, migration, schema changes | COMPLEX |
|
|
772
|
+
|
|
773
|
+
Post `<!-- FORGE:FAST_PATH -->` comment immediately after classification:
|
|
774
|
+
|
|
775
|
+
```bash
|
|
776
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:FAST_PATH -->
|
|
777
|
+
## Fast-Path Classification
|
|
778
|
+
|
|
779
|
+
**COMPLEXITY_BAND**: {TRIVIAL|STANDARD|COMPLEX}
|
|
780
|
+
**Task type**: {TASK_TYPE}
|
|
781
|
+
**Affected file count**: {N}
|
|
782
|
+
**Rationale**: {one-sentence explanation of classification decision}
|
|
783
|
+
**Phases skipped**: {list phases skipped, or 'none — full pipeline' for STANDARD/COMPLEX}"
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
**Resume path**: If `EXISTING_FAST_PATH` was read in Phase 3A, extract COMPLEXITY_BAND from it and skip re-classification.
|
|
787
|
+
|
|
788
|
+
**TRIVIAL tasks**: After posting FORGE:FAST_PATH, skip Phase 3C.5 (Context Gathering) and Phase 3C.6 (Architecture Plan) only. Phase 3C (Builder Contract) is **retained** — it still runs. Continue: 3C (Builder Contract) → 3D → 3E → 3F → 3F.5 → 3G → 3H onward. When filling in **Phases skipped** in the FORGE:FAST_PATH comment, write: `3C.5, 3C.6`.
|
|
789
|
+
|
|
790
|
+
**STANDARD and COMPLEX tasks**: Run full pipeline — 3C → 3C.5 → 3C.6 → 3D onward. No phases skipped.
|
|
448
791
|
|
|
449
792
|
### 3C: Builder Contract (MANDATORY)
|
|
450
793
|
|
|
@@ -476,9 +819,11 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:CONTRACT -->
|
|
|
476
819
|
|
|
477
820
|
Contract must be grounded in the investigation report. Adversarially validate proposed fixes against adjacent system layers.
|
|
478
821
|
|
|
479
|
-
### 3C.5: Context Gathering (MANDATORY —
|
|
822
|
+
### 3C.5: Context Gathering (MANDATORY for STANDARD/COMPLEX — skip for TRIVIAL)
|
|
480
823
|
|
|
481
|
-
**
|
|
824
|
+
**Skip if COMPLEXITY_BAND: TRIVIAL** (classified in Phase 3B) — post nothing, proceed directly to Phase 3C.6. Trivial single-file changes have no institutional memory value to surface.
|
|
825
|
+
|
|
826
|
+
**For STANDARD and COMPLEX tasks**: This phase is NOT optional. Run it regardless. Do NOT skip it without a TRIVIAL classification from Phase 3B.
|
|
482
827
|
|
|
483
828
|
Surface institutional memory before writing code. Extract function names from the contract deliverables table:
|
|
484
829
|
|
|
@@ -486,7 +831,7 @@ Surface institutional memory before writing code. Extract function names from th
|
|
|
486
831
|
FUNCTION_NAMES=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
487
832
|
--jq '.[] | select(.body | contains("FORGE:CONTRACT")) | .body' \
|
|
488
833
|
| awk '/^### Deliverables/{p=1; next} /^### /{p=0} p' \
|
|
489
|
-
| grep -
|
|
834
|
+
| grep -oE '`[A-Za-z_][A-Za-z0-9_]*`' \
|
|
490
835
|
| tr -d '`' | sort -u | tr '\n' ' ' | xargs)
|
|
491
836
|
```
|
|
492
837
|
|
|
@@ -511,7 +856,7 @@ done
|
|
|
511
856
|
|
|
512
857
|
**C2: Past Bugs in the Same Module**
|
|
513
858
|
```bash
|
|
514
|
-
git log --oneline -30 -- {AFFECTED_FILES} | grep -
|
|
859
|
+
git log --oneline -30 -- {AFFECTED_FILES} | grep -oE '#[0-9]+' | sort -u | head -8
|
|
515
860
|
# For each issue: fetch title + root cause, keep only bug/fix/review-finding labeled. Max 5.
|
|
516
861
|
```
|
|
517
862
|
|
|
@@ -557,13 +902,15 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:CONTEXT -->
|
|
|
557
902
|
|
|
558
903
|
If total time exceeds 2 minutes, post partial results with `<!-- FORGE:CONTEXT:PARTIAL -->`.
|
|
559
904
|
|
|
560
|
-
### 3C.6: Architecture Plan (MANDATORY)
|
|
905
|
+
### 3C.6: Architecture Plan (MANDATORY for STANDARD/COMPLEX — skip for TRIVIAL)
|
|
906
|
+
|
|
907
|
+
**Skip if COMPLEXITY_BAND: TRIVIAL** (classified in Phase 3B) — post nothing, proceed directly to Phase 3D. Trivial single-file changes have no multi-path consistency risk.
|
|
561
908
|
|
|
562
|
-
**This phase is NOT optional
|
|
909
|
+
**For STANDARD and COMPLEX tasks**: This phase is NOT optional. Always run it. Even a 1-file STANDARD fix benefits from cross-path consistency checks. Do NOT skip without a TRIVIAL classification from Phase 3B.
|
|
563
910
|
|
|
564
911
|
Trace ALL affected code paths before writing code.
|
|
565
912
|
|
|
566
|
-
**
|
|
913
|
+
**Additional skip condition** (STANDARD tasks only): Issue creates ONLY new files with no existing callers AND title starts with "docs:" or "chore:".
|
|
567
914
|
|
|
568
915
|
**A1: Read Entry Points** — For each affected file: identify the primary function, all callers (grep), and sibling implementations. Read 3–5 most relevant files, max 8 total.
|
|
569
916
|
|
|
@@ -633,25 +980,57 @@ If budget exceeded (3 min), use `<!-- FORGE:ARCHITECT:PARTIAL -->`.
|
|
|
633
980
|
|
|
634
981
|
### 3D: Set building label
|
|
635
982
|
```bash
|
|
636
|
-
|
|
983
|
+
RESOLUTION=$(resolve_script 'transition-label')
|
|
984
|
+
TIER="${RESOLUTION%%:*}"; SCRIPT_PATH="${RESOLUTION#*:}"
|
|
985
|
+
case "$TIER" in
|
|
986
|
+
adaptive|universal) bash "$SCRIPT_PATH" {NUMBER} {GH_FLAG} building ;;
|
|
987
|
+
prose)
|
|
988
|
+
gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:building" \
|
|
989
|
+
--remove-label "workflow:investigating,workflow:ready-to-build,workflow:in-review,workflow:merged,workflow:invalid,workflow:decomposed" 2>/dev/null || true
|
|
990
|
+
;;
|
|
991
|
+
esac
|
|
637
992
|
```
|
|
638
993
|
|
|
639
994
|
### 3E: Create worktree
|
|
640
995
|
|
|
641
996
|
Branch slug from title (lowercase, hyphenated, max 40 chars). Prefix: `fix/` (bugs) or `feat/` (features).
|
|
642
997
|
|
|
998
|
+
**Compute `PR_BASE` before worktree creation** — the source branch for the worktree MUST match the PR target. Compute `PR_BASE` now using `classify-lane.sh` so both worktree creation and Phase 4C use the same deterministic value. <!-- Added: forge#639 -->
|
|
999
|
+
|
|
1000
|
+
```bash
|
|
1001
|
+
# Compute PR_BASE deterministically from issue milestone — no LLM interpretation
|
|
1002
|
+
RESOLUTION=$(resolve_script 'classify-lane')
|
|
1003
|
+
TIER="${RESOLUTION%%:*}"; SCRIPT_PATH="${RESOLUTION#*:}"
|
|
1004
|
+
case "$TIER" in
|
|
1005
|
+
adaptive|universal)
|
|
1006
|
+
if ! PR_BASE=$(bash "$SCRIPT_PATH" {NUMBER} -R {GH_REPO}); then
|
|
1007
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "BLOCKER: classify-lane.sh failed to compute PR target — see script error above. Adding needs-human."
|
|
1008
|
+
gh issue edit {NUMBER} {GH_FLAG} --add-label "needs-human"
|
|
1009
|
+
exit 1
|
|
1010
|
+
fi
|
|
1011
|
+
;;
|
|
1012
|
+
prose)
|
|
1013
|
+
# classify-lane has no valid prose fallback — the script output is authoritative.
|
|
1014
|
+
# Without it, PR target cannot be determined safely. Add needs-human and stop.
|
|
1015
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "BLOCKER: classify-lane.sh not installed (prose tier). Cannot compute PR target deterministically. Adding needs-human."
|
|
1016
|
+
gh issue edit {NUMBER} {GH_FLAG} --add-label "needs-human"
|
|
1017
|
+
exit 1
|
|
1018
|
+
;;
|
|
1019
|
+
esac
|
|
1020
|
+
```
|
|
1021
|
+
|
|
643
1022
|
**Determine source branch**:
|
|
644
1023
|
- Review-finding → parse `**Code branch**: \`{branch}\`` from issue body; branch from `origin/{branch}`
|
|
645
1024
|
- **Milestone review-finding hybrid lane** (Code branch matches `milestone/*`): High-risk lane. NEVER use `git merge` to resolve conflicts — use `git rebase` or `git cherry-pick` only. If conflicts can't be resolved without merge, post comment, add `needs-human`, STOP.
|
|
646
|
-
- Feature lane (has milestone) → branch from `origin/{PR_BASE}`
|
|
647
|
-
- Fast lane (no milestone) → branch from `origin/{STAGING_BRANCH}`
|
|
1025
|
+
- Feature lane (has milestone) → branch from `origin/{PR_BASE}` (PR_BASE now set above)
|
|
1026
|
+
- Fast lane (no milestone) → branch from `origin/{PR_BASE}` (PR_BASE = `{STAGING_BRANCH}`)
|
|
648
1027
|
|
|
649
1028
|
```bash
|
|
650
1029
|
cd {REPO_PATH}
|
|
651
1030
|
git fetch origin
|
|
652
1031
|
BRANCH="fix/{slug}-{NUMBER}"
|
|
653
1032
|
WORKTREE_PATH="{REPO_PATH}/.claude/worktrees/{BRANCH_SLUG}"
|
|
654
|
-
git worktree add {WORKTREE_PATH} -b {BRANCH} origin/{
|
|
1033
|
+
git worktree add {WORKTREE_PATH} -b {BRANCH} origin/{PR_BASE}
|
|
655
1034
|
```
|
|
656
1035
|
|
|
657
1036
|
If worktree already exists: verify correct branch, reuse or remove and recreate.
|
|
@@ -794,6 +1173,30 @@ fi
|
|
|
794
1173
|
```
|
|
795
1174
|
Typecheck or build failures are BLOCKING.
|
|
796
1175
|
|
|
1176
|
+
**Learned test commands** — After all `verification.commands` steps complete, run any commands from `learned.test_commands` (captured from owner corrections in Phase 1D or set manually in forge.yaml):
|
|
1177
|
+
|
|
1178
|
+
```bash
|
|
1179
|
+
# LEARNED_TEST_COMMANDS was set in Phase 0B.1 from forge.yaml → learned.test_commands
|
|
1180
|
+
# If empty/null, this block is a no-op
|
|
1181
|
+
if [ -n "$LEARNED_TEST_COMMANDS" ] && [ "$LEARNED_TEST_COMMANDS" != "[]" ]; then
|
|
1182
|
+
echo "Running learned test commands..."
|
|
1183
|
+
# yq outputs each entry on its own line with -r flag
|
|
1184
|
+
echo "$LEARNED_TEST_COMMANDS" | yq '.[]' | while IFS= read -r cmd; do
|
|
1185
|
+
[ -z "$cmd" ] && continue
|
|
1186
|
+
echo "Running learned command: $cmd"
|
|
1187
|
+
eval "$cmd" 2>&1 | tail -30
|
|
1188
|
+
CMD_EXIT=$?
|
|
1189
|
+
if [ $CMD_EXIT -ne 0 ]; then
|
|
1190
|
+
echo "FAILED (exit $CMD_EXIT): $cmd"
|
|
1191
|
+
exit $CMD_EXIT
|
|
1192
|
+
fi
|
|
1193
|
+
done
|
|
1194
|
+
else
|
|
1195
|
+
echo "No learned test commands configured — skipping"
|
|
1196
|
+
fi
|
|
1197
|
+
```
|
|
1198
|
+
Learned test command failures are BLOCKING (same as verification.commands failures). <!-- Added: forge#667 -->
|
|
1199
|
+
|
|
797
1200
|
### 3I: Frontend proxy wiring check (MANDATORY)
|
|
798
1201
|
|
|
799
1202
|
Skip if no TS/TSX files changed.
|
|
@@ -928,6 +1331,15 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:BUILDER -->
|
|
|
928
1331
|
<!-- FORGE:BUILDER:COMPLETE -->"
|
|
929
1332
|
```
|
|
930
1333
|
|
|
1334
|
+
Write machine-readable phase checkpoint (MUST execute immediately after FORGE:BUILDER comment is posted, before Phase 4):
|
|
1335
|
+
```bash
|
|
1336
|
+
CHECKPOINT_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
1337
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:CHECKPOINT -->
|
|
1338
|
+
\`\`\`json
|
|
1339
|
+
{\"phase\": \"BUILD\", \"status\": \"COMPLETE\", \"next_phase\": \"REVIEW\", \"timestamp\": \"${CHECKPOINT_TIMESTAMP}\"}
|
|
1340
|
+
\`\`\`"
|
|
1341
|
+
```
|
|
1342
|
+
|
|
931
1343
|
---
|
|
932
1344
|
|
|
933
1345
|
## Phase 4: PR Creation
|
|
@@ -951,7 +1363,50 @@ cd {WORKTREE_PATH} && git push -u origin {BRANCH}
|
|
|
951
1363
|
If fails: try `--force-with-lease`. If still fails: post comment, add `needs-human`, STOP.
|
|
952
1364
|
|
|
953
1365
|
### 4C: Determine PR target
|
|
954
|
-
|
|
1366
|
+
`PR_BASE` was computed in Phase 3E. If somehow unset (e.g., resumed session after compaction), recompute:
|
|
1367
|
+
```bash
|
|
1368
|
+
RESOLUTION=$(resolve_script 'classify-lane')
|
|
1369
|
+
TIER="${RESOLUTION%%:*}"; SCRIPT_PATH="${RESOLUTION#*:}"
|
|
1370
|
+
case "$TIER" in
|
|
1371
|
+
adaptive|universal)
|
|
1372
|
+
if ! PR_BASE=$(bash "$SCRIPT_PATH" {NUMBER} -R {GH_REPO}); then
|
|
1373
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "BLOCKER: classify-lane.sh failed to recompute PR target — see script error above. Adding needs-human."
|
|
1374
|
+
gh issue edit {NUMBER} {GH_FLAG} --add-label "needs-human"
|
|
1375
|
+
exit 1
|
|
1376
|
+
fi
|
|
1377
|
+
;;
|
|
1378
|
+
prose)
|
|
1379
|
+
# No valid prose fallback — see Phase 3E note.
|
|
1380
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "BLOCKER: classify-lane.sh not installed (prose tier). Cannot recompute PR target. Adding needs-human."
|
|
1381
|
+
gh issue edit {NUMBER} {GH_FLAG} --add-label "needs-human"
|
|
1382
|
+
exit 1
|
|
1383
|
+
;;
|
|
1384
|
+
esac
|
|
1385
|
+
```
|
|
1386
|
+
Output is authoritative — no prose fallback. Script exits 1 on error (invalid issue, `gh` auth failure, or milestone branch absent on remote); treat non-zero exit as `needs-human` and STOP. <!-- Added: forge#669, forge#639 -->
|
|
1387
|
+
|
|
1388
|
+
### 4C.5: Validate PR target against classified lane
|
|
1389
|
+
```bash
|
|
1390
|
+
RESOLUTION=$(resolve_script 'validate-pr-target')
|
|
1391
|
+
TIER="${RESOLUTION%%:*}"; SCRIPT_PATH="${RESOLUTION#*:}"
|
|
1392
|
+
case "$TIER" in
|
|
1393
|
+
adaptive|universal)
|
|
1394
|
+
bash "$SCRIPT_PATH" {PR_BASE} {CLASSIFIED_LANE}
|
|
1395
|
+
;;
|
|
1396
|
+
prose)
|
|
1397
|
+
# validate-pr-target has no safe prose fallback — silently skipping validation risks
|
|
1398
|
+
# merging to the wrong branch. Log a warning but do NOT block the pipeline; the PR
|
|
1399
|
+
# review step will catch a mismatched target before merge.
|
|
1400
|
+
echo "WARNING: validate-pr-target.sh not installed (prose tier) — skipping lane validation. Confirm PR base manually." >&2
|
|
1401
|
+
;;
|
|
1402
|
+
esac
|
|
1403
|
+
```
|
|
1404
|
+
`{CLASSIFIED_LANE}` is the value returned by `classify-lane.sh` in Phase 4C. `{PR_BASE}` is the branch the PR will target. If exit code is 1 (mismatch):
|
|
1405
|
+
```bash
|
|
1406
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "BLOCKING: validate-pr-target.sh — PR base \`{PR_BASE}\` does not match classified lane \`{CLASSIFIED_LANE}\`. Manual intervention required."
|
|
1407
|
+
gh issue edit {NUMBER} {GH_FLAG} --add-label "needs-human"
|
|
1408
|
+
```
|
|
1409
|
+
→ STOP. Do NOT proceed to Phase 4D. <!-- Added: forge#671 -->
|
|
955
1410
|
|
|
956
1411
|
### 4D: Create PR
|
|
957
1412
|
```bash
|
|
@@ -978,7 +1433,15 @@ If PR already exists for this branch, use the existing PR number.
|
|
|
978
1433
|
|
|
979
1434
|
### 4E: Update labels
|
|
980
1435
|
```bash
|
|
981
|
-
|
|
1436
|
+
RESOLUTION=$(resolve_script 'transition-label')
|
|
1437
|
+
TIER="${RESOLUTION%%:*}"; SCRIPT_PATH="${RESOLUTION#*:}"
|
|
1438
|
+
case "$TIER" in
|
|
1439
|
+
adaptive|universal) bash "$SCRIPT_PATH" {NUMBER} {GH_FLAG} in-review ;;
|
|
1440
|
+
prose)
|
|
1441
|
+
gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:in-review" \
|
|
1442
|
+
--remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:merged,workflow:invalid,workflow:decomposed" 2>/dev/null || true
|
|
1443
|
+
;;
|
|
1444
|
+
esac
|
|
982
1445
|
```
|
|
983
1446
|
|
|
984
1447
|
---
|
|
@@ -986,6 +1449,16 @@ gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:in-review" --remove-label
|
|
|
986
1449
|
## Phase 5: Auto-Review
|
|
987
1450
|
|
|
988
1451
|
### 5A: Re-read state from GitHub (MANDATORY)
|
|
1452
|
+
|
|
1453
|
+
**Post Phase 5 heartbeat** (skip if issue already has a terminal label — `workflow:merged`, `workflow:invalid`, `needs-human`):
|
|
1454
|
+
```bash
|
|
1455
|
+
PHASE_START_TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
1456
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:HEARTBEAT -->
|
|
1457
|
+
**Phase**: Phase 5 — Review
|
|
1458
|
+
**Timestamp**: ${PHASE_START_TIMESTAMP}
|
|
1459
|
+
**Issue**: #{NUMBER}"
|
|
1460
|
+
```
|
|
1461
|
+
|
|
989
1462
|
```bash
|
|
990
1463
|
gh issue view {NUMBER} {GH_FLAG} --json number,title,body,labels,state
|
|
991
1464
|
PR_NUMBER=$(gh pr list {GH_FLAG} --head {BRANCH} --json number --jq '.[0].number')
|
|
@@ -1029,10 +1502,19 @@ gh pr view {PR_NUMBER} {GH_FLAG} --json state,mergedAt --jq '{state: .state, mer
|
|
|
1029
1502
|
gh issue view {NUMBER} {GH_FLAG} --json state --jq '.state'
|
|
1030
1503
|
```
|
|
1031
1504
|
|
|
1032
|
-
- PR MERGED + issue CLOSED → proceed to Phase 6
|
|
1033
|
-
- PR MERGED + issue OPEN → close issue manually
|
|
1505
|
+
- PR MERGED + issue CLOSED → write checkpoint, then proceed to Phase 6
|
|
1506
|
+
- PR MERGED + issue OPEN → close issue manually, write checkpoint, proceed to Phase 6
|
|
1034
1507
|
- PR NOT MERGED → `gh pr merge {PR_NUMBER} {GH_FLAG} --merge --auto`. If fails → post comment, add `needs-human`, STOP.
|
|
1035
1508
|
|
|
1509
|
+
**When PR is MERGED — write machine-readable phase checkpoint (MANDATORY)**:
|
|
1510
|
+
```bash
|
|
1511
|
+
CHECKPOINT_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
1512
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:CHECKPOINT -->
|
|
1513
|
+
\`\`\`json
|
|
1514
|
+
{\"phase\": \"REVIEW\", \"status\": \"COMPLETE\", \"next_phase\": \"CLOSE\", \"timestamp\": \"${CHECKPOINT_TIMESTAMP}\"}
|
|
1515
|
+
\`\`\`"
|
|
1516
|
+
```
|
|
1517
|
+
|
|
1036
1518
|
<!-- FORGE:PHASE_COMPLETE — Review done, PR merged. See Universal Phase Dispatcher: next phase is Phase 6 (Close & Cleanup). Not terminal — continue immediately. -->
|
|
1037
1519
|
|
|
1038
1520
|
**After /review-pr returns and the PR is confirmed merged: immediately proceed to Phase 6 (Close & Cleanup). Do NOT stop here. `REVIEW_RESULT: status: COMPLETE` is an intermediate result — the pipeline is NOT done. Invoke Phase 6 now to close the issue, update labels, post the trajectory log, and clean up the worktree.**
|
|
@@ -1077,9 +1559,15 @@ If all phases complete:
|
|
|
1077
1559
|
```bash
|
|
1078
1560
|
gh issue close {NUMBER} {GH_FLAG} \
|
|
1079
1561
|
--comment "Closed: PR #{PR_NUMBER} merged to \`{PR_BASE}\`. Closes #{NUMBER}."
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1562
|
+
RESOLUTION=$(resolve_script 'transition-label')
|
|
1563
|
+
TIER="${RESOLUTION%%:*}"; SCRIPT_PATH="${RESOLUTION#*:}"
|
|
1564
|
+
case "$TIER" in
|
|
1565
|
+
adaptive|universal) bash "$SCRIPT_PATH" {NUMBER} {GH_FLAG} merged ;;
|
|
1566
|
+
prose)
|
|
1567
|
+
gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:merged" \
|
|
1568
|
+
--remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:in-review,workflow:invalid,workflow:decomposed" 2>/dev/null || true
|
|
1569
|
+
;;
|
|
1570
|
+
esac
|
|
1083
1571
|
```
|
|
1084
1572
|
|
|
1085
1573
|
### 6D: Parent tracker update (sub-issues only)
|
|
@@ -1088,7 +1576,8 @@ gh issue edit {NUMBER} {GH_FLAG} \
|
|
|
1088
1576
|
|
|
1089
1577
|
```bash
|
|
1090
1578
|
PARENT_REF=$(gh issue view {NUMBER} {GH_FLAG} --json body --jq '.body' \
|
|
1091
|
-
| grep -
|
|
1579
|
+
| grep -iE '(part of|spawned from|sub-issue of|parent issue:?|parent:)\s*#[0-9]+' \
|
|
1580
|
+
| sed -n 's/.*#\([0-9][0-9]*\).*/\1/p' | head -1)
|
|
1092
1581
|
```
|
|
1093
1582
|
|
|
1094
1583
|
If parent found: check off this sub-issue in parent body. If ALL sub-issues checked off → close parent with `workflow:merged`.
|
|
@@ -1155,6 +1644,105 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:TRAJECTORY -->
|
|
|
1155
1644
|
**Pipeline completed**: {TIMESTAMP}"
|
|
1156
1645
|
```
|
|
1157
1646
|
|
|
1647
|
+
### 7C: Graph Decision Record (MANDATORY when PR exists)
|
|
1648
|
+
|
|
1649
|
+
**Skip if**: `{PR_NUMBER}` is empty (investigation-only tasks with no PR) OR `<!-- FORGE:DECISION_RECORD -->` already posted on the PR.
|
|
1650
|
+
|
|
1651
|
+
**Purpose**: Post a single consolidated provenance artifact to the PR that proves the merge was backed by citable evidence. Enables downstream benchmarking queries (repeated-mistake rate, stale-edge hit rate, review escape rate) by making every pipeline run queryable via `gh api`.
|
|
1652
|
+
|
|
1653
|
+
**Idempotency check**:
|
|
1654
|
+
```bash
|
|
1655
|
+
GDR_EXISTS=$(gh api repos/{GH_REPO}/issues/{PR_NUMBER}/comments \
|
|
1656
|
+
--jq '[.[] | select(.body | contains("FORGE:DECISION_RECORD"))] | length > 0' 2>/dev/null || echo "false")
|
|
1657
|
+
```
|
|
1658
|
+
|
|
1659
|
+
**Extract context edge counts** from FORGE:CONTEXT comment (already posted on issue):
|
|
1660
|
+
```bash
|
|
1661
|
+
CONTEXT_COMMENT=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
1662
|
+
--jq '.[] | select(.body | contains("FORGE:CONTEXT")) | .body' 2>/dev/null | head -1)
|
|
1663
|
+
|
|
1664
|
+
# Count historical review-finding issue references (#NNN patterns in Context comment)
|
|
1665
|
+
REVIEW_FINDING_COUNT=$(echo "$CONTEXT_COMMENT" | grep -oE '#[0-9]+' | wc -l | tr -d ' ')
|
|
1666
|
+
REVIEW_FINDING_COUNT=${REVIEW_FINDING_COUNT:-0}
|
|
1667
|
+
```
|
|
1668
|
+
|
|
1669
|
+
**Extract review verdict and findings count** from PR review summary (Phase 9 of review-pr):
|
|
1670
|
+
```bash
|
|
1671
|
+
REVIEW_SUMMARY=$(gh api repos/{GH_REPO}/issues/{PR_NUMBER}/comments \
|
|
1672
|
+
--jq '[.[] | select(.body | contains("FORGE:REVIEWER") or (.body | test("APPROVED:|CHANGES REQUESTED:"; "i")))] | last | .body // ""' 2>/dev/null || echo '')
|
|
1673
|
+
|
|
1674
|
+
REVIEW_VERDICT=$(echo "$REVIEW_SUMMARY" | sed -n 's/.*Verdict: \(APPROVED\|CHANGES REQUESTED\).*/\1/p' | head -1 || echo "APPROVED")
|
|
1675
|
+
REVIEW_VERDICT="${REVIEW_VERDICT:-APPROVED}"
|
|
1676
|
+
FINDINGS_COUNT=$(echo "$REVIEW_SUMMARY" | grep -oE '[0-9]+ findings' | grep -oE '[0-9]+' | head -1 || echo "0")
|
|
1677
|
+
FINDINGS_COUNT="${FINDINGS_COUNT:-0}"
|
|
1678
|
+
AGENTS_RUN=$(echo "$REVIEW_SUMMARY" | grep -oE '[0-9]+ agents' | grep -oE '[0-9]+' | head -1 || echo "0")
|
|
1679
|
+
AGENTS_RUN="${AGENTS_RUN:-0}"
|
|
1680
|
+
```
|
|
1681
|
+
|
|
1682
|
+
**Post GDR to PR** (not to issue — PR comment survives as permanent artifact on the merged diff):
|
|
1683
|
+
```bash
|
|
1684
|
+
if [ "$GDR_EXISTS" != "true" ] && [ -n "{PR_NUMBER}" ]; then
|
|
1685
|
+
GDR_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
1686
|
+
HEAD_SHA=$(gh pr view {PR_NUMBER} {GH_FLAG} --json headRefOid --jq '.headRefOid' 2>/dev/null || echo "")
|
|
1687
|
+
MERGE_COMMIT=$(gh pr view {PR_NUMBER} {GH_FLAG} --json mergeCommit --jq '.mergeCommit.oid // ""' 2>/dev/null || echo "")
|
|
1688
|
+
|
|
1689
|
+
gh pr comment {PR_NUMBER} {GH_FLAG} --body "<!-- FORGE:DECISION_RECORD -->
|
|
1690
|
+
## Graph Decision Record — Issue #${NUMBER} / PR #${PR_NUMBER}
|
|
1691
|
+
|
|
1692
|
+
\`\`\`json
|
|
1693
|
+
{
|
|
1694
|
+
\"schema_version\": \"1\",
|
|
1695
|
+
\"issue\": ${NUMBER},
|
|
1696
|
+
\"pr\": ${PR_NUMBER},
|
|
1697
|
+
\"repo\": \"{GH_REPO}\",
|
|
1698
|
+
\"lane\": \"{lane}\",
|
|
1699
|
+
\"pr_base\": \"{PR_BASE}\",
|
|
1700
|
+
\"branch\": \"{BRANCH}\",
|
|
1701
|
+
\"head_sha\": \"${HEAD_SHA}\",
|
|
1702
|
+
\"merge_commit\": \"${MERGE_COMMIT}\",
|
|
1703
|
+
\"investigation\": {
|
|
1704
|
+
\"verdict\": \"{VERDICT}\",
|
|
1705
|
+
\"confidence\": \"{CONFIDENCE}\",
|
|
1706
|
+
\"task_type\": \"{TASK_TYPE}\"
|
|
1707
|
+
},
|
|
1708
|
+
\"context\": {
|
|
1709
|
+
\"historical_edges_referenced\": ${REVIEW_FINDING_COUNT},
|
|
1710
|
+
\"forge_annotations_read\": [\"FORGE:INVESTIGATOR\", \"FORGE:CONTRACT\", \"FORGE:CONTEXT\", \"FORGE:ARCHITECT\", \"FORGE:BUILDER\"]
|
|
1711
|
+
},
|
|
1712
|
+
\"build\": {
|
|
1713
|
+
\"files_changed\": {FILES_CHANGED},
|
|
1714
|
+
\"quality_gate\": \"{pass|fail}\",
|
|
1715
|
+
\"quality_gate_iterations\": {GATE_ITERATIONS}
|
|
1716
|
+
},
|
|
1717
|
+
\"review\": {
|
|
1718
|
+
\"verdict\": \"${REVIEW_VERDICT:-APPROVED}\",
|
|
1719
|
+
\"findings_created\": ${FINDINGS_COUNT},
|
|
1720
|
+
\"agents_run\": ${AGENTS_RUN}
|
|
1721
|
+
},
|
|
1722
|
+
\"merge\": {
|
|
1723
|
+
\"merged_at\": \"${GDR_TIMESTAMP}\",
|
|
1724
|
+
\"justification\": \"Investigation confirmed ({VERDICT}/{CONFIDENCE}), quality gate passed, review ${REVIEW_VERDICT:-approved}\"
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
\`\`\`
|
|
1728
|
+
|
|
1729
|
+
**Queryable**: \`gh api repos/{GH_REPO}/issues/{PR_NUMBER}/comments --jq '[.[] | select(.body | contains(\"FORGE:DECISION_RECORD\"))] | .[0].body\`"
|
|
1730
|
+
fi
|
|
1731
|
+
```
|
|
1732
|
+
|
|
1733
|
+
**Benchmarking**: Query all GDRs for a repo to compute pipeline metrics:
|
|
1734
|
+
```bash
|
|
1735
|
+
# Fetch all merged PRs and extract their GDR JSON blocks for metric computation
|
|
1736
|
+
# (used by /pipeline-health to measure repeated-mistake rate, stale-edge hit rate, etc.)
|
|
1737
|
+
gh pr list -R {GH_REPO} --state merged --limit 100 --json number \
|
|
1738
|
+
--jq '.[].number' | while read pr; do
|
|
1739
|
+
gh api repos/{GH_REPO}/issues/$pr/comments \
|
|
1740
|
+
--jq '.[] | select(.body | contains("FORGE:DECISION_RECORD")) | .body' 2>/dev/null
|
|
1741
|
+
done
|
|
1742
|
+
```
|
|
1743
|
+
|
|
1744
|
+
<!-- Added: forge#776 -->
|
|
1745
|
+
|
|
1158
1746
|
---
|
|
1159
1747
|
|
|
1160
1748
|
## Error Handling
|