forgedock 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +244 -0
- package/README.md +251 -0
- package/bin/forgedock.mjs +232 -0
- package/commands/analytics.md +777 -0
- package/commands/audit-agents.md +410 -0
- package/commands/audit.md +410 -0
- package/commands/autopilot.md +432 -0
- package/commands/cleanup.md +264 -0
- package/commands/deploy-info.md +292 -0
- package/commands/failure-recon.md +375 -0
- package/commands/forge-stats.md +183 -0
- package/commands/geo-audit.md +343 -0
- package/commands/incident-response.md +384 -0
- package/commands/issue.md +506 -0
- package/commands/milestone.md +599 -0
- package/commands/orchestrate.md +1044 -0
- package/commands/pipeline-health.md +2343 -0
- package/commands/qa-sweep.md +237 -0
- package/commands/quality-gate.md +777 -0
- package/commands/review-pr-agents.md +1711 -0
- package/commands/review-pr-staging.md +325 -0
- package/commands/review-pr.md +888 -0
- package/commands/rollback.md +253 -0
- package/commands/security-audit.md +529 -0
- package/commands/sync-ecosystem.md +527 -0
- package/commands/validate.md +131 -0
- package/commands/work-on/build/architect.md +291 -0
- package/commands/work-on/build/context.md +349 -0
- package/commands/work-on/build/implement.md +280 -0
- package/commands/work-on/build/validate.md +292 -0
- package/commands/work-on/build.md +262 -0
- package/commands/work-on/close.md +350 -0
- package/commands/work-on/decompose.md +254 -0
- package/commands/work-on/investigate.md +437 -0
- package/commands/work-on/review.md +245 -0
- package/commands/work-on-monolithic.md +298 -0
- package/commands/work-on.md +1089 -0
- package/docs/CODEX.md +92 -0
- package/docs/WORKFLOW.md +104 -0
- package/package.json +45 -0
- package/scripts/verify-env-vars.sh +201 -0
- package/scripts/verify-host-headers.sh +117 -0
- package/scripts/verify-route-registration.sh +118 -0
- package/scripts/verify-sops-chain.sh +163 -0
|
@@ -0,0 +1,1089 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Pick up a GitHub issue and run the full investigate-build-review-merge pipeline
|
|
3
|
+
argument-hint: [issue number or "next" to pick highest priority]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /work-on — Full Issue Pipeline
|
|
7
|
+
|
|
8
|
+
**Input**: $ARGUMENTS
|
|
9
|
+
|
|
10
|
+
Orchestrator for the full issue lifecycle: investigate → decompose (if needed) → build → review → merge → close. GitHub issues are the persistent context layer — read existing comments before starting, write structured reports back, use `workflow:*` labels to track state.
|
|
11
|
+
|
|
12
|
+
**Agent model policy**: Default `model: "sonnet"`. Fallback: `model: "opus"` if Sonnet is rate-limited.
|
|
13
|
+
**NEVER use plan mode (EnterPlanMode).**
|
|
14
|
+
|
|
15
|
+
### Compaction Resilience
|
|
16
|
+
|
|
17
|
+
1. Write state to GitHub after EVERY significant step
|
|
18
|
+
2. Re-read GitHub state at the START of each phase (don't rely on in-memory context)
|
|
19
|
+
3. After compaction: re-read issue (body + comments + labels) to reconstruct state
|
|
20
|
+
4. Key principle: A NEW session running `/work-on {number}` should pick up where the last left off by reading GitHub state alone
|
|
21
|
+
|
|
22
|
+
### Universal Phase Dispatcher
|
|
23
|
+
|
|
24
|
+
<!-- FORGE:DISPATCHER — This is the SINGLE source of truth for phase transitions. Every phase boundary references this section. -->
|
|
25
|
+
|
|
26
|
+
**Phase sequence** (canonical order):
|
|
27
|
+
|
|
28
|
+
| Step | Phase | Entry Condition | Terminal? |
|
|
29
|
+
|------|-------|-----------------|-----------|
|
|
30
|
+
| 1 | Phase 0: Resolve Issue | Always first | No |
|
|
31
|
+
| 2 | Phase 1: Investigation | No `INVESTIGATION:COMPLETE` comment | No |
|
|
32
|
+
| 3 | Phase 1D: Route | Investigation complete | No |
|
|
33
|
+
| 4 | Phase 2: Decomposition | decompose: YES | Yes (spawns sub-issues) |
|
|
34
|
+
| 5 | Phase 3: Build (3A–3M) | `workflow:ready-to-build` or `workflow:building` | No |
|
|
35
|
+
| 6 | Phase 4: PR Creation | Builder comment posted, no PR exists | No |
|
|
36
|
+
| 7 | Phase 5: Auto-Review | PR exists, `workflow:in-review` | No |
|
|
37
|
+
| 8 | Phase 6: Close & Cleanup | PR merged | No |
|
|
38
|
+
| 9 | Phase 7: Trajectory | Issue closed | Yes |
|
|
39
|
+
|
|
40
|
+
**Phase 3 sub-phase sequence** (all mandatory, execute in order):
|
|
41
|
+
|
|
42
|
+
3A → 3B → 3C → 3C.5 → 3C.6 → 3D → 3E → 3F → 3F.5 → 3G → 3H → 3I → 3I.5 → 3J → 3K → 3L → 3M
|
|
43
|
+
|
|
44
|
+
**Universal continuation rule**: After ANY phase or sub-phase completes, check whether a terminal state has been reached. Terminal states are:
|
|
45
|
+
- `workflow:merged` label is set
|
|
46
|
+
- `workflow:invalid` label is set
|
|
47
|
+
- `needs-human` label is set
|
|
48
|
+
- `workflow:decomposed` label is set (sub-issues spawned)
|
|
49
|
+
- Issue state is CLOSED with terminal label
|
|
50
|
+
|
|
51
|
+
**If the current state is NOT terminal: proceed to the next phase in the sequence immediately. Do NOT stop. Do NOT emit a summary. Do NOT treat any intermediate phase completion as a terminal signal.** Every phase completion — investigation done, quality gate passed, PR merged, review complete — is an intermediate result. Only the terminal states listed above allow stopping.
|
|
52
|
+
|
|
53
|
+
**Adding a new phase**: Insert it into the phase sequence table above and the sub-phase sequence if it belongs to Phase 3. No per-boundary transition code is needed — the universal continuation rule handles all transitions.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Pipeline Rules
|
|
58
|
+
|
|
59
|
+
- **NEVER merge to main.** PRs target `staging` (fast lane) or `milestone/{slug}` (feature lane).
|
|
60
|
+
- **`Closes #N` does not auto-close for non-default-branch PRs.** You MUST explicitly `gh issue close`.
|
|
61
|
+
- **Review findings are NOT merge blockers.** They become separate issues.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Multi-Repo Support
|
|
66
|
+
|
|
67
|
+
| Input | Resolves To | GH_REPO | STAGING_BRANCH |
|
|
68
|
+
|-------|-------------|---------|----------------|
|
|
69
|
+
| `123` / `#123` | AlterLab (default) | `RapierCraft/AlterLab` | `staging` |
|
|
70
|
+
| `mcp:5` | MCP Server | `RapierCraft/alterlab-mcp-server` | `main` |
|
|
71
|
+
| `n8n:12` | n8n Node | `RapierCraft/n8n-nodes-alterlab` | `main` |
|
|
72
|
+
|
|
73
|
+
Satellite repos (MCP, n8n) have no staging — fast-lane PRs go to `main`.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Phase 0: Resolve Issue & Load Context
|
|
78
|
+
|
|
79
|
+
### 0A: Parse input
|
|
80
|
+
Extract project prefix and issue number. If `next`/`pick`: list open issues sorted by priority, skip `needs-human` and `workflow:decomposed`, pick highest priority.
|
|
81
|
+
|
|
82
|
+
### 0B: Load issue + existing context
|
|
83
|
+
```bash
|
|
84
|
+
gh issue view {NUMBER} {GH_FLAG} --json number,title,body,labels,state,comments,milestone
|
|
85
|
+
gh api repos/{GH_REPO}/issues/{NUMBER}/comments --jq '.[] | {id: .id, author: .user.login, body: .body}'
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**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.
|
|
89
|
+
|
|
90
|
+
**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.
|
|
91
|
+
|
|
92
|
+
**Classify lane**: Milestone → feature lane (`milestone/{slug}`). No milestone → fast lane (`staging`).
|
|
93
|
+
|
|
94
|
+
**Source branch for review-findings**: Parse `**Code branch**: \`{branch}\`` from body. Branch from there, not main.
|
|
95
|
+
|
|
96
|
+
### 0C: Sync to Project board
|
|
97
|
+
Add issue to project, set Status=In Progress, Lane, Component, Priority, Workflow=Investigating.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Phase 1: Investigation
|
|
102
|
+
|
|
103
|
+
**Skip if**: `<!-- FORGE:INVESTIGATOR -->` or `<!-- ALTERLAB:INVESTIGATOR -->` exists with `<!-- INVESTIGATION:COMPLETE -->` in the SAME comment.
|
|
104
|
+
|
|
105
|
+
**Partial investigation**: If investigator comment exists BUT `<!-- INVESTIGATION:COMPLETE -->` is ABSENT → investigation was interrupted. Delete the partial comment and restart:
|
|
106
|
+
```bash
|
|
107
|
+
COMMENT_ID=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
108
|
+
--jq '.[] | select(.body | (contains("FORGE:INVESTIGATOR") or contains("ALTERLAB:INVESTIGATOR"))) | .id')
|
|
109
|
+
gh api repos/{GH_REPO}/issues/comments/$COMMENT_ID -X DELETE
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 1A: Set label
|
|
113
|
+
```bash
|
|
114
|
+
gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:investigating"
|
|
115
|
+
gh issue edit {NUMBER} {GH_FLAG} --remove-label "workflow:ready-to-build,workflow:building,workflow:in-review" 2>/dev/null || true
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### 1A.5: Normalize Issue Body (MANDATORY)
|
|
119
|
+
|
|
120
|
+
Before investigation begins, verify the issue body contains the four mandatory pipeline sections. If any are missing, add placeholder content so the investigator has the correct scaffolding.
|
|
121
|
+
|
|
122
|
+
**Skip if**: All four sections (`## Problem`, `## Affected Files`, `## Expected Behavior`, `## Acceptance Criteria`) are already present.
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
ISSUE_BODY=$(gh issue view {NUMBER} {GH_FLAG} --json body --jq '.body')
|
|
126
|
+
|
|
127
|
+
MISSING_SECTIONS=""
|
|
128
|
+
echo "$ISSUE_BODY" | grep -q "^## Problem" || MISSING_SECTIONS="$MISSING_SECTIONS PROBLEM"
|
|
129
|
+
echo "$ISSUE_BODY" | grep -q "^## Affected Files" || MISSING_SECTIONS="$MISSING_SECTIONS AFFECTED_FILES"
|
|
130
|
+
echo "$ISSUE_BODY" | grep -q "^## Expected Behavior" || MISSING_SECTIONS="$MISSING_SECTIONS EXPECTED_BEHAVIOR"
|
|
131
|
+
echo "$ISSUE_BODY" | grep -q "^## Acceptance Criteria" || MISSING_SECTIONS="$MISSING_SECTIONS ACCEPTANCE_CRITERIA"
|
|
132
|
+
|
|
133
|
+
if [ -n "$MISSING_SECTIONS" ]; then
|
|
134
|
+
echo "Missing sections:$MISSING_SECTIONS — normalizing issue body before investigation"
|
|
135
|
+
|
|
136
|
+
APPEND_TEXT=""
|
|
137
|
+
echo "$MISSING_SECTIONS" | grep -q "PROBLEM" && APPEND_TEXT="$APPEND_TEXT
|
|
138
|
+
## Problem
|
|
139
|
+
|
|
140
|
+
Root cause unknown — investigation needed."
|
|
141
|
+
|
|
142
|
+
echo "$MISSING_SECTIONS" | grep -q "AFFECTED_FILES" && APPEND_TEXT="$APPEND_TEXT
|
|
143
|
+
## Affected Files
|
|
144
|
+
|
|
145
|
+
Files to be identified during investigation."
|
|
146
|
+
|
|
147
|
+
echo "$MISSING_SECTIONS" | grep -q "EXPECTED_BEHAVIOR" && APPEND_TEXT="$APPEND_TEXT
|
|
148
|
+
## Expected Behavior
|
|
149
|
+
|
|
150
|
+
Expected behavior to be determined during investigation."
|
|
151
|
+
|
|
152
|
+
echo "$MISSING_SECTIONS" | grep -q "ACCEPTANCE_CRITERIA" && APPEND_TEXT="$APPEND_TEXT
|
|
153
|
+
## Acceptance Criteria
|
|
154
|
+
|
|
155
|
+
- [ ] Fix confirmed during investigation."
|
|
156
|
+
|
|
157
|
+
# Append missing sections to the existing body (never replace — only extend)
|
|
158
|
+
NORMALIZED_BODY="${ISSUE_BODY}${APPEND_TEXT}"
|
|
159
|
+
gh issue edit {NUMBER} {GH_FLAG} --body "$NORMALIZED_BODY"
|
|
160
|
+
echo "Issue body normalized — added:$MISSING_SECTIONS"
|
|
161
|
+
else
|
|
162
|
+
echo "Issue body already contains all mandatory sections — skipping normalization"
|
|
163
|
+
fi
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Continue to Phase 1B unconditionally.** Normalization is a compensation step — it never blocks investigation.
|
|
167
|
+
|
|
168
|
+
### 1B: Investigate the issue
|
|
169
|
+
|
|
170
|
+
Mission: Validate whether the issue is real. Assume description is wrong until proven otherwise.
|
|
171
|
+
|
|
172
|
+
**Resolve target repo and branch**:
|
|
173
|
+
|
|
174
|
+
For Forge issues (repo = `RapierCraftStudios/forge`):
|
|
175
|
+
- Key files: `commands/work-on.md`, `commands/review-pr.md`, `commands/quality-gate.md`, `commands/orchestrate.md`, `install.sh`, `CLAUDE.md`
|
|
176
|
+
|
|
177
|
+
For AlterLab issues (repo = `RapierCraft/AlterLab`):
|
|
178
|
+
|
|
179
|
+
| Domain | Key files |
|
|
180
|
+
|--------|-----------|
|
|
181
|
+
| BILLING | `routers/billing.py`, `core/pricing.py`, `services/credit_service.py` |
|
|
182
|
+
| SCRAPING | `unified_consumer.py`, `queues.py`, `domain_playbooks.json` |
|
|
183
|
+
| AUTH | `core/auth.py`, `routers/auth.py`, `dependencies.py` |
|
|
184
|
+
| DATABASE | `infra/migrations/`, `models/`, `db/` |
|
|
185
|
+
| FRONTEND | `web/src/app/`, `web/src/components/`, `web/src/lib/` |
|
|
186
|
+
| CORTEX | `cortex_client.py`, `routers/cortex.py` |
|
|
187
|
+
| INFRA | `.github/workflows/`, `docker-compose.yml`, `infra/traefik/` |
|
|
188
|
+
|
|
189
|
+
**INFRA domain known footguns** (read before writing any `.github/workflows/*.yml` changes):
|
|
190
|
+
- **appleboy/ssh-action Go template preprocessing**: Any `{{` in a `script:` block is interpreted as a Go template directive **before the script reaches SSH**. This means `docker ps --format '{{.Names}}'` and `docker inspect --format '{{index .RepoTags 0}}'` will crash the action with exit 1. Both function calls (`{{index .X Y}}`) AND field accessors (`{{.Names}}`, `{{.Status}}`) fail on the action's empty data context. Shell error handlers (`|| fallback`, `set -e`, `2>/dev/null`) are bypassed because the failure is client-side. Always use `docker inspect IMAGE | jq -r '.[0].RepoTags[0]'` and `docker ps --format json | jq -r '.Names'` patterns in `appleboy/ssh-action` scripts. (Ref: forge#226 — 6-day silent deploy failure masked by `continue-on-error: true`)
|
|
191
|
+
|
|
192
|
+
**Steps**:
|
|
193
|
+
1. Check the right branch — read from branch specified in issue body (`**Code branch**: \`{branch}\``) if present
|
|
194
|
+
2. Read domain files — start with key files for the affected domain
|
|
195
|
+
2.5. **Existing system search (conditional)**: If the issue describes a gap in a functional capability — content not being distributed, notifications not sending, jobs not running, data not being synced — MUST search for an existing automated system before proposing a new one. The issue body may name a specific tool or path (e.g., `reddit-bot/`, `marketing/`) — do NOT anchor on that path alone. Expand the search to all service layers:
|
|
196
|
+
```bash
|
|
197
|
+
# For AlterLab: check Herald, worker, and scheduler for the capability
|
|
198
|
+
grep -rn "{capability_keyword}" services/herald/app/scheduler/ services/herald/app/content/ services/worker/
|
|
199
|
+
grep -rn "{capability_keyword}" services/ --include="*.py" -l | head -20
|
|
200
|
+
# Look for scheduled jobs, automated runners, existing integrations
|
|
201
|
+
grep -rn "scheduler\|celery\|cron\|nightly\|periodic" services/ --include="*.py" -l | head -10
|
|
202
|
+
```
|
|
203
|
+
If an existing system is found that already handles the capability: the fix MUST route through the existing system (fix its config, env var, or gate) — NOT create a new parallel tool. Document the existing system in the investigation report and make it the centerpiece of the recommendation. This check is especially critical when the issue references a standalone tool directory (`reddit-bot/`, `scripts/`, `tools/`) — those directories often duplicate functionality that a service already owns. (Ref: forge#279 — investigator anchored on `reddit-bot/` from issue body, never checked `services/herald/app/scheduler/`, built parallel PRAW integration alongside Herald's existing automated crosspost scheduler)
|
|
204
|
+
3. Verify claims — does the code actually have the problem described?
|
|
205
|
+
3.5. **Type Invariant Verification (MANDATORY)**: Before declaring that a field, key, or parameter has a specific type (e.g. "content is always a dict", "status is always an int"), search for ALL code paths that write to that field across ALL services:
|
|
206
|
+
```bash
|
|
207
|
+
grep -rn '"field_name"\s*:' services/ # Python dict key assignments
|
|
208
|
+
grep -rn 'result\["field_name"\]\s*=' services/ # Direct assignments
|
|
209
|
+
grep -rn '\.field_name\s*=' services/ # Attribute assignments
|
|
210
|
+
```
|
|
211
|
+
If the field is written with different types in different code paths (e.g. dict in the standard path, string in the auth-gated path), document ALL variants. The fix must handle every variant — not just the one on the primary investigated code path. A type guard like `or {}` only protects against falsy values; a non-empty string is truthy and bypasses it.
|
|
212
|
+
4. Git blame — trace when/why the relevant code was written
|
|
213
|
+
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)
|
|
214
|
+
5. Domain context discovery (narrow scope only, 1–5 files):
|
|
215
|
+
```bash
|
|
216
|
+
git log --oneline --all -30 -- {affected_files} | grep -oP '#\d+' | sort -u
|
|
217
|
+
gh issue list -R {GH_REPO} --state closed --limit 8 --search "{function_name}"
|
|
218
|
+
```
|
|
219
|
+
Keep only file/function-level overlap. Max 5 related issues.
|
|
220
|
+
6. Determine root cause
|
|
221
|
+
7. Identify affected files — full list of files that need changes
|
|
222
|
+
7.5. **Sibling Pattern Sweep** *(conditional — when the bug is a condition, gated function call, or field presence check)*: After identifying the affected files, grep for the same pattern in sibling files within the same directory. The issue spec may name only the file where the error was first observed — but the same commit or PR that introduced the bug often applied it uniformly across related handlers.
|
|
223
|
+
```bash
|
|
224
|
+
# Identify the broken condition or gated function call from the issue
|
|
225
|
+
# Then search sibling files in the same router/service directory
|
|
226
|
+
AFFECTED_DIR=$(dirname {PRIMARY_AFFECTED_FILE})
|
|
227
|
+
grep -rn "{broken_pattern}" "$AFFECTED_DIR" --include="*.py" | grep -v "{PRIMARY_AFFECTED_FILE}"
|
|
228
|
+
```
|
|
229
|
+
**If identical patterns are found in files NOT listed in the issue spec**, output a scope-gap warning:
|
|
230
|
+
> **Scope-Gap Warning**: The issue spec lists `{PRIMARY_FILE}` but the same pattern exists in `{SIBLING_FILE}:{LINE}`. These were likely introduced together. Recommend widening scope to fix all callers in this PR, or creating follow-up issues for the other files before proceeding.
|
|
231
|
+
|
|
232
|
+
Do NOT silently exclude sibling matches. The appropriate output when sibling files have the same bug is to flag them explicitly — even if the issue spec's silence appears intentional. The fix-approach validation step (step 8) will confirm whether to widen scope or create follow-ups. <!-- Added: forge#383 -->
|
|
233
|
+
8. Fix-approach validation — if issue proposes a fix, don't adopt as spec. Trace through middleware, auth, routing, config. Cross-domain: if fix in domain A interacts with domain B, read domain B's files too.
|
|
234
|
+
|
|
235
|
+
### 1C: Post investigation comment
|
|
236
|
+
|
|
237
|
+
The comment MUST include `<!-- INVESTIGATION:COMPLETE -->` at the very end.
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:INVESTIGATOR -->
|
|
241
|
+
## Investigation Report
|
|
242
|
+
|
|
243
|
+
**Verdict**: {CONFIRMED|PARTIAL|INVALID}
|
|
244
|
+
**Confidence**: {HIGH|MEDIUM|LOW}
|
|
245
|
+
**Severity**: {CRITICAL|HIGH|MEDIUM|LOW}
|
|
246
|
+
**Task Type**: {Bug Fix|Feature|Refactor|Maintenance|Investigation}
|
|
247
|
+
|
|
248
|
+
### What Was Claimed
|
|
249
|
+
{summary of what the issue describes}
|
|
250
|
+
|
|
251
|
+
### What We Found
|
|
252
|
+
{what the code actually shows}
|
|
253
|
+
|
|
254
|
+
### Root Cause
|
|
255
|
+
{specific root cause, with file:line references where applicable}
|
|
256
|
+
|
|
257
|
+
### Affected Files
|
|
258
|
+
{numbered list of files that need changes}
|
|
259
|
+
|
|
260
|
+
### Evidence
|
|
261
|
+
{specific findings — function names, line numbers, behavior observed}
|
|
262
|
+
|
|
263
|
+
### Recommendation
|
|
264
|
+
{what to build/fix, concrete and actionable}
|
|
265
|
+
|
|
266
|
+
### Related Issues
|
|
267
|
+
{if any found via domain context discovery, max 5}
|
|
268
|
+
|
|
269
|
+
### Decomposition Assessment
|
|
270
|
+
**{YES|NO}** — {reason}
|
|
271
|
+
{if YES: proposed sub-issues with titles and dependencies}
|
|
272
|
+
|
|
273
|
+
<!-- INVESTIGATION:COMPLETE -->"
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### 1D: Update labels & route
|
|
277
|
+
|
|
278
|
+
**CONFIRMED or PARTIAL with decompose: NO**:
|
|
279
|
+
```bash
|
|
280
|
+
gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:ready-to-build" --remove-label "workflow:investigating"
|
|
281
|
+
```
|
|
282
|
+
<!-- FORGE:PHASE_COMPLETE — Investigation routed to build. See Universal Phase Dispatcher: next phase is Phase 3. Not terminal — continue immediately. -->
|
|
283
|
+
→ Continue to Phase 3.
|
|
284
|
+
|
|
285
|
+
**CONFIRMED or PARTIAL with decompose: YES**:
|
|
286
|
+
```bash
|
|
287
|
+
gh issue edit {NUMBER} {GH_FLAG} --remove-label "workflow:investigating"
|
|
288
|
+
```
|
|
289
|
+
<!-- FORGE:PHASE_COMPLETE — Investigation routed to decomposition. See Universal Phase Dispatcher: next phase is Phase 2. Not terminal — continue immediately. -->
|
|
290
|
+
→ Continue to Phase 2 (Decomposition).
|
|
291
|
+
|
|
292
|
+
**INVALID**:
|
|
293
|
+
```bash
|
|
294
|
+
gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:invalid" --remove-label "workflow:investigating"
|
|
295
|
+
gh issue close {NUMBER} {GH_FLAG} --comment "Closing as invalid: {reason from investigation}"
|
|
296
|
+
```
|
|
297
|
+
→ STOP.
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## Phase 2: Decomposition (Conditional)
|
|
302
|
+
|
|
303
|
+
**Skip if**: Already decomposed, is a sub-issue, or investigation says decompose: NO.
|
|
304
|
+
|
|
305
|
+
**Trigger if**: Investigation assessment says YES — 2+ signals match (at least 1 Strong): multiple task types, 3+ service groups, phased requirements, 6+ files across directories.
|
|
306
|
+
|
|
307
|
+
### 2A: Load state
|
|
308
|
+
```bash
|
|
309
|
+
gh issue view {NUMBER} {GH_FLAG} --json number,title,body,labels,state,milestone
|
|
310
|
+
gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
311
|
+
--jq '.[] | select(.body | (contains("FORGE:INVESTIGATOR") or contains("ALTERLAB:INVESTIGATOR"))) | .body'
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
**MANDATORY — Owner override detection**: After reading the investigation comment, read ALL comments on the issue to check for owner override signals:
|
|
315
|
+
```bash
|
|
316
|
+
gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
317
|
+
--jq '.[] | select(.body | (contains("FORGE:INVESTIGATOR") or contains("ALTERLAB:INVESTIGATOR")) | not) | {author: .user.login, body: .body}'
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Scan non-agent comments for override signals — phrases like "do not", "do NOT", "instead", "revert", "remove this", "override", "actually", or explicit disagreement with the investigation's recommendation. If an override comment is found from a repo owner or admin (not a bot):
|
|
321
|
+
|
|
322
|
+
1. **Document the override**: Note which direction the owner is steering (e.g., "remove the feature" vs. investigation's "keep with warnings")
|
|
323
|
+
2. **Re-derive sub-issue scopes**: Derive sub-issue titles, bodies, and file scope from the override direction — NOT from the original investigation recommendation. The investigation's Decomposition Assessment may list sub-issues that are now stale or contradictory with the override.
|
|
324
|
+
3. **If override makes a sub-issue obsolete**: Skip creating it. Note the skip reason in the decomposition comment.
|
|
325
|
+
4. **If override changes the sequencing dependency**: Revise the execution order so that the override's primary action (e.g., "strip the feature") completes before any downstream doc/SDK sub-issues are built against it.
|
|
326
|
+
|
|
327
|
+
**Why this matters**: Sub-issues scoped before an override are built against a stale premise. A docs sub-issue scoped as "neutralize liability language" becomes incorrect if the upstream schema sub-issue will fully remove the feature — the docs sub-issue should instead be "remove all references to the deleted feature." Building both in parallel against the pre-override scope produces contradictory staging state.
|
|
328
|
+
|
|
329
|
+
### 2B: Design sub-issues
|
|
330
|
+
From the Decomposition Assessment (adjusted for any owner override detected in 2A), extract sub-issue titles (dependency order — independent first), dependencies, and descriptions.
|
|
331
|
+
|
|
332
|
+
For each sub-issue:
|
|
333
|
+
- **Title**: from investigation report
|
|
334
|
+
- **Body**: brief scope + `**Parent**: #{NUMBER}` + dependency note
|
|
335
|
+
- **Labels**: inherit priority label from parent; do NOT copy workflow labels
|
|
336
|
+
- **Milestone**: same as parent
|
|
337
|
+
|
|
338
|
+
### 2C: Create sub-issues
|
|
339
|
+
```bash
|
|
340
|
+
gh issue create {GH_FLAG} \
|
|
341
|
+
--title "{fix|feat|refactor}: {SUB_ISSUE_TITLE}" \
|
|
342
|
+
--body "$(cat <<'SUB_BODY_EOF'
|
|
343
|
+
## Problem
|
|
344
|
+
|
|
345
|
+
{1-3 sentences: what this sub-issue specifically addresses. What's wrong or what needs to be built.}
|
|
346
|
+
|
|
347
|
+
## Root Cause (if known)
|
|
348
|
+
|
|
349
|
+
{Specific root cause for this sub-task. Reference the parent investigation findings where applicable. If unknown: "Root cause unknown — investigation needed."}
|
|
350
|
+
|
|
351
|
+
## Affected Files
|
|
352
|
+
|
|
353
|
+
Files that need changes:
|
|
354
|
+
1. `{filepath}` — {what needs to change}
|
|
355
|
+
2. `{filepath}` — {what needs to change}
|
|
356
|
+
|
|
357
|
+
## Acceptance Criteria
|
|
358
|
+
|
|
359
|
+
- [ ] {Specific, testable criterion}
|
|
360
|
+
- [ ] {Specific, testable criterion}
|
|
361
|
+
- [ ] No regression in {related feature}
|
|
362
|
+
|
|
363
|
+
## Context
|
|
364
|
+
|
|
365
|
+
**Parent**: #{NUMBER}
|
|
366
|
+
{If depends on another sub-issue: "**Depends on**: #{SUB_ISSUE_N} — {reason}"}
|
|
367
|
+
SUB_BODY_EOF
|
|
368
|
+
)" \
|
|
369
|
+
--label "{PRIORITY_LABEL}" \
|
|
370
|
+
--milestone "{MILESTONE_TITLE}"
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### 2D: Update parent issue body
|
|
374
|
+
Add tracker checklist with all sub-issues in dependency order.
|
|
375
|
+
|
|
376
|
+
### 2E: Post decomposition comment
|
|
377
|
+
```bash
|
|
378
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:DECOMPOSED -->
|
|
379
|
+
## Decomposition Complete
|
|
380
|
+
|
|
381
|
+
### Sub-Issues Created
|
|
382
|
+
- #{SUB_NUMBER}: {TITLE}
|
|
383
|
+
|
|
384
|
+
### Decomposition Rationale
|
|
385
|
+
{brief summary}
|
|
386
|
+
|
|
387
|
+
<!-- FORGE:DECOMPOSED:COMPLETE -->"
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### 2F: Update labels
|
|
391
|
+
```bash
|
|
392
|
+
gh issue edit {NUMBER} {GH_FLAG} \
|
|
393
|
+
--add-label "workflow:decomposed" \
|
|
394
|
+
--remove-label "workflow:ready-to-build,workflow:building,workflow:investigating" 2>/dev/null || true
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
→ STOP. Each sub-issue runs its own `/work-on`.
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## Phase 3: Build
|
|
402
|
+
|
|
403
|
+
<!-- FORGE:PHASE_COMPLETE — Entering Phase 3 (Build). See Universal Phase Dispatcher: sub-phases 3A–3M execute in sequence. No sub-phase completion is terminal. -->
|
|
404
|
+
|
|
405
|
+
**Skip if**: `<!-- FORGE:BUILDER -->` or `<!-- ALTERLAB:BUILDER -->` exists.
|
|
406
|
+
|
|
407
|
+
**CRITICAL: You MUST execute ALL sub-phases 3A–3M in order. Do NOT skip phases 3C.5 (context) or 3C.6 (architect) — they post mandatory `FORGE:CONTEXT` and `FORGE:ARCHITECT` comments that Phase 3F reads as its primary input. Skipping them degrades build quality and causes review findings. After each sub-phase, continue to the next — no sub-phase is terminal.**
|
|
408
|
+
|
|
409
|
+
### 3A: Re-read state from GitHub (MANDATORY)
|
|
410
|
+
```bash
|
|
411
|
+
gh issue view {NUMBER} {GH_FLAG} --json number,title,body,labels,state,milestone
|
|
412
|
+
|
|
413
|
+
# Read investigation report
|
|
414
|
+
gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
415
|
+
--jq '.[] | select(.body | (contains("FORGE:INVESTIGATOR") or contains("ALTERLAB:INVESTIGATOR"))) | .body'
|
|
416
|
+
|
|
417
|
+
# Check if build already completed
|
|
418
|
+
gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
419
|
+
--jq '.[] | select(.body | (contains("FORGE:BUILDER") or contains("ALTERLAB:BUILDER"))) | .body'
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
If no investigation comment with `<!-- INVESTIGATION:COMPLETE -->` → STOP (investigation not complete).
|
|
423
|
+
|
|
424
|
+
Extract from investigation: affected files, root cause, recommendation, task type.
|
|
425
|
+
|
|
426
|
+
### 3B: Classify task type
|
|
427
|
+
|
|
428
|
+
| Signal | Type | Approach |
|
|
429
|
+
|--------|------|----------|
|
|
430
|
+
| Title: "Investigate"/"Audit"/"Research" | Investigation | Produce issues as deliverables |
|
|
431
|
+
| UI/UX, feature + web/ files | UI/UX | `frontend-design` skill |
|
|
432
|
+
| Feature + services/ | Backend Feature | Implement directly |
|
|
433
|
+
| Feature + both | Full-Stack | Backend first, then frontend-design |
|
|
434
|
+
| Bug + web/ | Frontend Fix | Direct |
|
|
435
|
+
| Bug + services/ | Backend Fix | Direct |
|
|
436
|
+
| Refactor/docs | Maintenance | Direct |
|
|
437
|
+
|
|
438
|
+
**Investigation tasks**: Research deeply, create GitHub issues for each finding, post deliverables comment, close issue, skip to Phase 7.
|
|
439
|
+
|
|
440
|
+
### 3C: Builder Contract (MANDATORY)
|
|
441
|
+
|
|
442
|
+
Post `<!-- FORGE:CONTRACT -->` comment with: task type, proposed approach, deliverables table (file/change/why), acceptance criteria, quality considerations (auth model, new env vars, SQL safety, security surface), out of scope, alternatives.
|
|
443
|
+
|
|
444
|
+
```bash
|
|
445
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:CONTRACT -->
|
|
446
|
+
## Builder Contract
|
|
447
|
+
|
|
448
|
+
**Task type**: {TASK_TYPE}
|
|
449
|
+
|
|
450
|
+
### Proposed Approach
|
|
451
|
+
{BRIEF_APPROACH_DESCRIPTION}
|
|
452
|
+
|
|
453
|
+
### Deliverables
|
|
454
|
+
| File | Change | Why |
|
|
455
|
+
|------|--------|-----|
|
|
456
|
+
{DELIVERABLES_ROWS}
|
|
457
|
+
|
|
458
|
+
### Acceptance Criteria
|
|
459
|
+
{ACCEPTANCE_CRITERIA_CHECKLIST}
|
|
460
|
+
|
|
461
|
+
### Quality Considerations
|
|
462
|
+
{AUTH_MODEL_NEW_ENV_VARS_SQL_SAFETY_SECURITY_SURFACE}
|
|
463
|
+
|
|
464
|
+
### Out of Scope
|
|
465
|
+
{OUT_OF_SCOPE_ITEMS}"
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
Contract must be grounded in the investigation report. Adversarially validate proposed fixes against adjacent system layers.
|
|
469
|
+
|
|
470
|
+
### 3C.5: Context Gathering (MANDATORY — max 2 minutes)
|
|
471
|
+
|
|
472
|
+
**This phase is NOT optional.** Always run it regardless of issue size or complexity. Do NOT skip it — trivial cases return quickly on their own.
|
|
473
|
+
|
|
474
|
+
Surface institutional memory before writing code. Extract function names from the contract deliverables table:
|
|
475
|
+
|
|
476
|
+
```bash
|
|
477
|
+
FUNCTION_NAMES=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
478
|
+
--jq '.[] | select(.body | contains("FORGE:CONTRACT")) | .body' \
|
|
479
|
+
| awk '/^### Deliverables/{p=1; next} /^### /{p=0} p' \
|
|
480
|
+
| grep -oP '`[A-Za-z_][A-Za-z0-9_]*`' \
|
|
481
|
+
| tr -d '`' | sort -u | tr '\n' ' ' | xargs)
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
**The ONLY acceptable skip conditions** (all must be true): Issue is a 1-file config/docs edit with no code logic AND affected files have zero git history. In all other cases, run context gathering.
|
|
485
|
+
|
|
486
|
+
Run these queries (20s timeout each, 2 min total budget):
|
|
487
|
+
|
|
488
|
+
**C1: Past Review Findings on These Files**
|
|
489
|
+
```bash
|
|
490
|
+
for file in {AFFECTED_FILES}; do
|
|
491
|
+
basename=$(basename "$file" .py)
|
|
492
|
+
gh issue list -R {GH_REPO} --state closed --label "review-finding" \
|
|
493
|
+
--search "$basename" --limit 10 \
|
|
494
|
+
--json number,title,body \
|
|
495
|
+
--jq '.[] | {number, title,
|
|
496
|
+
pattern: (.body | capture("\\*\\*Pattern\\*\\*: *(?<p>[^\\n]+)").p // null),
|
|
497
|
+
prevention: (.body | capture("\\*\\*Prevention\\*\\*: *(?<v>[^\\n]+)").v // null),
|
|
498
|
+
root_cause: (.body | capture("\\*\\*Root cause\\*\\*: *(?<rc>[^\\n]+)").rc // (.body | capture("Root Cause[^\\n]*\\n(?<rc>[^\\n]+)").rc // "see body"))
|
|
499
|
+
}'
|
|
500
|
+
done
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
**C2: Past Bugs in the Same Module**
|
|
504
|
+
```bash
|
|
505
|
+
git log --oneline -30 -- {AFFECTED_FILES} | grep -oP '#\d+' | sort -u | head -8
|
|
506
|
+
# For each issue: fetch title + root cause, keep only bug/fix/review-finding labeled. Max 5.
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
**C3: Related Code Paths** (callers/importers of FUNCTION_NAMES)
|
|
510
|
+
```bash
|
|
511
|
+
for fn in {FUNCTION_NAMES}; do
|
|
512
|
+
grep -r "$fn" {WORKTREE_PATH} --include="*.py" -l | grep -v __pycache__ | head -5
|
|
513
|
+
grep -r "$fn" {WORKTREE_PATH}/web/src --include="*.ts" --include="*.tsx" -l 2>/dev/null | head -5
|
|
514
|
+
done
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
**C4: Successful Similar Implementations**
|
|
518
|
+
```bash
|
|
519
|
+
gh pr list -R {GH_REPO} --state merged --search "{domain_keywords}" --limit 5 \
|
|
520
|
+
--json number,title,files --jq '.[] | {number, title, file_count: (.files | length)}'
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
Post `<!-- FORGE:CONTEXT -->` comment with findings:
|
|
524
|
+
```bash
|
|
525
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:CONTEXT -->
|
|
526
|
+
## Implementation Context for #{NUMBER}
|
|
527
|
+
|
|
528
|
+
### Known Pitfalls for This Area
|
|
529
|
+
{prevention rules from past review-findings}
|
|
530
|
+
|
|
531
|
+
### Historical Findings on These Files
|
|
532
|
+
{past review-finding issues}
|
|
533
|
+
|
|
534
|
+
### Past Bugs in This Module
|
|
535
|
+
{closed bug issues from git log mining}
|
|
536
|
+
|
|
537
|
+
### Related Code Paths (must stay consistent)
|
|
538
|
+
{files that import/call changed functions}
|
|
539
|
+
|
|
540
|
+
### Patterns That Cause Bugs Here
|
|
541
|
+
{recurring bug types synthesized from C1+C2}
|
|
542
|
+
|
|
543
|
+
### Successful Similar Implementations
|
|
544
|
+
{positive patterns from C4}
|
|
545
|
+
|
|
546
|
+
<!-- FORGE:CONTEXT:COMPLETE -->"
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
If total time exceeds 2 minutes, post partial results with `<!-- FORGE:CONTEXT:PARTIAL -->`.
|
|
550
|
+
|
|
551
|
+
### 3C.6: Architecture Plan (MANDATORY)
|
|
552
|
+
|
|
553
|
+
**This phase is NOT optional.** Always run it regardless of issue size or complexity. Even a 1-file fix benefits from cross-path consistency checks. Do NOT invent skip heuristics — the phase handles simple changes gracefully and returns quickly.
|
|
554
|
+
|
|
555
|
+
Trace ALL affected code paths before writing code.
|
|
556
|
+
|
|
557
|
+
**The ONLY acceptable skip conditions** (all must be true): Issue creates ONLY new files with no existing callers AND title starts with "docs:" or "chore:".
|
|
558
|
+
|
|
559
|
+
**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.
|
|
560
|
+
|
|
561
|
+
**A1.5: Route-Tree Classification for Shared Components** *(conditional — skip if no files under `components/` are affected, except `components/ui/primitives/`)* — When a change adds a new hook call or context dependency to a shared component, classify ALL call sites found in A1 by route context:
|
|
562
|
+
1. **Authenticated routes**: Callers under `app/dashboard/`, `app/(authenticated)/`, or any layout that wraps children with an auth provider (e.g., `UserProvider`, `SessionProvider`).
|
|
563
|
+
2. **Public routes**: Callers under `app/(public)/`, `app/playground/`, `app/(marketing)/`, or any layout without the relevant provider.
|
|
564
|
+
3. **If both categories have callers AND the new hook throws when its provider is absent**: add an explicit implementation step to either (a) guard the hook call with a null-context check, (b) make the hook return a safe default when called outside its provider, or (c) remove the hook from the shared component and move it to the authenticated-route-only caller. Document the split in the FORGE:ARCHITECT affected paths table. Do NOT leave a shared component that crashes public routes to be discovered by the FE review agent. <!-- Added: forge#381 -->
|
|
565
|
+
|
|
566
|
+
**A2: Trace Data Flow** — From each entry point: Entry → Transform → Persist/Relay → Exit. Check if the proposed change needs to propagate to each step. **For every field or key read by the changed code, enumerate ALL write paths first** — search across all services for assignments to that field. If multiple code paths write different types to the same field (e.g. dict in the standard path, string in the auth-gated path), the implementation must handle all variants. Do not assume the type you see on the primary code path is the only possible type.
|
|
567
|
+
|
|
568
|
+
**A2.1: Runtime UID × Volume Ownership Check** *(conditional — skip if no Dockerfile or entrypoint is affected)* — When the PR changes the container's runtime user (Dockerfile `USER` directive, `su-exec`, `gosu`, `setuid`), the architect MUST trace the full write-path chain before writing the implementation plan:
|
|
569
|
+
1. **Enumerate volume mounts**: Read all `docker-compose*.yml` files for the affected service. List every named volume and its container mount point (e.g. `storage_shared:/app/storage`). Docker named volumes are created as root-owned by default — any UID change without a corresponding ownership fix will silently break writes.
|
|
570
|
+
2. **Grep for filesystem writes**: Search the affected service's codebase for all filesystem write operations (`mkdir`, `Path.mkdir`, `write_bytes`, `open(`, `os.makedirs`, `shutil.copy`, `shutil.move`). For each write operation, identify the target path.
|
|
571
|
+
3. **Cross-reference**: For each write path that falls under a named volume mount point, add an explicit implementation step to ensure ownership compatibility before the privilege drop — typically `chown -R <user>:<group> <mount_point>` in the entrypoint script before the `exec su-exec` / `exec gosu` call.
|
|
572
|
+
4. **Add to FORGE:ARCHITECT deliverables table**: List the entrypoint or docker-compose change as an explicit deliverable. Do NOT leave volume ownership to be discovered by the builder or reviewer. <!-- Added: forge#323 -->
|
|
573
|
+
|
|
574
|
+
**A2.2: Gate-Condition Caller Sweep** *(conditional — when the fix changes a gate condition that guards a function call or restricts a field)*: Before finalizing the affected-paths table, grep for all callers of the gated function across sibling files in the same service directory. For each caller, verify that the gate condition applied at the call site is semantically correct.
|
|
575
|
+
```bash
|
|
576
|
+
# Identify the gated function from the issue/contract
|
|
577
|
+
GATED_FUNCTION="{function_being_called_inside_the_gate}"
|
|
578
|
+
SERVICE_DIR=$(dirname {PRIMARY_AFFECTED_FILE})
|
|
579
|
+
# Find all call sites
|
|
580
|
+
grep -rn "$GATED_FUNCTION" "$SERVICE_DIR" --include="*.py" | grep -v "#"
|
|
581
|
+
```
|
|
582
|
+
For each call site found: read the surrounding gate condition (±10 lines). If the condition includes fields that do NOT require the gated resource (e.g., `extraction_schema` gated behind an LLM key check when `extraction_schema` is processed without an LLM), add that caller file to the FORGE:ARCHITECT affected-paths table with a note explaining the incorrect gate condition.
|
|
583
|
+
|
|
584
|
+
**Do NOT** omit sibling callers from the affected-paths table simply because they were not listed in the issue spec. The architect's scope is determined by code correctness, not by the issue spec's file list. A gate-condition bug that exists identically in 3 router files must be fixed in all 3 — even if the issue only named 1. <!-- Added: forge#383 -->
|
|
585
|
+
|
|
586
|
+
**A2.5: Pipeline Phase-Dependency Check** *(Forge pipeline changes only — skip if no `commands/*.md` file is affected)* — Identify artefact contracts the changed file produces (comment markers, structured output blocks, Skill() invocation signatures, label transitions). Find downstream pipeline phases that consume those artefacts. Flag any invocation signature change and verify all callers. Emit a checklist of downstream phases that must be updated — appended to the Consistency Checks block in the FORGE:ARCHITECT comment.
|
|
587
|
+
|
|
588
|
+
**A3: Consistency Rules** — Identify invariants all paths must satisfy: null handling, validation, logging, error response shape, auth checks.
|
|
589
|
+
|
|
590
|
+
**A4: Sequence Implementation** — Order: schema/type changes first → core logic → secondary paths → tests → config/env. Files imported by others change before importers. Higher risk first.
|
|
591
|
+
|
|
592
|
+
**A5: Risk Assessment** — Rate each non-obvious interaction: HIGH/MEDIUM/LOW with mitigation.
|
|
593
|
+
|
|
594
|
+
Post `<!-- FORGE:ARCHITECT -->` comment:
|
|
595
|
+
```bash
|
|
596
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:ARCHITECT -->
|
|
597
|
+
## Implementation Plan for #{NUMBER}
|
|
598
|
+
|
|
599
|
+
### Affected Paths (ALL must be updated)
|
|
600
|
+
| # | File | Function/Class | Change Required | Why |
|
|
601
|
+
|---|------|----------------|-----------------|-----|
|
|
602
|
+
{rows}
|
|
603
|
+
|
|
604
|
+
### Implementation Order
|
|
605
|
+
1. {FIRST_CHANGE} — {WHY_FIRST}
|
|
606
|
+
2. {SECOND_CHANGE} — {WHY_SECOND}
|
|
607
|
+
|
|
608
|
+
### Consistency Checks
|
|
609
|
+
- [ ] {INVARIANT_1}
|
|
610
|
+
- [ ] {INVARIANT_2}
|
|
611
|
+
|
|
612
|
+
### Risk Assessment
|
|
613
|
+
| Risk | Severity | Mitigation |
|
|
614
|
+
|------|----------|------------|
|
|
615
|
+
{rows}
|
|
616
|
+
|
|
617
|
+
### Files to Read Before Coding
|
|
618
|
+
- \`{FILE}\` — {WHY_READ_IT}
|
|
619
|
+
|
|
620
|
+
<!-- FORGE:ARCHITECT:COMPLETE -->"
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
If budget exceeded (3 min), use `<!-- FORGE:ARCHITECT:PARTIAL -->`.
|
|
624
|
+
|
|
625
|
+
### 3D: Set building label
|
|
626
|
+
```bash
|
|
627
|
+
gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:building" --remove-label "workflow:ready-to-build,workflow:investigating"
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
### 3E: Create worktree
|
|
631
|
+
|
|
632
|
+
Branch slug from title (lowercase, hyphenated, max 40 chars). Prefix: `fix/` (bugs) or `feat/` (features).
|
|
633
|
+
|
|
634
|
+
**Determine source branch**:
|
|
635
|
+
- Review-finding → parse `**Code branch**: \`{branch}\`` from issue body; branch from `origin/{branch}`
|
|
636
|
+
- **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.
|
|
637
|
+
- Feature lane (has milestone) → branch from `origin/{PR_BASE}`
|
|
638
|
+
- Fast lane (no milestone) → branch from `origin/{STAGING_BRANCH}`
|
|
639
|
+
|
|
640
|
+
```bash
|
|
641
|
+
cd {REPO_PATH}
|
|
642
|
+
git fetch origin
|
|
643
|
+
BRANCH="fix/{slug}-{NUMBER}"
|
|
644
|
+
WORKTREE_PATH="{REPO_PATH}/.claude/worktrees/{BRANCH_SLUG}"
|
|
645
|
+
git worktree add {WORKTREE_PATH} -b {BRANCH} origin/{SOURCE_BRANCH}
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
If worktree already exists: verify correct branch, reuse or remove and recreate.
|
|
649
|
+
|
|
650
|
+
### 3F: Implement
|
|
651
|
+
|
|
652
|
+
**Load context chain** — Read these from GitHub BEFORE writing code:
|
|
653
|
+
1. `FORGE:ARCHITECT` comment (primary implementation guide — if present, follow its ordered list exactly)
|
|
654
|
+
2. `FORGE:INVESTIGATOR` comment (root cause, affected files)
|
|
655
|
+
3. `FORGE:CONTRACT` comment (deliverables, acceptance criteria)
|
|
656
|
+
4. `FORGE:CONTEXT` comment (pitfalls, related paths, past bugs)
|
|
657
|
+
|
|
658
|
+
If `FORGE:ARCHITECT` is absent, fall back to investigation report + contract.
|
|
659
|
+
|
|
660
|
+
**Route by task type**: Bug Fix → implement directly. Feature (backend) → implement directly. Feature (UI/UX) → invoke `frontend-design` skill. Full-Stack → backend first, then frontend-design. Investigation → create issues, skip to Phase 7.
|
|
661
|
+
|
|
662
|
+
**Implementation rules**:
|
|
663
|
+
- Work in `{WORKTREE_PATH}` — all file reads, writes, git ops happen here
|
|
664
|
+
- Read the current file before modifying it — never assume its state
|
|
665
|
+
- Read related files identified in context briefing before touching changed code
|
|
666
|
+
- Follow architect plan's implementation order exactly (when present)
|
|
667
|
+
- For each acceptance criterion: implement it, then verify it's met
|
|
668
|
+
- Do NOT add unrequested scope — contract out-of-scope stays out
|
|
669
|
+
- **Library callback verification**: When writing a lambda/callable passed to a library parameter, MUST verify expected calling convention BEFORE writing it. Check library's default value, documentation, or source code. Wrong arity causes runtime `TypeError` invisible to static analysis. (Ref: PR #14391 — `lambda _: ""` passed where SQLAlchemy expects 0 args)
|
|
670
|
+
- **Cross-lane import guard**: Before adding any `import` or `from X import Y` statement for a service-internal module (`app.*`), verify the module exists on the PR's base branch — NOT just on your local disk or a milestone branch. Run `git show origin/{base_branch}:{module_path}.py` (replacing dots with slashes) to confirm. If the module only exists on a milestone branch, do NOT import it — find an alternative implementation or make the import conditional with a `try/except ImportError` fallback. A milestone-only import on a fast-lane PR will crash production on every request with `ModuleNotFoundError`. (Ref: forge#277 — builder imported `app.billing.subscriptions` from `milestone/subscription-model`, which doesn't exist on `staging`/`main`, causing P1 production crash for paying customer)
|
|
671
|
+
- **Deliverable-type consistency check**: Before committing, compare the actual output against the CONTRACT's deliverable list. If the CONTRACT explicitly states "no code changes required", "docs only", or "configuration update only" AND the diff introduces new executable files (`.py`, `.js`, `.ts`, `.sh` — not test, config, or documentation files), STOP. Do NOT commit. Re-read the CONTRACT, the investigation recommendation, and the ARCHITECT plan. If all three agree that code is needed, update the CONTRACT comment to reflect the new deliverable type before proceeding. If only the builder decided to add code without contract support, discard the code change and implement the contracted deliverable instead. <!-- Added: forge#279 -->
|
|
672
|
+
- **Pipeline check documentation — generalization rule**: When writing or updating pipeline check documentation (in `commands/*.md`), describe the **bug class**, not a specific incident. Do NOT embed: PR numbers, issue numbers, run IDs, timestamps, function names, dollar amounts, or multi-sentence incident timelines in check prose or `**Evidence**:` blocks. One brief HTML comment `<!-- Added: forge#NNN -->` is acceptable per check for traceability. `**Evidence**:` blocks must describe the vulnerability pattern (what the class of bug looks like, why it's dangerous) — not narrate a single historical occurrence. CHANGELOG entries may reference originating issues; command prompt text must not.
|
|
673
|
+
- **Endpoint response contract consumer tracing**: When changing an endpoint's response body shape or status field values (e.g., changing `"status": "healthy"` to `"status": "ok"`, renaming response keys, removing fields), grep the full repo for ALL consumers of that response body before committing. Consumers are not limited to the service being changed — they include deploy scripts, CI health checks, monitoring configs, docker-compose healthcheck definitions, Traefik probes, and any script that parses or pattern-matches on the response body. Run: `grep -rn "{old_value}\|{endpoint_path}" scripts/ infra/ .github/ docker-compose*.yml traefik/ 2>/dev/null`. All consumers whose behavior depends on the old response format MUST be updated in the same PR — a response contract change that updates only one consumer while leaving others on the old format is a deploy-time breakage. <!-- Added: forge#321 -->
|
|
674
|
+
- If contract is wrong (file doesn't exist, function has different signature): STOP, post comment, add `needs-human`, EXIT
|
|
675
|
+
|
|
676
|
+
### 3F.5: Env/Config Completeness Check
|
|
677
|
+
|
|
678
|
+
Run BEFORE committing. Read-only scan of working changes.
|
|
679
|
+
|
|
680
|
+
**Trigger**: Run whenever diff introduces env vars, touches infra/deploy configs, or adds literal IPs.
|
|
681
|
+
|
|
682
|
+
**Check 1 — New env var sync**: Scan for `os.getenv`/`process.env.` references. For each, verify present in `.env.example`, `ENV_VARS.md`, `env_validation.py`. Add if missing.
|
|
683
|
+
|
|
684
|
+
**Check 2 — Deploy/infra restart risk**: If docker-compose/deploy/infra files changed, scan for restart-inducing changes. Annotate commit with `[restart: <service>]`.
|
|
685
|
+
|
|
686
|
+
**Check 3 — Hardcoded IPs and credentials**: Scan for bare IPv4 literals and credential-like assignments. HARD BLOCKER — replace with env vars before staging.
|
|
687
|
+
|
|
688
|
+
**Check 4 — SDK/API Literal sync advisory** (trigger: diff contains `Literal[` in a schema file):
|
|
689
|
+
```bash
|
|
690
|
+
# Detect Literal type changes in API schema files
|
|
691
|
+
cd {WORKTREE_PATH}
|
|
692
|
+
LITERAL_CHANGES=$(git diff HEAD -- | grep -E '^\+.*Literal\[' | grep -v '^\+\+\+')
|
|
693
|
+
if [ -n "$LITERAL_CHANGES" ]; then
|
|
694
|
+
echo "SDK SYNC ADVISORY: Literal type changed in schema."
|
|
695
|
+
echo "Changed Literal lines:"
|
|
696
|
+
echo "$LITERAL_CHANGES"
|
|
697
|
+
echo ""
|
|
698
|
+
echo "ACTION REQUIRED — verify SDK method/type lists match new API schema:"
|
|
699
|
+
echo " - sdk/python/*/client.py: check _valid_methods or equivalent list"
|
|
700
|
+
echo " - sdk/node/src/index.ts: check JSDoc @param Literal type annotation"
|
|
701
|
+
echo " - web/public/openapi*.json: check enum arrays for affected field"
|
|
702
|
+
echo " - web/public/openapi-versions/*.json: check all versioned specs"
|
|
703
|
+
echo ""
|
|
704
|
+
echo "Inconsistency example: API schema narrows Literal['GET','POST','PUT','PATCH','DELETE']"
|
|
705
|
+
echo "to Literal['GET','POST'] but SDK JSDoc still lists all 5 methods — API returns 422"
|
|
706
|
+
echo "for callers following SDK docs. This produces silent user-facing failures."
|
|
707
|
+
fi
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
This advisory is informational — it does NOT block the commit. But the implementer MUST check each listed file and add to the implementation scope if any SDK/spec file still documents the removed/changed Literal values. If SDK files need changes, add them to the current PR rather than leaving the inconsistency for review to catch.
|
|
711
|
+
|
|
712
|
+
### 3G: Quality Gate
|
|
713
|
+
|
|
714
|
+
Skip for 1-file config/docs edits.
|
|
715
|
+
|
|
716
|
+
```
|
|
717
|
+
iteration = 0
|
|
718
|
+
max_iterations = 3
|
|
719
|
+
|
|
720
|
+
while iteration < max_iterations:
|
|
721
|
+
iteration += 1
|
|
722
|
+
Skill("quality-gate", args="{CHANGED_FILES} --worktree {WORKTREE_PATH}")
|
|
723
|
+
if result == "QUALITY GATE: PASS":
|
|
724
|
+
GATE_PASSED = true
|
|
725
|
+
break
|
|
726
|
+
else:
|
|
727
|
+
Fix each HIGH and MEDIUM finding in {WORKTREE_PATH}
|
|
728
|
+
Re-stage fixes
|
|
729
|
+
|
|
730
|
+
if iteration == max_iterations AND not PASS:
|
|
731
|
+
Post "Quality Gate Failed After 3 Iterations" comment
|
|
732
|
+
Add needs-human label → STOP
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
<!-- FORGE:PHASE_COMPLETE — Quality gate done. See Universal Phase Dispatcher: next sub-phase is 3H. Not terminal — continue immediately. -->
|
|
736
|
+
|
|
737
|
+
**After quality gate completes (PASS or fixes applied): proceed to the next sub-phase per the Universal Phase Dispatcher. Quality gate is an intermediate check — "PASS" means the code is clean, NOT that the build is done.**
|
|
738
|
+
|
|
739
|
+
### 3H: Format and verify
|
|
740
|
+
|
|
741
|
+
**Python**:
|
|
742
|
+
```bash
|
|
743
|
+
cd {WORKTREE_PATH}
|
|
744
|
+
black {PYTHON_FILES} && isort {PYTHON_FILES} && python -m py_compile {PYTHON_FILES}
|
|
745
|
+
```
|
|
746
|
+
`py_compile` failures are BLOCKING.
|
|
747
|
+
|
|
748
|
+
**TypeScript (AlterLab)**:
|
|
749
|
+
```bash
|
|
750
|
+
cd {WORKTREE_PATH}
|
|
751
|
+
prettier --write {TS_FILES} && tsc --noEmit
|
|
752
|
+
```
|
|
753
|
+
`tsc --noEmit` failures are BLOCKING.
|
|
754
|
+
|
|
755
|
+
**TypeScript (satellite repos)**:
|
|
756
|
+
```bash
|
|
757
|
+
cd {WORKTREE_PATH}
|
|
758
|
+
npm run build && prettier --write {TS_FILES}
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
### 3I: Frontend proxy wiring check (MANDATORY)
|
|
762
|
+
|
|
763
|
+
Skip if no TS/TSX files changed.
|
|
764
|
+
|
|
765
|
+
All client-side `fetch`/`useSWR`/`apiFetch`/`axios` MUST use `/api/...` proxy routes, NEVER `/api/v1/...` or hardcoded host:port. Scan and fix violations.
|
|
766
|
+
|
|
767
|
+
### 3I.5: Database Configuration Change Advisory
|
|
768
|
+
|
|
769
|
+
Skip if no changed Python files contain DB engine/session/pool patterns.
|
|
770
|
+
|
|
771
|
+
```bash
|
|
772
|
+
cd {WORKTREE_PATH}
|
|
773
|
+
for f in $(echo {CHANGED_FILES} | tr ' ' '\n' | grep -E '\.py$'); do
|
|
774
|
+
grep -qE "create_async_engine|AsyncSession|connect_args|pool_size|prepared_statement|engine_from_config|sessionmaker" "$f" 2>/dev/null && \
|
|
775
|
+
echo "DB CONFIG CHANGE DETECTED in: $f"
|
|
776
|
+
done
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
Advisory only — does not block build. Check for lambda/callable in connect_args (the exact bug class from PR #14391).
|
|
780
|
+
|
|
781
|
+
### 3J: Deployment completeness check (MANDATORY)
|
|
782
|
+
|
|
783
|
+
Skip if no new env vars introduced.
|
|
784
|
+
|
|
785
|
+
For each new env var, verify present in ALL required locations:
|
|
786
|
+
|
|
787
|
+
| Location | Required for |
|
|
788
|
+
|----------|-------------|
|
|
789
|
+
| `.env.example` | All new vars |
|
|
790
|
+
| `infra/secrets/prod.enc.yaml` (SOPS) | Production secrets |
|
|
791
|
+
| `infra/decrypt-secrets.sh` ENV_MAPPING | All vars in deploy chain |
|
|
792
|
+
| `app/env_validation.py` | API service vars |
|
|
793
|
+
| `docker-compose.prod.yml` | Vars needing explicit injection |
|
|
794
|
+
|
|
795
|
+
Deploy chain: SOPS → `decrypt-secrets.sh` (ENV_MAPPING) → `.env.secrets` → `merge-env-secrets.sh` → `.env.production` → docker-compose `env_file`.
|
|
796
|
+
|
|
797
|
+
**Operator-set var classification** *(trigger: new env var is NOT present in `infra/secrets/prod.enc.yaml`)*: <!-- Added: forge#380 -->
|
|
798
|
+
|
|
799
|
+
Some env vars are operator-set (non-secret, not sourced from SOPS) — they must be manually added to `.env.production` on the production server. The SOPS deploy chain does not carry them. When a new env var has no entry in `prod.enc.yaml`, classify it as operator-set and add a **HARD BLOCKER** item to the Testing Checklist:
|
|
800
|
+
|
|
801
|
+
```
|
|
802
|
+
- [ ] HARD BLOCKER: Add {VAR_NAME} to .env.production on the production server.
|
|
803
|
+
This var is operator-set (not in SOPS) — it does NOT flow through the automated
|
|
804
|
+
deploy chain. It must be added manually before or after deploy.
|
|
805
|
+
Verify with: docker exec {service}-blue env | grep {VAR_NAME}
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
**`env_file` re-read warning** *(trigger: any new env var added to `.env.production` path)*:
|
|
809
|
+
|
|
810
|
+
> **Docker `env_file` re-read behavior**: New entries in `.env.production` are only read when a container is **recreated** (e.g., `docker compose up --force-recreate`). A plain `docker restart` restarts the existing container with its frozen env — new `env_file` entries are silently absent. The standard deploy workflow uses `--force-recreate` and handles this correctly. If any out-of-band restart is used, new env vars will not take effect.
|
|
811
|
+
|
|
812
|
+
Add this warning to the Testing Checklist whenever a new env var is introduced (whether secret or operator-set).
|
|
813
|
+
|
|
814
|
+
**Post-deploy in-container verification** *(trigger: any new env var)*:
|
|
815
|
+
|
|
816
|
+
Add the following to the Testing Checklist so the deployer can confirm delivery after deploy:
|
|
817
|
+
|
|
818
|
+
```bash
|
|
819
|
+
# Verify env var reached the running container (run post-deploy)
|
|
820
|
+
docker exec {service}-blue env | grep {VAR_NAME}
|
|
821
|
+
# Expected: {VAR_NAME}={value}
|
|
822
|
+
# If blank: container was not recreated — run: docker compose up --no-deps --force-recreate {service}
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
### 3K: Commit
|
|
826
|
+
|
|
827
|
+
Stage all changes and commit:
|
|
828
|
+
|
|
829
|
+
```bash
|
|
830
|
+
cd {WORKTREE_PATH}
|
|
831
|
+
git add -u
|
|
832
|
+
git commit -m "fix({SCOPE}): {description} (#{NUMBER})"
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
Conventional prefix: `fix`/`feat`/`refactor`/`docs`. Reference `#{NUMBER}` in message.
|
|
836
|
+
|
|
837
|
+
**Post-commit ancestry audit (MANDATORY)**:
|
|
838
|
+
```bash
|
|
839
|
+
cd {WORKTREE_PATH}
|
|
840
|
+
if git ls-remote --exit-code origin {PR_BASE} >/dev/null 2>&1; then
|
|
841
|
+
MERGE_COMMITS=$(git log --merges HEAD ^origin/{PR_BASE} 2>/dev/null)
|
|
842
|
+
if [ -n "$MERGE_COMMITS" ]; then
|
|
843
|
+
# Post ancestry audit failure comment, add needs-human → STOP
|
|
844
|
+
fi
|
|
845
|
+
fi
|
|
846
|
+
```
|
|
847
|
+
|
|
848
|
+
### 3L: Update issue body (MANDATORY)
|
|
849
|
+
Check off completed items, mark phases complete, add PR references.
|
|
850
|
+
|
|
851
|
+
### 3M: Post implementation comment
|
|
852
|
+
```bash
|
|
853
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:BUILDER -->
|
|
854
|
+
## Implementation Complete
|
|
855
|
+
|
|
856
|
+
**Branch**: \`{BRANCH}\`
|
|
857
|
+
**Commits**: {COMMIT_SHA(S)}
|
|
858
|
+
**Files changed**: {COUNT}
|
|
859
|
+
|
|
860
|
+
### Approach
|
|
861
|
+
{what was built, key decisions}
|
|
862
|
+
|
|
863
|
+
### Changes
|
|
864
|
+
{bulleted list of file changes}
|
|
865
|
+
|
|
866
|
+
### Acceptance Criteria Status
|
|
867
|
+
{checklist from contract, marked pass/fail}
|
|
868
|
+
|
|
869
|
+
### Testing Checklist
|
|
870
|
+
- [ ] {scenario 1}
|
|
871
|
+
- [ ] {scenario 2}
|
|
872
|
+
|
|
873
|
+
<!-- FORGE:BUILDER:COMPLETE -->"
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
---
|
|
877
|
+
|
|
878
|
+
## Phase 4: PR Creation
|
|
879
|
+
|
|
880
|
+
### 4A: Pre-push ancestry guard
|
|
881
|
+
|
|
882
|
+
```bash
|
|
883
|
+
cd {WORKTREE_PATH}
|
|
884
|
+
if git ls-remote --exit-code origin {PR_BASE} >/dev/null 2>&1; then
|
|
885
|
+
MERGE_COMMITS=$(git log --merges {BRANCH} ^origin/{PR_BASE} 2>/dev/null)
|
|
886
|
+
if [ -n "$MERGE_COMMITS" ]; then
|
|
887
|
+
# Post ancestry guard failure, add needs-human → STOP
|
|
888
|
+
fi
|
|
889
|
+
fi
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
### 4B: Push branch
|
|
893
|
+
```bash
|
|
894
|
+
cd {WORKTREE_PATH} && git push -u origin {BRANCH}
|
|
895
|
+
```
|
|
896
|
+
If fails: try `--force-with-lease`. If still fails: post comment, add `needs-human`, STOP.
|
|
897
|
+
|
|
898
|
+
### 4C: Determine PR target
|
|
899
|
+
No milestone → `staging`. Has milestone → `milestone/{slug}`. NEVER `main`.
|
|
900
|
+
|
|
901
|
+
### 4D: Create PR
|
|
902
|
+
```bash
|
|
903
|
+
gh pr create {GH_FLAG} --base {PR_BASE} --head {BRANCH} \
|
|
904
|
+
--title "{Fix|Feat|Refactor}: {description}" \
|
|
905
|
+
--body "## Summary
|
|
906
|
+
{BRIEF_DESCRIPTION}
|
|
907
|
+
|
|
908
|
+
## Changes
|
|
909
|
+
{CHANGES_LIST}
|
|
910
|
+
|
|
911
|
+
## Testing
|
|
912
|
+
{TESTING_CHECKLIST}
|
|
913
|
+
|
|
914
|
+
---
|
|
915
|
+
Closes #{NUMBER}
|
|
916
|
+
**Implementation branch**: \`{BRANCH}\`
|
|
917
|
+
**Base**: \`{PR_BASE}\`"
|
|
918
|
+
```
|
|
919
|
+
|
|
920
|
+
`Closes #{NUMBER}` documents intent but does NOT auto-close for non-default-branch PRs.
|
|
921
|
+
|
|
922
|
+
If PR already exists for this branch, use the existing PR number.
|
|
923
|
+
|
|
924
|
+
### 4E: Update labels
|
|
925
|
+
```bash
|
|
926
|
+
gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:in-review" --remove-label "workflow:building"
|
|
927
|
+
```
|
|
928
|
+
|
|
929
|
+
---
|
|
930
|
+
|
|
931
|
+
## Phase 5: Auto-Review
|
|
932
|
+
|
|
933
|
+
### 5A: Re-read state from GitHub (MANDATORY)
|
|
934
|
+
```bash
|
|
935
|
+
gh issue view {NUMBER} {GH_FLAG} --json number,title,body,labels,state
|
|
936
|
+
PR_NUMBER=$(gh pr list {GH_FLAG} --head {BRANCH} --json number --jq '.[0].number')
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
### 5B: Post progress comment
|
|
940
|
+
```bash
|
|
941
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "## Submitting for Review
|
|
942
|
+
|
|
943
|
+
PR #${PR_NUMBER} created targeting \`{PR_BASE}\`. Invoking /review-pr with --auto-merge.
|
|
944
|
+
|
|
945
|
+
<!-- FORGE:REVIEW_STARTED -->"
|
|
946
|
+
```
|
|
947
|
+
|
|
948
|
+
### 5C: Invoke /review-pr with --auto-merge
|
|
949
|
+
```
|
|
950
|
+
Skill(skill="review-pr", args="{PR_NUMBER} --auto-merge --issue {NUMBER} --base {PR_BASE} --gh-flag {GH_FLAG}")
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
Review-pr handles: full domain-agent review → post findings as separate issues → merge PR. It does NOT close the issue or clean up the worktree — those run in Phase 6.
|
|
954
|
+
|
|
955
|
+
### 5D: Verify merge and close (recovery)
|
|
956
|
+
|
|
957
|
+
```bash
|
|
958
|
+
gh pr view {PR_NUMBER} {GH_FLAG} --json state,mergedAt --jq '{state: .state, mergedAt: .mergedAt}'
|
|
959
|
+
gh issue view {NUMBER} {GH_FLAG} --json state --jq '.state'
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
- PR MERGED + issue CLOSED → proceed to Phase 6
|
|
963
|
+
- PR MERGED + issue OPEN → close issue manually
|
|
964
|
+
- PR NOT MERGED → `gh pr merge {PR_NUMBER} {GH_FLAG} --merge --auto`. If fails → post comment, add `needs-human`, STOP.
|
|
965
|
+
|
|
966
|
+
<!-- FORGE:PHASE_COMPLETE — Review done, PR merged. See Universal Phase Dispatcher: next phase is Phase 6 (Close & Cleanup). Not terminal — continue immediately. -->
|
|
967
|
+
|
|
968
|
+
---
|
|
969
|
+
|
|
970
|
+
## Phase 6: Close & Cleanup
|
|
971
|
+
|
|
972
|
+
### 6A: Final issue body update
|
|
973
|
+
|
|
974
|
+
**Multi-phase guard**: Detect whether the issue has multiple phases. Only check off items belonging to the current completed phase.
|
|
975
|
+
|
|
976
|
+
```bash
|
|
977
|
+
BODY=$(gh issue view {NUMBER} {GH_FLAG} --json body --jq '.body')
|
|
978
|
+
REMAINING_BEFORE=$(echo "$BODY" | grep -c '^- \[ \]' || true)
|
|
979
|
+
HAS_PHASE_HEADINGS=$(echo "$BODY" | grep -cP '^#{2,3} ' || true)
|
|
980
|
+
```
|
|
981
|
+
|
|
982
|
+
If multi-phase (`HAS_PHASE_HEADINGS > 0` AND `REMAINING_BEFORE > 0`): do NOT check off future phase items. Add PR reference only.
|
|
983
|
+
|
|
984
|
+
If single-phase or final phase: check off all `[ ]` items, add PR reference.
|
|
985
|
+
|
|
986
|
+
### 6B: Project board update (Status=Done, Workflow=Merged)
|
|
987
|
+
```bash
|
|
988
|
+
ISSUE_URL="https://github.com/{GH_REPO}/issues/{NUMBER}"
|
|
989
|
+
ITEM_ID=$(gh project item-list 1 --owner RapierCraft --format json --limit 200 \
|
|
990
|
+
--jq ".items[] | select(.content.url == \"$ISSUE_URL\") | .id" 2>/dev/null | head -1)
|
|
991
|
+
```
|
|
992
|
+
|
|
993
|
+
If found: set Status=Done, Workflow=Merged using project field IDs.
|
|
994
|
+
|
|
995
|
+
### 6C: Ensure issue is closed
|
|
996
|
+
|
|
997
|
+
**Multi-phase guard**: If `REMAINING_AFTER > 0`, uncompleted phases remain. Post phase-complete comment, leave issue open, EXIT — router picks up next phase on next iteration.
|
|
998
|
+
|
|
999
|
+
If all phases complete:
|
|
1000
|
+
```bash
|
|
1001
|
+
gh issue close {NUMBER} {GH_FLAG} \
|
|
1002
|
+
--comment "Closed: PR #{PR_NUMBER} merged to \`{PR_BASE}\`. Closes #{NUMBER}."
|
|
1003
|
+
gh issue edit {NUMBER} {GH_FLAG} \
|
|
1004
|
+
--add-label "workflow:merged" \
|
|
1005
|
+
--remove-label "workflow:in-review,workflow:building,workflow:investigating" 2>/dev/null || true
|
|
1006
|
+
```
|
|
1007
|
+
|
|
1008
|
+
### 6D: Parent tracker update (sub-issues only)
|
|
1009
|
+
|
|
1010
|
+
**Skip if**: Not a sub-issue (no parent reference in body).
|
|
1011
|
+
|
|
1012
|
+
```bash
|
|
1013
|
+
PARENT_REF=$(gh issue view {NUMBER} {GH_FLAG} --json body --jq '.body' \
|
|
1014
|
+
| grep -oP '(?i)(part of|spawned from|sub-issue of|parent issue[:]?|parent[:])\s*#\K\d+' | head -1)
|
|
1015
|
+
```
|
|
1016
|
+
|
|
1017
|
+
If parent found: check off this sub-issue in parent body. If ALL sub-issues checked off → close parent with `workflow:merged`.
|
|
1018
|
+
|
|
1019
|
+
### 6E: Worktree & branch cleanup
|
|
1020
|
+
|
|
1021
|
+
```bash
|
|
1022
|
+
if [ -n "{WORKTREE_PATH}" ] && [ -d "{WORKTREE_PATH}" ]; then
|
|
1023
|
+
GIT_COMMON=$(git -C {WORKTREE_PATH} rev-parse --git-common-dir 2>/dev/null)
|
|
1024
|
+
REPO_ROOT=$(dirname "$(realpath "$GIT_COMMON" 2>/dev/null || echo "$GIT_COMMON")")
|
|
1025
|
+
git -C "$REPO_ROOT" worktree remove {WORKTREE_PATH} --force 2>/dev/null || true
|
|
1026
|
+
if [ -n "{BRANCH}" ]; then
|
|
1027
|
+
git -C "$REPO_ROOT" branch -D {BRANCH} 2>/dev/null || true
|
|
1028
|
+
fi
|
|
1029
|
+
fi
|
|
1030
|
+
```
|
|
1031
|
+
|
|
1032
|
+
---
|
|
1033
|
+
|
|
1034
|
+
## Phase 7: Summary & Trajectory
|
|
1035
|
+
|
|
1036
|
+
### 7A: Report
|
|
1037
|
+
```
|
|
1038
|
+
## Done: #{NUMBER} — {TITLE}
|
|
1039
|
+
- Investigation: {VERDICT} ({CONFIDENCE})
|
|
1040
|
+
- Lane: {FAST/FEATURE}
|
|
1041
|
+
- Fix: {BRANCH} → PR #{PR_NUMBER} → merged to `{PR_BASE}`
|
|
1042
|
+
- Files changed: {COUNT}
|
|
1043
|
+
```
|
|
1044
|
+
|
|
1045
|
+
### 7B: Trajectory Log (MANDATORY)
|
|
1046
|
+
|
|
1047
|
+
**Review-presence check** (run before filling in Phase 4-5 row): <!-- Added: forge#381 -->
|
|
1048
|
+
```bash
|
|
1049
|
+
# Check whether /review-pr was actually invoked — look for review agent comments on the PR
|
|
1050
|
+
REVIEW_PRESENT=$(gh pr view {PR_NUMBER} {GH_FLAG} --json reviews,comments \
|
|
1051
|
+
--jq '([.reviews[].body // ""] + [.comments[].body // ""]) |
|
|
1052
|
+
map(select(test("APPROVED:|CHANGES REQUESTED:|FORGE:REVIEWER|review-pr";"i"))) |
|
|
1053
|
+
length > 0')
|
|
1054
|
+
# Set Phase 4-5 row: ✅ Merged if review present, ⚠ Skipped (no review) if not
|
|
1055
|
+
REVIEW_ROW=$([ "$REVIEW_PRESENT" = "true" ] && echo "✅ Merged" || echo "⚠ Skipped (no review)")
|
|
1056
|
+
```
|
|
1057
|
+
|
|
1058
|
+
This check is **audit-only** — it annotates the trajectory for visibility. It cannot retroactively block a merged PR. If `⚠ Skipped (no review)` is emitted, log it in the Anomalies field so the skip is surfaced during pipeline health review.
|
|
1059
|
+
|
|
1060
|
+
Post `<!-- FORGE:TRAJECTORY -->` comment with phase-by-phase results table:
|
|
1061
|
+
|
|
1062
|
+
```bash
|
|
1063
|
+
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:TRAJECTORY -->
|
|
1064
|
+
## Pipeline Trajectory — #{NUMBER}
|
|
1065
|
+
|
|
1066
|
+
| Phase | Result | Notes |
|
|
1067
|
+
|-------|--------|-------|
|
|
1068
|
+
| Phase 0: Context Load | ✅ Complete | {lane} → \`{PR_BASE}\` |
|
|
1069
|
+
| Phase 1: Investigation | ✅ {VERDICT} ({CONFIDENCE}) | Task type: {TASK_TYPE} |
|
|
1070
|
+
| Phase 2: Decomposition | ⏭ Skipped | {reason} |
|
|
1071
|
+
| Phase 3: Build | ✅ Complete | Branch: \`{BRANCH}\` |
|
|
1072
|
+
| Phase 3G: Quality Gate | ✅ Gate passed | {iterations} iterations |
|
|
1073
|
+
| Phase 4–5: Review + PR | {REVIEW_ROW} | PR #{PR_NUMBER} → \`{PR_BASE}\` |
|
|
1074
|
+
| Phase 6: Close | ✅ Complete | Issue closed |
|
|
1075
|
+
|
|
1076
|
+
**Decisions**: {key decisions}
|
|
1077
|
+
**Anomalies**: {anomalies or None}
|
|
1078
|
+
**Pipeline completed**: {TIMESTAMP}"
|
|
1079
|
+
```
|
|
1080
|
+
|
|
1081
|
+
---
|
|
1082
|
+
|
|
1083
|
+
## Error Handling
|
|
1084
|
+
|
|
1085
|
+
- Worktree exists: reuse or clean up
|
|
1086
|
+
- PR creation fails: check if branch pushed, if PR already exists
|
|
1087
|
+
- Merge conflicts: report to user, do NOT auto-resolve
|
|
1088
|
+
- gh CLI fails: check `gh auth status`
|
|
1089
|
+
- Label missing: `gh label create "{name}" --color {hex} -R {GH_REPO}`
|