forgedock 1.0.16 → 1.0.18
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 +54 -3
- package/bin/cross-publish.mjs +344 -0
- package/bin/forgedock.mjs +2205 -541
- 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/autopilot.md +77 -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/design-bench.md +176 -0
- package/commands/design-render-critique-loop.md +103 -0
- package/commands/design.md +259 -0
- 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 +9 -3
- package/commands/milestone.md +2 -0
- package/commands/optimize.md +411 -0
- package/commands/orchestrate.md +239 -0
- package/commands/pipeline-health.md +60 -1
- package/commands/qa-sweep.md +39 -2
- package/commands/quality-gate.md +67 -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 +160 -2
- 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/test-gate.md +929 -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 +7 -3
- 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 +676 -38
- package/docs/BRAND.md +51 -0
- package/docs/CI.md +177 -0
- package/docs/CONFIG.md +94 -0
- package/docs/FORGE-PROTOCOL.md +324 -0
- package/docs/articles/b-vs-c-baseline.md +210 -0
- package/docs/articles/full-abc-benchmark.md +205 -0
- package/docs/articles/per-repo-adaptive-scripts-token-savings.md +174 -0
- package/docs/articles/selective-spec-loading-token-savings.md +140 -0
- package/docs/claude-breakpoints.json +47 -0
- package/docs/design/abc-benchmark.md +195 -0
- package/docs/design/design-architect-rationale.md +123 -0
- package/docs/design/design-memory.md +61 -0
- package/docs/design/design-spec-schema.md +248 -0
- package/docs/design/design-system-lint.md +150 -0
- package/docs/design/divergent-generation.md +106 -0
- package/docs/design/effects-appropriateness.md +133 -0
- package/docs/design/fixtures/briefs/README.md +47 -0
- package/docs/design/fixtures/briefs/cadence.md +39 -0
- package/docs/design/fixtures/briefs/plume.md +39 -0
- package/docs/design/fixtures/briefs/slipstream.md +39 -0
- package/docs/design/fixtures/briefs/tender.md +39 -0
- package/docs/design/fixtures/briefs/voltage.md +39 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/cadence/arm-b/run-1.html +364 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/cadence/arm-b/run-2.html +420 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/cadence/arm-b/run-3.html +506 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/plume/arm-b/run-1.html +611 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/plume/arm-b/run-2.html +594 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/plume/arm-b/run-3.html +817 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/runs.json +175 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/scorecard.json +138 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/slipstream/arm-b/run-1.html +373 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/slipstream/arm-b/run-2.html +398 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/slipstream/arm-b/run-3.html +595 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/tender/arm-b/run-1.html +531 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/tender/arm-b/run-2.html +565 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/tender/arm-b/run-3.html +674 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/voltage/arm-b/run-1.html +388 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/voltage/arm-b/run-2.html +463 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/voltage/arm-b/run-3.html +568 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-a/run-1.html +562 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-a/run-2.html +618 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-a/run-3.html +618 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-b/run-1.html +364 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-b/run-2.html +420 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-b/run-3.html +506 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-a/run-1.html +527 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-a/run-2.html +445 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-a/run-3.html +483 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-b/run-1.html +611 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-b/run-2.html +594 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-b/run-3.html +817 -0
- package/docs/design/fixtures/runs/full-abc/runs.json +190 -0
- package/docs/design/fixtures/runs/full-abc/scorecard.json +144 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-a/run-1.html +496 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-a/run-2.html +447 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-a/run-3.html +564 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-b/run-1.html +373 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-b/run-2.html +398 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-b/run-3.html +595 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-a/run-1.html +622 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-a/run-2.html +475 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-a/run-3.html +660 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-b/run-1.html +531 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-b/run-2.html +565 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-b/run-3.html +674 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-a/run-1.html +525 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-a/run-2.html +446 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-a/run-3.html +463 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-b/run-1.html +388 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-b/run-2.html +463 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-b/run-3.html +568 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-a/run-1.html +1068 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-a/run-2.html +1423 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-a/run-3.html +1245 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-b/run-1.html +305 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-b/run-2.html +327 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-b/run-3.html +428 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-a/run-1.html +1270 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-a/run-2.html +1172 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-a/run-3.html +1532 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-b/run-1.html +315 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-b/run-2.html +342 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-b/run-3.html +353 -0
- package/docs/design/fixtures/runs/full-abc-opus/runs.json +190 -0
- package/docs/design/fixtures/runs/full-abc-opus/scorecard.json +144 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-a/run-1.html +345 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-a/run-2.html +396 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-a/run-3.html +502 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-b/run-1.html +408 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-b/run-2.html +446 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-b/run-3.html +496 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-a/run-1.html +813 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-a/run-2.html +711 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-a/run-3.html +721 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-b/run-1.html +280 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-b/run-2.html +291 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-b/run-3.html +366 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-a/run-1.html +1010 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-a/run-2.html +836 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-a/run-3.html +930 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-b/run-1.html +407 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-b/run-2.html +396 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-b/run-3.html +529 -0
- package/docs/design/foundations/brutalist.css +294 -0
- package/docs/design/foundations/editorial.css +300 -0
- package/docs/design/foundations/minimal-luxury.css +295 -0
- package/docs/design/foundations/technical.css +293 -0
- package/docs/design/foundations/warm-photo.css +299 -0
- package/docs/design/reference-corpus.md +1726 -0
- package/docs/design/render-critique-loop.md +57 -0
- package/docs/site/getting-started.md +21 -0
- package/docs/spec-graph-schema.md +397 -0
- package/package.json +3 -3
- package/scripts/bench-scorecard.mjs +239 -0
- package/scripts/build-spec-graph.mjs +509 -0
- package/scripts/classify-lane.sh +138 -0
- package/scripts/design-system-lint.mjs +993 -0
- package/scripts/graph-query.sh +486 -0
- package/scripts/transition-label.sh +169 -0
- package/scripts/validate-pr-target.sh +30 -0
- package/scripts/validate-spec-graph.sh +423 -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/templates/workflows/forgedock-review.yml +96 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Recover full pipeline context after compaction — find the last active issue, reconstruct FORGE annotations, and resume work
|
|
3
|
+
argument-hint: [issue number (optional — auto-detects if omitted)]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /resume — Context Recovery After Compaction
|
|
7
|
+
|
|
8
|
+
**Input**: $ARGUMENTS
|
|
9
|
+
|
|
10
|
+
You are the context-recovery agent. When a session ends or context is compressed, all working state is lost. Your job is to restore it instantly — find the most recently active issue, read all FORGE annotations, reconstruct the current pipeline phase, and re-enter the pipeline exactly where it left off.
|
|
11
|
+
|
|
12
|
+
**Agent model policy**: Default `model: "sonnet"`. Fallback: `model: "opus"` if Sonnet is rate-limited.
|
|
13
|
+
**NEVER use plan mode (EnterPlanMode).**
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Phase 0: Config Resolution
|
|
18
|
+
|
|
19
|
+
Read `forge.yaml` to configure all project-specific variables before running any commands:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
CONFIG_FILE="${FORGE_CONFIG:-forge.yaml}"
|
|
23
|
+
GH_REPO=$(yq '.project.owner + "/" + .project.repo' "$CONFIG_FILE")
|
|
24
|
+
GH_FLAG="-R $GH_REPO"
|
|
25
|
+
REPO_PATH=$(yq '.paths.root' "$CONFIG_FILE")
|
|
26
|
+
STAGING_BRANCH=$(yq '.branches.staging' "$CONFIG_FILE")
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
If `forge.yaml` is missing: stop and tell the user to run `npx forgedock init` to generate it.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Phase 1: Find Active Issue
|
|
34
|
+
|
|
35
|
+
### 1A: Parse input
|
|
36
|
+
|
|
37
|
+
Check `$ARGUMENTS`:
|
|
38
|
+
|
|
39
|
+
- **Explicit issue number** (e.g., `610`, `#610`): strip `#`, set `TARGET_NUMBER={number}`, skip to Phase 2.
|
|
40
|
+
- **Empty**: proceed with auto-detection below.
|
|
41
|
+
|
|
42
|
+
### 1B: Query for in-progress issues
|
|
43
|
+
|
|
44
|
+
The `gh issue list --label` flag uses AND filtering when multiple labels are given. To find issues with ANY active workflow label, run one query per label and merge results:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Find open issues with any active workflow label, sorted by most recently updated
|
|
48
|
+
# (gh --label uses AND, so query each label separately and merge)
|
|
49
|
+
ACTIVE_ISSUES=$(
|
|
50
|
+
for LABEL in "workflow:investigating" "workflow:ready-to-build" "workflow:building" "workflow:in-review"; do
|
|
51
|
+
gh issue list {GH_FLAG} \
|
|
52
|
+
--state open \
|
|
53
|
+
--label "$LABEL" \
|
|
54
|
+
--limit 10 \
|
|
55
|
+
--json number,title,labels,updatedAt
|
|
56
|
+
done | jq -s '
|
|
57
|
+
flatten |
|
|
58
|
+
unique_by(.number) |
|
|
59
|
+
sort_by(.updatedAt) | reverse |
|
|
60
|
+
.[] | {
|
|
61
|
+
number,
|
|
62
|
+
title,
|
|
63
|
+
phase: (.labels | map(.name | select(startswith("workflow:"))) | first // "unknown"),
|
|
64
|
+
updatedAt
|
|
65
|
+
}
|
|
66
|
+
')
|
|
67
|
+
echo "$ACTIVE_ISSUES"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 1C: Select issue
|
|
71
|
+
|
|
72
|
+
**If one result**: use it — set `TARGET_NUMBER={number}`.
|
|
73
|
+
|
|
74
|
+
**If multiple results**: display a ranked list (most recently updated first), then select the top entry:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
## Active Issues Found (most recent first)
|
|
78
|
+
|
|
79
|
+
| # | Issue | Phase | Last Updated |
|
|
80
|
+
|---|-------|-------|--------------|
|
|
81
|
+
| 1 | #{number}: {title} | {phase} | {updatedAt} |
|
|
82
|
+
| 2 | #{number}: {title} | {phase} | {updatedAt} |
|
|
83
|
+
...
|
|
84
|
+
|
|
85
|
+
Auto-selecting #1 (most recently updated). Pass an explicit issue number to override.
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Set `TARGET_NUMBER` to the top-ranked issue number.
|
|
89
|
+
|
|
90
|
+
### 1D: Fallback — no active label found
|
|
91
|
+
|
|
92
|
+
If `ACTIVE_ISSUES` is empty, search recent issues by FORGE annotation presence:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Look for recently updated open issues with FORGE annotations (pipeline may have stalled pre-label)
|
|
96
|
+
RECENT_ISSUES=$(gh issue list {GH_FLAG} \
|
|
97
|
+
--state open \
|
|
98
|
+
--limit 20 \
|
|
99
|
+
--json number,title,updatedAt \
|
|
100
|
+
--jq 'sort_by(.updatedAt) | reverse | .[0:5] | .[] | {number, title, updatedAt}')
|
|
101
|
+
|
|
102
|
+
# For each candidate, check if it has FORGE annotations
|
|
103
|
+
for NUMBER in $(echo "$RECENT_ISSUES" | jq -r '.number'); do
|
|
104
|
+
HAS_FORGE=$(gh api repos/{GH_REPO}/issues/$NUMBER/comments \
|
|
105
|
+
--jq '[.[] | select(.body | test("<!-- FORGE:"))] | length > 0' 2>/dev/null || echo "false")
|
|
106
|
+
if [ "$HAS_FORGE" = "true" ]; then
|
|
107
|
+
TARGET_NUMBER=$NUMBER
|
|
108
|
+
break
|
|
109
|
+
fi
|
|
110
|
+
done
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
If still no candidate: display `No recent active issues found. To resume work on a specific issue, run: /resume {issue-number}` and STOP.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Phase 2: Reconstruct Context
|
|
118
|
+
|
|
119
|
+
### 2A: Load issue state
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
ISSUE=$(gh issue view {TARGET_NUMBER} {GH_FLAG} \
|
|
123
|
+
--json number,title,body,labels,state,milestone,updatedAt)
|
|
124
|
+
|
|
125
|
+
ISSUE_TITLE=$(echo "$ISSUE" | jq -r '.title')
|
|
126
|
+
ISSUE_STATE=$(echo "$ISSUE" | jq -r '.state')
|
|
127
|
+
ISSUE_LABELS=$(echo "$ISSUE" | jq -r '[.labels[].name] | join(", ")')
|
|
128
|
+
ISSUE_MILESTONE=$(echo "$ISSUE" | jq -r '.milestone.title // "none"')
|
|
129
|
+
|
|
130
|
+
echo "Issue: #${TARGET_NUMBER} — $ISSUE_TITLE"
|
|
131
|
+
echo "State: $ISSUE_STATE | Labels: $ISSUE_LABELS | Milestone: $ISSUE_MILESTONE"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
If issue is CLOSED and has `workflow:merged` or `workflow:invalid` label: display completion status and STOP — no work remaining.
|
|
135
|
+
|
|
136
|
+
### 2B: Read all FORGE annotations
|
|
137
|
+
|
|
138
|
+
Fetch all comments and extract the FORGE-annotated ones in pipeline order:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
COMMENTS=$(gh api repos/{GH_REPO}/issues/{TARGET_NUMBER}/comments \
|
|
142
|
+
--jq '.[] | select(.body | test("<!-- FORGE:")) | {
|
|
143
|
+
id: .id,
|
|
144
|
+
created_at: .created_at,
|
|
145
|
+
type: (.body | capture("<!-- FORGE:(?<t>[A-Z_:]+)") | .t),
|
|
146
|
+
body: .body
|
|
147
|
+
}')
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Extract specific annotations:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
# Investigation report
|
|
154
|
+
FORGE_INVESTIGATOR=$(gh api repos/{GH_REPO}/issues/{TARGET_NUMBER}/comments \
|
|
155
|
+
--jq '.[] | select(.body | contains("FORGE:INVESTIGATOR")) | .body' 2>/dev/null || echo "")
|
|
156
|
+
|
|
157
|
+
# Builder contract
|
|
158
|
+
FORGE_CONTRACT=$(gh api repos/{GH_REPO}/issues/{TARGET_NUMBER}/comments \
|
|
159
|
+
--jq '.[] | select(.body | contains("FORGE:CONTRACT")) | .body' 2>/dev/null || echo "")
|
|
160
|
+
|
|
161
|
+
# Implementation context
|
|
162
|
+
FORGE_CONTEXT=$(gh api repos/{GH_REPO}/issues/{TARGET_NUMBER}/comments \
|
|
163
|
+
--jq '.[] | select(.body | contains("FORGE:CONTEXT")) | .body' 2>/dev/null || echo "")
|
|
164
|
+
|
|
165
|
+
# Architecture plan
|
|
166
|
+
FORGE_ARCHITECT=$(gh api repos/{GH_REPO}/issues/{TARGET_NUMBER}/comments \
|
|
167
|
+
--jq '.[] | select(.body | contains("FORGE:ARCHITECT")) | .body' 2>/dev/null || echo "")
|
|
168
|
+
|
|
169
|
+
# Builder implementation report
|
|
170
|
+
FORGE_BUILDER=$(gh api repos/{GH_REPO}/issues/{TARGET_NUMBER}/comments \
|
|
171
|
+
--jq '.[] | select(.body | contains("FORGE:BUILDER")) | .body' 2>/dev/null || echo "")
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### 2C: Determine current pipeline phase
|
|
175
|
+
|
|
176
|
+
Read labels and annotation presence to identify where the pipeline was interrupted:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
# Extract active workflow label
|
|
180
|
+
WORKFLOW_LABEL=$(echo "$ISSUE_LABELS" | grep -oP 'workflow:[a-z:-]+' | grep -v 'workflow:merged\|workflow:invalid\|workflow:decomposed' | head -1)
|
|
181
|
+
|
|
182
|
+
# Check annotation completion markers
|
|
183
|
+
HAS_INVESTIGATION=$([ -n "$FORGE_INVESTIGATOR" ] && echo "$FORGE_INVESTIGATOR" | grep -q "INVESTIGATION:COMPLETE" && echo "YES" || echo "NO")
|
|
184
|
+
HAS_CONTRACT=$([ -n "$FORGE_CONTRACT" ] && echo "YES" || echo "NO")
|
|
185
|
+
HAS_CONTEXT=$([ -n "$FORGE_CONTEXT" ] && echo "YES" || echo "NO")
|
|
186
|
+
HAS_ARCHITECT=$([ -n "$FORGE_ARCHITECT" ] && echo "YES" || echo "NO")
|
|
187
|
+
HAS_BUILDER=$([ -n "$FORGE_BUILDER" ] && echo "$FORGE_BUILDER" | grep -q "FORGE:BUILDER:COMPLETE" && echo "YES" || echo "NO")
|
|
188
|
+
|
|
189
|
+
# Check for open PR on this issue's branch
|
|
190
|
+
OPEN_PR=$(gh pr list {GH_FLAG} --state open --search "#{TARGET_NUMBER}" --limit 1 --json number,headRefName --jq '.[0]' 2>/dev/null || echo "")
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Map to current phase:
|
|
194
|
+
|
|
195
|
+
| Condition | Current Phase | Resume Action |
|
|
196
|
+
|-----------|---------------|---------------|
|
|
197
|
+
| `HAS_INVESTIGATION=NO` | Phase 1: Investigating | Reinvestigate |
|
|
198
|
+
| `HAS_INVESTIGATION=YES`, `WORKFLOW_LABEL=workflow:ready-to-build` | Phase 3: Build | Enter build from 3A |
|
|
199
|
+
| `HAS_INVESTIGATION=YES`, `HAS_BUILDER=NO`, `WORKFLOW_LABEL=workflow:building` | Phase 3: Build in progress | Enter build from 3A (will skip completed sub-phases) |
|
|
200
|
+
| `HAS_BUILDER=YES`, PR not found | Phase 4: PR Creation | Push branch + create PR |
|
|
201
|
+
| `OPEN_PR` found, `WORKFLOW_LABEL=workflow:in-review` | Phase 5: Auto-Review | Invoke review-pr |
|
|
202
|
+
| PR merged, issue still open | Phase 6: Close | Close issue, update board |
|
|
203
|
+
|
|
204
|
+
### 2D: Display context summary
|
|
205
|
+
|
|
206
|
+
Emit a structured context block before resuming — this is what anchors the agent's working memory:
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
## Context Recovered — Issue #${TARGET_NUMBER}
|
|
210
|
+
|
|
211
|
+
**Title**: {ISSUE_TITLE}
|
|
212
|
+
**Lane**: {fast-lane | feature-lane (milestone: {MILESTONE})}
|
|
213
|
+
**Current Phase**: {CURRENT_PHASE}
|
|
214
|
+
**Active Label**: {WORKFLOW_LABEL}
|
|
215
|
+
|
|
216
|
+
### Investigation Summary
|
|
217
|
+
{if HAS_INVESTIGATION=YES: extract "### What We Found" + "### Root Cause" + "### Affected Files" blocks from FORGE_INVESTIGATOR}
|
|
218
|
+
{if HAS_INVESTIGATION=NO: "Not yet investigated."}
|
|
219
|
+
|
|
220
|
+
### Contract
|
|
221
|
+
{if HAS_CONTRACT=YES: extract "### Proposed Approach" + "### Deliverables" table from FORGE_CONTRACT}
|
|
222
|
+
{if HAS_CONTRACT=NO: "Contract not yet written."}
|
|
223
|
+
|
|
224
|
+
### Architecture Plan
|
|
225
|
+
{if HAS_ARCHITECT=YES: extract "### Affected Paths" table + "### Implementation Order" from FORGE_ARCHITECT}
|
|
226
|
+
{if HAS_ARCHITECT=NO: "Architecture plan not yet written."}
|
|
227
|
+
|
|
228
|
+
### Build Status
|
|
229
|
+
{if HAS_BUILDER=YES: extract "### Changes" + "### Acceptance Criteria Status" from FORGE_BUILDER}
|
|
230
|
+
{if HAS_BUILDER=NO: "Implementation not yet started."}
|
|
231
|
+
|
|
232
|
+
### Open PR
|
|
233
|
+
{if OPEN_PR: "PR #{pr_number} → {headRefName} (open)"}
|
|
234
|
+
{if not OPEN_PR: "No PR yet."}
|
|
235
|
+
|
|
236
|
+
### Resume Entry Point
|
|
237
|
+
→ Will invoke /work-on {TARGET_NUMBER} — pipeline re-enters at {CURRENT_PHASE}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Phase 3: Resume
|
|
243
|
+
|
|
244
|
+
Invoke `/work-on` to re-enter the pipeline. Work-on reads GitHub state independently and routes to the correct phase automatically.
|
|
245
|
+
|
|
246
|
+
```
|
|
247
|
+
Skill("work-on", args="{TARGET_NUMBER}")
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**Why delegation works**: `/work-on` Phase 0B already implements "Determine resume point" — it reads issue labels and FORGE annotations to route to the correct phase. `/resume` provides the human-readable context summary, then hands off to `/work-on` for execution.
|
|
251
|
+
|
|
252
|
+
**Do NOT re-implement pipeline logic here.** The context summary above is for the agent's working memory only — the actual phase routing and execution is owned by `/work-on`.
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Error Handling
|
|
257
|
+
|
|
258
|
+
| Condition | Response |
|
|
259
|
+
|-----------|----------|
|
|
260
|
+
| `forge.yaml` missing | Stop: "Run \`npx forgedock init\` to generate forge.yaml" |
|
|
261
|
+
| No active issues + no FORGE annotations found | Stop: "No recent active issues found. Run \`/resume {number}\` with an explicit issue number, or \`/work-on next\` to start a new issue." |
|
|
262
|
+
| Issue is closed with `workflow:merged` | Stop: "Issue #{number} is already merged. Nothing to resume." |
|
|
263
|
+
| Issue is closed with `workflow:invalid` | Stop: "Issue #{number} was closed as invalid. Nothing to resume." |
|
|
264
|
+
| Issue has `needs-human` label | Stop: "Issue #{number} is blocked (needs-human). Review the blocking comment and resolve it manually before resuming." |
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
2
|
+
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
3
|
+
|
|
1
4
|
# Agent Catalog for `/review-pr`
|
|
2
5
|
|
|
3
6
|
This file is the Agent Catalog referenced by the `/review-pr` orchestrator. It is read via the `Read` tool during Phase 3C (agent dispatch). It contains:
|
|
@@ -1171,9 +1174,10 @@ If no API context is configured above, derive conventions from the changed files
|
|
|
1171
1174
|
BASE=$(gh pr view [PR_NUMBER] --json baseRefName --jq '.baseRefName')
|
|
1172
1175
|
|
|
1173
1176
|
# Find PRs merged to this base in the last 48 hours
|
|
1177
|
+
CUTOFF="$(date -u -d '48 hours ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-48H +%Y-%m-%dT%H:%M:%SZ)"
|
|
1174
1178
|
RECENT_MERGED=$(gh pr list --base "$BASE" --state merged --limit 20 \
|
|
1175
1179
|
--json number,title,mergedAt,files \
|
|
1176
|
-
|
|
1180
|
+
2>/dev/null | jq --arg cutoff "$CUTOFF" \
|
|
1177
1181
|
'.[] | select(.mergedAt > $cutoff) | {number, title, schema_files: [.files[].path | select(test("schemas?/|scrape\\.py|models\\.py"))]}' \
|
|
1178
1182
|
2>/dev/null | head -40)
|
|
1179
1183
|
|
|
@@ -3,6 +3,8 @@ description: Staging review mode — comprehensive review of staging branch befo
|
|
|
3
3
|
argument-hint: [PR number or "staging"]
|
|
4
4
|
allowed-tools: Task, Bash, Read, Grep, Glob, WebFetch
|
|
5
5
|
---
|
|
6
|
+
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
7
|
+
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
6
8
|
|
|
7
9
|
# Staging Review
|
|
8
10
|
|
|
@@ -24,9 +26,13 @@ GH_REPO=$(yq '.project.owner + "/" + .project.repo' "$CONFIG_FILE")
|
|
|
24
26
|
GH_FLAG="-R $GH_REPO"
|
|
25
27
|
DEFAULT_BRANCH=$(yq '.branches.default' "$CONFIG_FILE")
|
|
26
28
|
STAGING_BRANCH=$(yq '.branches.staging' "$CONFIG_FILE")
|
|
29
|
+
|
|
30
|
+
# Test-gate config (Phase 6.5) — read here so vars are available before first use
|
|
31
|
+
GATE_POSTURE=$(yq '.verification.test_gate.posture // "blocking"' "$CONFIG_FILE" 2>/dev/null || echo "blocking")
|
|
32
|
+
OVERRIDE_PHRASE=$(yq '.verification.test_gate.override_phrase // "OVERRIDE: shipping with test failures —"' "$CONFIG_FILE" 2>/dev/null || echo "OVERRIDE: shipping with test failures —")
|
|
27
33
|
```
|
|
28
34
|
|
|
29
|
-
All `$DEFAULT_BRANCH
|
|
35
|
+
All `$DEFAULT_BRANCH`, `$STAGING_BRANCH`, `$GATE_POSTURE`, and `$OVERRIDE_PHRASE` references below are populated from `forge.yaml`.
|
|
30
36
|
|
|
31
37
|
---
|
|
32
38
|
|
|
@@ -340,6 +346,132 @@ Agent maps dependencies, assesses integration points (service boundaries, env va
|
|
|
340
346
|
|
|
341
347
|
---
|
|
342
348
|
|
|
349
|
+
## Phase 6.5: Runtime Test Gate (BLOCKING — runs after static analysis, before finding triage)
|
|
350
|
+
|
|
351
|
+
**Purpose**: Verify the integrated bundle's acceptance criteria against running code before the deploy verdict. Catches runtime defects (cross-PR interactions, container/startup failures, regression in tested behaviour) that static review cannot surface.
|
|
352
|
+
|
|
353
|
+
**Why here**: Phase 6 completes all static analysis (regression risk, security, quality). Phase 6.5 adds the runtime dimension before Phase 7 triages findings — so any test-gate failures can be filed as `test-failure` issues by `/test-gate` itself, then surface in Phase 7's triage pass.
|
|
354
|
+
|
|
355
|
+
**Posture**: Controlled by `verification.test_gate.posture` in `forge.yaml` (resolved in Config Resolution as `$GATE_POSTURE`). Default: `blocking`. Set to `advisory` to surface failures without preventing deploy. <!-- Added: forge#906 -->
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
echo "=== Phase 6.5: Runtime Test Gate ==="
|
|
359
|
+
echo "Bundle PRs: $(echo $ALL_PR_NUMBERS | tr '\n' ' ')"
|
|
360
|
+
echo "Posture: ${GATE_POSTURE}"
|
|
361
|
+
|
|
362
|
+
# Initialize test-gate verdict (default SKIP — safe if Phase 6.5 is bypassed)
|
|
363
|
+
TEST_GATE_VERDICT="SKIP"
|
|
364
|
+
TEST_GATE_REASON="Phase 6.5 not yet run"
|
|
365
|
+
|
|
366
|
+
# Invoke /test-gate with the bundle PRs already computed in Phase 0A
|
|
367
|
+
# ALL_PR_NUMBERS is the de-duplicated union of both scan methods (commit log + merge subjects)
|
|
368
|
+
GATE_OUTPUT=$(Skill("test-gate", "--prs \"$(echo $ALL_PR_NUMBERS | tr '\n' ' ' | xargs)\" --base $DEFAULT_BRANCH"))
|
|
369
|
+
|
|
370
|
+
# Extract machine-readable verdict from Skill output
|
|
371
|
+
TEST_GATE_VERDICT=$(echo "$GATE_OUTPUT" | grep -oP '(?<=FORGE:TEST_GATE:RESULT=)(BLOCK|PASS|SKIP)' | tail -1 || echo "SKIP")
|
|
372
|
+
|
|
373
|
+
echo "Test-gate verdict: ${TEST_GATE_VERDICT}"
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
**Verdict handling**:
|
|
377
|
+
|
|
378
|
+
```bash
|
|
379
|
+
case "$TEST_GATE_VERDICT" in
|
|
380
|
+
|
|
381
|
+
SKIP)
|
|
382
|
+
echo "ℹ️ Test gate: SKIP — no executable changes or tests not configured."
|
|
383
|
+
echo " This gap will appear in the Phase 8 summary. No deploy impact."
|
|
384
|
+
TEST_GATE_REASON="SKIP — no runtime tests ran (docs-only bundle, no integration tests configured, or manual-only criteria)"
|
|
385
|
+
;;
|
|
386
|
+
|
|
387
|
+
PASS)
|
|
388
|
+
echo "✅ Test gate: PASS — all test clusters passed. Deploy may proceed."
|
|
389
|
+
TEST_GATE_REASON="PASS — all automated test clusters passed"
|
|
390
|
+
;;
|
|
391
|
+
|
|
392
|
+
BLOCK)
|
|
393
|
+
# Check for override comment on the staging→main PR (mirrors Phase 0A pattern)
|
|
394
|
+
if [ -n "$PR_NUMBER" ]; then
|
|
395
|
+
TG_OVERRIDE=$(gh pr view "$PR_NUMBER" ${GH_FLAG} \
|
|
396
|
+
--json comments \
|
|
397
|
+
--jq "[.comments[].body | select(startswith(\"${OVERRIDE_PHRASE}\"))] | length" 2>/dev/null || echo 0)
|
|
398
|
+
else
|
|
399
|
+
TG_OVERRIDE=0
|
|
400
|
+
fi
|
|
401
|
+
|
|
402
|
+
if [ "${TG_OVERRIDE:-0}" -gt 0 ]; then
|
|
403
|
+
OVERRIDE_REASON=$(gh pr view "$PR_NUMBER" ${GH_FLAG} \
|
|
404
|
+
--json comments \
|
|
405
|
+
--jq "[.comments[].body | select(startswith(\"${OVERRIDE_PHRASE}\"))] | last" 2>/dev/null || echo "(reason not captured)")
|
|
406
|
+
echo "⚠️ Test gate: BLOCK — but override comment detected on PR #${PR_NUMBER}."
|
|
407
|
+
echo " Override: ${OVERRIDE_REASON}"
|
|
408
|
+
echo " Proceeding with deploy. Override is logged in Phase 8 summary."
|
|
409
|
+
TEST_GATE_VERDICT="PASS"
|
|
410
|
+
TEST_GATE_REASON="BLOCK downgraded to PASS by override: ${OVERRIDE_REASON}"
|
|
411
|
+
|
|
412
|
+
elif [ "$GATE_POSTURE" = "advisory" ]; then
|
|
413
|
+
echo "⚠️ Test gate: BLOCK (advisory posture) — runtime failures detected but deploy is NOT prevented."
|
|
414
|
+
echo " Switch verification.test_gate.posture to 'blocking' in forge.yaml to enforce this gate."
|
|
415
|
+
TEST_GATE_REASON="BLOCK (advisory) — runtime failures detected; deploy allowed by advisory posture"
|
|
416
|
+
|
|
417
|
+
else
|
|
418
|
+
# blocking posture (default) — STOP
|
|
419
|
+
echo "⛔ DEPLOY BLOCKED — /test-gate returned BLOCK verdict."
|
|
420
|
+
echo ""
|
|
421
|
+
echo "Runtime failures were detected in the staging→${DEFAULT_BRANCH} bundle."
|
|
422
|
+
echo "The failures were batch-introduced (not pre-existing on ${DEFAULT_BRANCH} baseline)."
|
|
423
|
+
echo ""
|
|
424
|
+
echo "Options:"
|
|
425
|
+
echo " 1. Fix the failing tests and merge fixes to staging before retrying the deploy."
|
|
426
|
+
echo " 2. Post a comment on this PR starting with \"${OVERRIDE_PHRASE} <reason>\" to bypass this gate."
|
|
427
|
+
echo " 3. Set verification.test_gate.posture: advisory in forge.yaml to downgrade to a warning."
|
|
428
|
+
echo ""
|
|
429
|
+
echo "RESULT: BLOCK DEPLOY"
|
|
430
|
+
|
|
431
|
+
# Post structured FORGE:GATE_FAILURE comment for pipeline-health tracking
|
|
432
|
+
GATE_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
433
|
+
if [ -n "$PR_NUMBER" ]; then
|
|
434
|
+
gh pr comment "$PR_NUMBER" ${GH_FLAG} --body "<!-- FORGE:GATE_FAILURE -->
|
|
435
|
+
## Deploy Gate: BLOCKED
|
|
436
|
+
|
|
437
|
+
**Gate**: test-gate
|
|
438
|
+
**Timestamp**: ${GATE_TIMESTAMP}
|
|
439
|
+
**Bundle PRs**: $(echo $ALL_PR_NUMBERS | tr '\n' ' ')
|
|
440
|
+
**Posture**: ${GATE_POSTURE}
|
|
441
|
+
|
|
442
|
+
### What Happened
|
|
443
|
+
|
|
444
|
+
\`/test-gate\` detected runtime failures in the staging→\`${DEFAULT_BRANCH}\` bundle that were NOT present on the \`${DEFAULT_BRANCH}\` baseline. These are batch-introduced regressions.
|
|
445
|
+
|
|
446
|
+
\`/test-gate\` has filed \`test-failure\` issues for each failing cluster — check the issue tracker for details.
|
|
447
|
+
|
|
448
|
+
### Resolution
|
|
449
|
+
|
|
450
|
+
1. Fix the failing tests and merge fixes to staging.
|
|
451
|
+
2. Retry the staging→\`${DEFAULT_BRANCH}\` deploy.
|
|
452
|
+
|
|
453
|
+
To override (ship known failures with documented reason), post a comment containing:
|
|
454
|
+
\`${OVERRIDE_PHRASE} <reason>\`
|
|
455
|
+
|
|
456
|
+
<!-- FORGE:GATE_FAILURE:TYPE=test-gate|BUNDLE=$(echo $ALL_PR_NUMBERS | tr '\n' ' ' | xargs) -->" 2>/dev/null || true
|
|
457
|
+
fi
|
|
458
|
+
exit 1
|
|
459
|
+
fi
|
|
460
|
+
;;
|
|
461
|
+
|
|
462
|
+
*)
|
|
463
|
+
echo "⚠️ Test gate: unrecognised verdict '${TEST_GATE_VERDICT}' — treating as SKIP."
|
|
464
|
+
TEST_GATE_VERDICT="SKIP"
|
|
465
|
+
TEST_GATE_REASON="SKIP — unrecognised verdict from /test-gate (treated as SKIP)"
|
|
466
|
+
;;
|
|
467
|
+
|
|
468
|
+
esac
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
If the gate exits with `RESULT: BLOCK DEPLOY` → **STOP**. A `<!-- FORGE:GATE_FAILURE -->` structured comment is automatically posted on the staging→main PR (if `$PR_NUMBER` is set) for pipeline-health tracking. `/test-gate` will have filed `test-failure` issues for each failing cluster before returning BLOCK.
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
343
475
|
## Phase 7: Finding Triage & Issue Creation
|
|
344
476
|
|
|
345
477
|
### 7A: Extract Findings
|
|
@@ -422,10 +554,36 @@ Labels: `review-finding` + `needs-validation` + `staging-review` + priority (`pr
|
|
|
422
554
|
Post summary with verdict:
|
|
423
555
|
1. CI failed + autofix failed → BLOCK DEPLOY
|
|
424
556
|
2. CI failed + autofix succeeded → continue
|
|
557
|
+
2.5. Test gate returned BLOCK (blocking posture, no override) → BLOCK DEPLOY *(handled in Phase 6.5 — if Phase 8 is reached, BLOCK was either overridden or posture is advisory)*
|
|
425
558
|
3. CONFIRMED CRITICAL (non-CI) → BLOCK DEPLOY
|
|
426
559
|
4. CONFIRMED HIGH blocking (crashes, data loss) → NEEDS FIXES FIRST
|
|
427
560
|
5. All else → APPROVE FOR DEPLOY
|
|
428
561
|
|
|
429
|
-
Include: Material Changes Summary, Risk Matrix (CI, Build, Bugs, Security, Billing, Quality, Regression), Finding Triage Results, Blocking Issues, Deployment Checklist (pre-deploy, deploy, post-deploy verification, rollback triggers), Stats.
|
|
562
|
+
Include: Material Changes Summary, Risk Matrix (CI, Build, Bugs, Security, Billing, Quality, Regression, **Test Gate**), Finding Triage Results, Blocking Issues, Deployment Checklist (pre-deploy, deploy, post-deploy verification, rollback triggers), Stats.
|
|
563
|
+
|
|
564
|
+
**Risk Matrix must include a Test Gate row** (use `$TEST_GATE_VERDICT` and `$TEST_GATE_REASON` set in Phase 6.5):
|
|
565
|
+
|
|
566
|
+
| Domain | Result | Notes |
|
|
567
|
+
|--------|--------|-------|
|
|
568
|
+
| CI | ... | ... |
|
|
569
|
+
| Build | ... | ... |
|
|
570
|
+
| Bugs | ... | ... |
|
|
571
|
+
| Security | ... | ... |
|
|
572
|
+
| Billing | ... | ... |
|
|
573
|
+
| Quality | ... | ... |
|
|
574
|
+
| Regression | ... | ... |
|
|
575
|
+
| **Test Gate** | `${TEST_GATE_VERDICT:-SKIP}` | `${TEST_GATE_REASON:-Phase 6.5 not run}` |
|
|
576
|
+
|
|
577
|
+
If `TEST_GATE_VERDICT` is `SKIP`, surface the gap explicitly in the summary:
|
|
578
|
+
|
|
579
|
+
> **Test Gate: SKIP** — No runtime tests ran for this bundle. This means acceptance criteria were NOT verified against running code before deploy. Cause: `${TEST_GATE_REASON}`. To enable runtime testing, configure `verification.integration_tests` in `forge.yaml`.
|
|
580
|
+
|
|
581
|
+
If `TEST_GATE_VERDICT` is `PASS`, note it as a positive signal:
|
|
582
|
+
|
|
583
|
+
> **Test Gate: PASS** — Acceptance criteria verified against running code. No batch-introduced runtime failures detected.
|
|
584
|
+
|
|
585
|
+
If `TEST_GATE_VERDICT` is `BLOCK` and Phase 8 was reached (advisory posture or override active), note it as a risk:
|
|
586
|
+
|
|
587
|
+
> **Test Gate: BLOCK (override/advisory)** — Runtime failures were detected but deploy is proceeding. Reason: `${TEST_GATE_REASON}`. Filed `test-failure` issues track the failures.
|
|
430
588
|
|
|
431
589
|
**CRITICAL**: This review NEVER merges staging → main. User makes deploy decision via GitHub web UI.
|
package/commands/review-pr.md
CHANGED
|
@@ -3,6 +3,8 @@ description: Context-aware PR review — analyzes what the PR touches, spawns do
|
|
|
3
3
|
argument-hint: [PR number, URL, "open", or "staging" for feature→main review]
|
|
4
4
|
allowed-tools: Task, Bash, Read, Grep, Glob, WebFetch, Skill
|
|
5
5
|
---
|
|
6
|
+
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
7
|
+
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
6
8
|
|
|
7
9
|
# PR Review — Orchestrator
|
|
8
10
|
|
|
@@ -124,6 +126,9 @@ while [ "$MERGE_HEALTH" = "UNKNOWN" ] && [ "$MERGE_RETRY" -lt 3 ]; do
|
|
|
124
126
|
done
|
|
125
127
|
# MERGE_HEALTH: MERGEABLE | CONFLICTING | UNKNOWN (still async after retries)
|
|
126
128
|
# MERGE_HEALTH_STATE: CLEAN | DIRTY | BLOCKED | UNSTABLE | UNKNOWN
|
|
129
|
+
|
|
130
|
+
# Resolve repo name early — used in Phases 5, 6, 8B, 9A (clean-review skip path bypasses Phase 6A) <!-- Added: forge#820 -->
|
|
131
|
+
REPO=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')
|
|
127
132
|
```
|
|
128
133
|
|
|
129
134
|
### 1B: Classify
|
|
@@ -731,7 +736,6 @@ If synthesis needed, launch a `general-purpose` Task (model: resolved per policy
|
|
|
731
736
|
### 6A: Extract Findings
|
|
732
737
|
|
|
733
738
|
```bash
|
|
734
|
-
REPO=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')
|
|
735
739
|
HAS_SYNTHESIS=$(gh api "repos/${REPO}/issues/${PR_NUMBER}/comments" --jq '.[].body' | grep -c 'REVIEW-FINDINGS-SYNTHESIZED-START' || echo 0)
|
|
736
740
|
|
|
737
741
|
if [ "$HAS_SYNTHESIS" -gt 0 ]; then
|
|
@@ -786,8 +790,8 @@ fi
|
|
|
786
790
|
|
|
787
791
|
if [ -n "$MILESTONE_BRANCH" ]; then
|
|
788
792
|
BRANCH_SLUG=$(echo "$MILESTONE_BRANCH" | sed 's|^milestone/||')
|
|
789
|
-
MILESTONE_NUMBER=$(gh api repos/${REPO}/milestones
|
|
790
|
-
[ -z "$MILESTONE_NUMBER" ] && MILESTONE_NUMBER=$(gh api repos/${REPO}/milestones
|
|
793
|
+
MILESTONE_NUMBER=$(gh api repos/${REPO}/milestones 2>/dev/null | jq --arg slug "$BRANCH_SLUG" '.[] | select((.title | ascii_downcase | gsub("[^a-z0-9]+"; "-")) == $slug) | .number' 2>/dev/null | head -1)
|
|
794
|
+
[ -z "$MILESTONE_NUMBER" ] && MILESTONE_NUMBER=$(gh api repos/${REPO}/milestones 2>/dev/null | jq --arg slug "$BRANCH_SLUG" '.[] | select((.title | ascii_downcase | gsub("[^a-z0-9]+"; "-")) | test($slug)) | .number' 2>/dev/null | head -1)
|
|
791
795
|
[ -n "$MILESTONE_NUMBER" ] && MILESTONE_FLAG="--milestone $(gh api repos/${REPO}/milestones/${MILESTONE_NUMBER} --jq '.title')"
|
|
792
796
|
fi
|
|
793
797
|
```
|
|
@@ -1060,12 +1064,89 @@ fi
|
|
|
1060
1064
|
|
|
1061
1065
|
**Important**: Phase 8 ONLY merges the PR. It does NOT close the issue, update labels, or clean up worktrees. When invoked via `/work-on`, those responsibilities belong to `work-on/close.md` — which runs after the router detects state 4 (MERGE_COMPLETE: PR merged + issue open). Doing them here would cause the router to hit TERMINAL_MERGED (state 1) and skip the close phase entirely.
|
|
1062
1066
|
|
|
1067
|
+
### 8B: Post-Merge Review Finding Demilestoning (Milestone PRs Only)
|
|
1068
|
+
|
|
1069
|
+
**Skip if**: `IS_MILESTONE_TO_STAGING` is false or `MERGE_STATE != "MERGED"`. Runs only when a milestone→staging PR was just successfully merged.
|
|
1070
|
+
|
|
1071
|
+
**Purpose**: Review-finding issues created during a milestone PR review (Phase 6C) inherit the milestone. Once the milestone PR merges, those findings should flow through the fast lane independently — not remain stranded on a closed milestone. This step clears their milestone assignment automatically.
|
|
1072
|
+
|
|
1073
|
+
```bash
|
|
1074
|
+
if [ "${IS_MILESTONE_TO_STAGING:-false}" = "true" ] && [ "${MERGE_STATE:-}" = "MERGED" ]; then
|
|
1075
|
+
echo "Phase 8B: Clearing milestone from open review-finding issues referencing PR #${PR_NUMBER}..."
|
|
1076
|
+
|
|
1077
|
+
# Find open review-finding issues whose body references this PR number
|
|
1078
|
+
# The title template in Phase 6C always includes: "review finding — PR #${PR_NUMBER}"
|
|
1079
|
+
FINDINGS_TO_DEMILESTONE=$(gh issue list -R "${REPO}" \
|
|
1080
|
+
--state open \
|
|
1081
|
+
--label "review-finding" \
|
|
1082
|
+
--limit 200 \
|
|
1083
|
+
--json number,title,milestone \
|
|
1084
|
+
--jq ".[] | select(.milestone != null) | select(.title | test(\"PR #${PR_NUMBER}\")) | .number" \
|
|
1085
|
+
2>/dev/null || echo "")
|
|
1086
|
+
|
|
1087
|
+
if [ -z "$FINDINGS_TO_DEMILESTONE" ]; then
|
|
1088
|
+
echo "Phase 8B: No open review-finding issues with milestones found referencing PR #${PR_NUMBER}."
|
|
1089
|
+
else
|
|
1090
|
+
MOVED_COUNT=0
|
|
1091
|
+
echo "$FINDINGS_TO_DEMILESTONE" | while IFS= read -r FINDING_NUM; do
|
|
1092
|
+
[ -z "$FINDING_NUM" ] && continue
|
|
1093
|
+
FINDING_TITLE=$(gh issue view "$FINDING_NUM" -R "${REPO}" --json title --jq '.title' 2>/dev/null || echo "#${FINDING_NUM}")
|
|
1094
|
+
gh issue edit "$FINDING_NUM" -R "${REPO}" --milestone "" 2>/dev/null && \
|
|
1095
|
+
echo " Moved to fast lane: #${FINDING_NUM} — ${FINDING_TITLE}" || \
|
|
1096
|
+
echo " WARNING: Failed to clear milestone for #${FINDING_NUM}"
|
|
1097
|
+
MOVED_COUNT=$((MOVED_COUNT + 1))
|
|
1098
|
+
done
|
|
1099
|
+
echo "Phase 8B: Review finding demilestoning complete."
|
|
1100
|
+
fi
|
|
1101
|
+
fi
|
|
1102
|
+
```
|
|
1103
|
+
|
|
1104
|
+
<!-- Added: forge#815 -->
|
|
1105
|
+
|
|
1063
1106
|
---
|
|
1064
1107
|
|
|
1065
1108
|
## Phase 9: Integrity Check & Summary (LAST)
|
|
1066
1109
|
|
|
1067
1110
|
**Run AFTER Phase 6 (issues created), Phase 7 (verdict posted), Phase 8 (merge if applicable).**
|
|
1068
1111
|
|
|
1112
|
+
### 9A: Post-Merge Review Finding Demilestoning Fallback (Milestone PRs Only)
|
|
1113
|
+
|
|
1114
|
+
**Skip if**: `IS_MILESTONE_TO_STAGING` is false. Runs when `AUTO_MERGE=false` but the PR was merged manually — Phase 8B did not run in this case, so Phase 9 handles cleanup.
|
|
1115
|
+
|
|
1116
|
+
**Detection**: Check if the PR is now MERGED. If so and `IS_MILESTONE_TO_STAGING=true`, run the same demilestoning logic as Phase 8B.
|
|
1117
|
+
|
|
1118
|
+
```bash
|
|
1119
|
+
if [ "${IS_MILESTONE_TO_STAGING:-false}" = "true" ]; then
|
|
1120
|
+
PR_MERGE_STATE=$(gh pr view $ARGUMENTS --json state --jq '.state' 2>/dev/null || echo "")
|
|
1121
|
+
if [ "$PR_MERGE_STATE" = "MERGED" ]; then
|
|
1122
|
+
echo "Phase 9A: Checking for open review-finding issues to demilestone (fallback — manual merge path)..."
|
|
1123
|
+
|
|
1124
|
+
FINDINGS_TO_DEMILESTONE=$(gh issue list -R "${REPO}" \
|
|
1125
|
+
--state open \
|
|
1126
|
+
--label "review-finding" \
|
|
1127
|
+
--limit 200 \
|
|
1128
|
+
--json number,title,milestone \
|
|
1129
|
+
--jq ".[] | select(.milestone != null) | select(.title | test(\"PR #${PR_NUMBER}\")) | .number" \
|
|
1130
|
+
2>/dev/null || echo "")
|
|
1131
|
+
|
|
1132
|
+
if [ -z "$FINDINGS_TO_DEMILESTONE" ]; then
|
|
1133
|
+
echo "Phase 9A: No open review-finding issues with milestones found referencing PR #${PR_NUMBER} (already cleared or none created)."
|
|
1134
|
+
else
|
|
1135
|
+
echo "$FINDINGS_TO_DEMILESTONE" | while IFS= read -r FINDING_NUM; do
|
|
1136
|
+
[ -z "$FINDING_NUM" ] && continue
|
|
1137
|
+
FINDING_TITLE=$(gh issue view "$FINDING_NUM" -R "${REPO}" --json title --jq '.title' 2>/dev/null || echo "#${FINDING_NUM}")
|
|
1138
|
+
gh issue edit "$FINDING_NUM" -R "${REPO}" --milestone "" 2>/dev/null && \
|
|
1139
|
+
echo " Moved to fast lane: #${FINDING_NUM} — ${FINDING_TITLE}" || \
|
|
1140
|
+
echo " WARNING: Failed to clear milestone for #${FINDING_NUM}"
|
|
1141
|
+
done
|
|
1142
|
+
echo "Phase 9A: Fallback demilestoning complete."
|
|
1143
|
+
fi
|
|
1144
|
+
fi
|
|
1145
|
+
fi
|
|
1146
|
+
```
|
|
1147
|
+
|
|
1148
|
+
<!-- Added: forge#815 -->
|
|
1149
|
+
|
|
1069
1150
|
```bash
|
|
1070
1151
|
CURRENT_SHA=$(gh pr view $ARGUMENTS --json headRefOid --jq '.headRefOid')
|
|
1071
1152
|
REVIEW_IS_STALE="false"
|
|
@@ -1118,4 +1199,5 @@ Review complete for PR #X. Verdict: [VERDICT].
|
|
|
1118
1199
|
- Integration checks: [pass/N broken paths found]
|
|
1119
1200
|
- Issues created: [M]
|
|
1120
1201
|
{IF AUTO_MERGE: "PR merged and issue closed." / "Merge FAILED — see issue."}
|
|
1202
|
+
{IF IS_MILESTONE_TO_STAGING: "Review findings demilestoned: [N] issues moved to fast lane." / ""}
|
|
1121
1203
|
```
|
package/commands/rollback.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
description: Create a revert PR to roll back a shipped feature or fix that caused a production incident
|
|
3
3
|
argument-hint: [PR number to revert, or "last" for most recent deploy]
|
|
4
4
|
---
|
|
5
|
+
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
6
|
+
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
5
7
|
|
|
6
8
|
# /rollback — Automated Revert PR Creation
|
|
7
9
|
|