forgedock 1.0.21 → 1.1.1
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 +227 -174
- package/bin/cinema.mjs +320 -0
- package/bin/demo.mjs +386 -0
- package/bin/engine/phases.mjs +148 -0
- package/bin/engine/projector.mjs +32 -0
- package/bin/engine/reconcile.mjs +11 -0
- package/bin/engine/runlog.mjs +79 -0
- package/bin/engine/state.mjs +27 -0
- package/bin/engine-cli.mjs +50 -0
- package/bin/engine.mjs +138 -0
- package/bin/forgedock.mjs +693 -1393
- package/bin/init-detect.mjs +95 -0
- package/bin/journey.mjs +783 -0
- package/bin/runner.mjs +847 -0
- package/bin/settings-hook.mjs +112 -0
- package/bin/tui.mjs +6 -3
- package/commands/autopilot.md +4 -4
- package/commands/cleanup.md +85 -39
- package/commands/deploy-info.md +11 -5
- package/commands/design-bench.md +1 -0
- package/commands/design-render-critique-loop.md +1 -0
- package/commands/design.md +1 -0
- package/commands/forge-stats.md +2 -1
- package/commands/issue.md +1 -1
- package/commands/optimize.md +1 -1
- package/commands/orchestrate.md +346 -34
- package/commands/quality-gate.md +168 -80
- package/commands/review-pr-agents.md +69 -45
- package/commands/review-pr.md +54 -6
- package/commands/scope.md +11 -3
- package/commands/security-audit.md +24 -16
- package/commands/sync-ecosystem.md +2 -2
- package/commands/work-on/build/architect.md +47 -3
- package/commands/work-on/build/context.md +36 -5
- package/commands/work-on/build/implement.md +16 -4
- package/commands/work-on/build/validate.md +54 -8
- package/commands/work-on/build.md +27 -9
- package/commands/work-on/close.md +131 -3
- package/commands/work-on/investigate.md +24 -1
- package/commands/work-on/review.md +24 -0
- package/commands/work-on-monolithic.md +1 -0
- package/commands/work-on.md +271 -11
- package/docs/BRAND.md +6 -2
- package/docs/CONFIG.md +39 -34
- package/docs/site/.vitepress/config.mjs +85 -0
- package/docs/site/annotations-explained.md +55 -0
- package/docs/site/command-learning-path.md +104 -0
- package/docs/site/command-reference.md +3 -0
- package/docs/site/getting-started.md +42 -13
- package/docs/site/index.md +1 -1
- package/docs/site/troubleshooting.md +208 -0
- package/docs/spec/forge-protocol-v1.md +768 -0
- package/docs/superpowers/plans/2026-07-03-cinematic-onboarding-tui.md +2339 -0
- package/docs/superpowers/plans/2026-07-03-durable-execution-engine.md +1223 -0
- package/docs/superpowers/specs/2026-07-03-cinematic-onboarding-tui-design.md +157 -0
- package/docs/superpowers/specs/2026-07-03-durable-execution-engine-design.md +194 -0
- package/examples/forgedock-demo/README.md +113 -0
- package/examples/forgedock-demo/bootstrap.sh +125 -0
- package/examples/forgedock-demo/issues/01-bug-delete-missing-auth.md +37 -0
- package/examples/forgedock-demo/issues/02-feature-filter-notes.md +37 -0
- package/examples/forgedock-demo/issues/03-refactor-router-table.md +33 -0
- package/examples/forgedock-demo/issues/04-perf-index-lookup.md +35 -0
- package/examples/forgedock-demo/issues/05-docs-api-reference.md +39 -0
- package/examples/forgedock-demo/labels.json +21 -0
- package/examples/forgedock-demo/package.json +16 -0
- package/examples/forgedock-demo/scripts/smoke.js +61 -0
- package/examples/forgedock-demo/src/auth.js +28 -0
- package/examples/forgedock-demo/src/db.js +66 -0
- package/examples/forgedock-demo/src/routes/notes.js +64 -0
- package/examples/forgedock-demo/src/server.js +79 -0
- package/package.json +13 -3
- package/scripts/verify-env-vars.sh +18 -2
- package/scripts/verify-host-headers.sh +4 -4
- package/scripts/verify-route-registration.sh +2 -2
- package/scripts/worktree-lifecycle.sh +202 -0
- package/templates/forge.yaml.minimal +44 -0
|
@@ -176,6 +176,11 @@ if [ -n "$DEVDOCS_PATH" ] && [ -f "$INDEX_PATH" ]; then
|
|
|
176
176
|
ISSUE_LABELS=$(gh issue view {NUMBER} -R {GH_REPO} --json labels \
|
|
177
177
|
--jq '[.labels[].name] | join(" ")' 2>/dev/null || echo "")
|
|
178
178
|
echo "Issue labels: ${ISSUE_LABELS:-none}"
|
|
179
|
+
# Separate newline-joined list for the loop below — GitHub label names CAN
|
|
180
|
+
# contain spaces (e.g. "good first issue"), so the space-joined $ISSUE_LABELS
|
|
181
|
+
# above is only safe for display, never for iteration.
|
|
182
|
+
ISSUE_LABELS_LIST=$(gh issue view {NUMBER} -R {GH_REPO} --json labels \
|
|
183
|
+
--jq '.labels[].name' 2>/dev/null || echo "")
|
|
179
184
|
|
|
180
185
|
# Read index content
|
|
181
186
|
INDEX_CONTENT=$(cat "$INDEX_PATH" 2>/dev/null || echo "")
|
|
@@ -190,7 +195,12 @@ if [ -n "$DEVDOCS_PATH" ] && [ -f "$INDEX_PATH" ]; then
|
|
|
190
195
|
|
|
191
196
|
# Extract domain blocks matching any issue label keyword
|
|
192
197
|
DOMAIN_PATHS=""
|
|
193
|
-
|
|
198
|
+
# $ISSUE_LABELS_LIST is one label per line — herestring (not a piped
|
|
199
|
+
# `| while read`, which would run the loop body in a subshell and discard
|
|
200
|
+
# DOMAIN_PATHS once the loop exits) preserves newline-safety for labels
|
|
201
|
+
# containing spaces.
|
|
202
|
+
while IFS= read -r label; do
|
|
203
|
+
[ -z "$label" ] && continue
|
|
194
204
|
KEYWORD=$(echo "$label" | sed 's/^workflow://; s/^priority://; s/^review-finding$//')
|
|
195
205
|
[ -z "$KEYWORD" ] && continue
|
|
196
206
|
# Sanitize KEYWORD before AWK injection — strip chars that would break awk regex delimiters
|
|
@@ -207,7 +217,7 @@ if [ -n "$DEVDOCS_PATH" ] && [ -f "$INDEX_PATH" ]; then
|
|
|
207
217
|
echo "Domain '${SAFE_KEYWORD}' matched — adding docs: $(echo "$BLOCK_PATHS" | tr '\n' ' ')"
|
|
208
218
|
DOMAIN_PATHS="${DOMAIN_PATHS}${BLOCK_PATHS}"$'\n'
|
|
209
219
|
fi
|
|
210
|
-
done
|
|
220
|
+
done <<< "$ISSUE_LABELS_LIST"
|
|
211
221
|
|
|
212
222
|
# Combine always_load + domain paths (deduplicate)
|
|
213
223
|
ALL_PATHS=$(printf "%s\n%s" "$ALWAYS_LOAD_PATHS" "$DOMAIN_PATHS" \
|
|
@@ -448,11 +458,45 @@ For each non-obvious interaction or potential failure mode:
|
|
|
448
458
|
- Suggest a mitigation or check
|
|
449
459
|
|
|
450
460
|
Focus on:
|
|
451
|
-
-
|
|
461
|
+
- Churn / hot-spot files (see below) — high historical change frequency correlates with defect density
|
|
452
462
|
- Paths that are called in both sync and async contexts
|
|
453
463
|
- Any place where the change touches a serialization boundary (JSON in/out, DB read/write)
|
|
454
464
|
- Unused or dormant code that the change might activate
|
|
455
465
|
|
|
466
|
+
### Churn (Hot-Spot) Signal
|
|
467
|
+
|
|
468
|
+
Recency alone (did the file appear in the last N commits?) is a weak, binary proxy. Compute a bounded per-file **churn tier** instead — how often each affected file has actually changed over a fixed window — and fold it into the risk table below.
|
|
469
|
+
|
|
470
|
+
For each file in `{AFFECTED_FILES}` (never repo-wide — this must stay O(affected files) to fit the Phase A0–A5 time budget):
|
|
471
|
+
|
|
472
|
+
```bash
|
|
473
|
+
CHURN_WINDOW="90 days ago" # named constant — must match the same window used in review-pr.md Phase 3A
|
|
474
|
+
|
|
475
|
+
# {AFFECTED_FILES} is a space-separated argument (see --files contract at the top
|
|
476
|
+
# of this file), not newline-separated like review-pr.md's $FILES — so a herestring
|
|
477
|
+
# `while read` line-loop would misparse it as a single line. Split explicitly on
|
|
478
|
+
# IFS=' ' into an array instead of relying on a bare `for FILE in {AFFECTED_FILES}`
|
|
479
|
+
# (which word-splits on the shell's default IFS — space, tab, AND newline). This
|
|
480
|
+
# keeps the existing space-separated contract intact while narrowing the splitting
|
|
481
|
+
# surface to spaces only.
|
|
482
|
+
IFS=' ' read -ra AFFECTED_FILES_ARR <<< "{AFFECTED_FILES}"
|
|
483
|
+
for FILE in "${AFFECTED_FILES_ARR[@]}"; do
|
|
484
|
+
COMMITS=$(git log --oneline --since="$CHURN_WINDOW" -- "$FILE" | wc -l)
|
|
485
|
+
if [ "$COMMITS" -ge 15 ]; then TIER="HOT"
|
|
486
|
+
elif [ "$COMMITS" -ge 5 ]; then TIER="MEDIUM"
|
|
487
|
+
else TIER="LOW"
|
|
488
|
+
fi
|
|
489
|
+
echo "$FILE: $TIER ($COMMITS commits in last 90 days)"
|
|
490
|
+
done
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
**Tiers** (fixed thresholds — keep identical to `review-pr.md` Phase 3A so both phases agree on what "hot" means):
|
|
494
|
+
- **HOT**: 15+ commits in the window
|
|
495
|
+
- **MEDIUM**: 5–14 commits in the window
|
|
496
|
+
- **LOW**: 0–4 commits in the window
|
|
497
|
+
|
|
498
|
+
Any file classified **HOT** MUST get an explicit row in the Risk Assessment table below (e.g. "High historical churn — {N} commits in last 90 days — changes here have a higher defect-density prior"), rated at least MEDIUM severity, with a mitigation such as "extra reviewer scrutiny" or "prefer smaller, isolated diffs to this file." MEDIUM-tier files may be noted inline without a dedicated row. LOW-tier files need no mention.
|
|
499
|
+
|
|
456
500
|
---
|
|
457
501
|
|
|
458
502
|
## Output Format
|
|
@@ -125,6 +125,11 @@ if [ -n "$DEVDOCS_PATH" ] && [ -f "$INDEX_PATH" ]; then
|
|
|
125
125
|
ISSUE_LABELS=$(gh issue view {NUMBER} -R {GH_REPO} --json labels \
|
|
126
126
|
--jq '[.labels[].name] | join(" ")' 2>/dev/null || echo "")
|
|
127
127
|
echo "Issue labels: ${ISSUE_LABELS:-none}"
|
|
128
|
+
# Separate newline-joined list for the loop below — GitHub label names CAN
|
|
129
|
+
# contain spaces (e.g. "good first issue"), so the space-joined $ISSUE_LABELS
|
|
130
|
+
# above is only safe for display, never for iteration.
|
|
131
|
+
ISSUE_LABELS_LIST=$(gh issue view {NUMBER} -R {GH_REPO} --json labels \
|
|
132
|
+
--jq '.labels[].name' 2>/dev/null || echo "")
|
|
128
133
|
|
|
129
134
|
# Read index content
|
|
130
135
|
INDEX_CONTENT=$(cat "$INDEX_PATH" 2>/dev/null || echo "")
|
|
@@ -139,7 +144,12 @@ if [ -n "$DEVDOCS_PATH" ] && [ -f "$INDEX_PATH" ]; then
|
|
|
139
144
|
|
|
140
145
|
# Extract domain blocks matching any issue label keyword
|
|
141
146
|
DOMAIN_PATHS=""
|
|
142
|
-
|
|
147
|
+
# $ISSUE_LABELS_LIST is one label per line — herestring (not a piped
|
|
148
|
+
# `| while read`, which would run the loop body in a subshell and discard
|
|
149
|
+
# DOMAIN_PATHS once the loop exits) preserves newline-safety for labels
|
|
150
|
+
# containing spaces.
|
|
151
|
+
while IFS= read -r label; do
|
|
152
|
+
[ -z "$label" ] && continue
|
|
143
153
|
# Strip workflow:, priority:, review-finding prefixes — use bare keyword
|
|
144
154
|
KEYWORD=$(echo "$label" | sed 's/^workflow://; s/^priority://; s/^review-finding$//')
|
|
145
155
|
[ -z "$KEYWORD" ] && continue
|
|
@@ -158,7 +168,7 @@ if [ -n "$DEVDOCS_PATH" ] && [ -f "$INDEX_PATH" ]; then
|
|
|
158
168
|
echo "Domain '${SAFE_KEYWORD}' matched — adding docs: $(echo "$BLOCK_PATHS" | tr '\n' ' ')"
|
|
159
169
|
DOMAIN_PATHS="${DOMAIN_PATHS}${BLOCK_PATHS}"$'\n'
|
|
160
170
|
fi
|
|
161
|
-
done
|
|
171
|
+
done <<< "$ISSUE_LABELS_LIST"
|
|
162
172
|
|
|
163
173
|
# Combine always_load + domain paths (deduplicate)
|
|
164
174
|
ALL_PATHS=$(printf "%s\n%s" "$ALWAYS_LOAD_PATHS" "$DOMAIN_PATHS" \
|
|
@@ -397,7 +407,12 @@ If `GIST_SUMMARIES` is non-empty, it will be included in the `### Prior Investig
|
|
|
397
407
|
Query closed issues with `review-finding` label, searching by filename:
|
|
398
408
|
|
|
399
409
|
```bash
|
|
400
|
-
|
|
410
|
+
# {AFFECTED_FILES} is a space-separated file path list (see contract note above)
|
|
411
|
+
# — split explicitly on IFS=' ' into an array instead of a bare
|
|
412
|
+
# `for file in {AFFECTED_FILES}`, which word-splits on the shell's default IFS
|
|
413
|
+
# (space, tab, AND newline) and would corrupt any path containing a space.
|
|
414
|
+
IFS=' ' read -ra AFFECTED_FILES_ARR <<< "{AFFECTED_FILES}"
|
|
415
|
+
for file in "${AFFECTED_FILES_ARR[@]}"; do
|
|
401
416
|
basename=$(basename "$file" .py)
|
|
402
417
|
gh issue list -R {GH_REPO} \
|
|
403
418
|
--state closed \
|
|
@@ -425,7 +440,7 @@ Keep findings where the filename or function name appears in the title or body.
|
|
|
425
440
|
|
|
426
441
|
## Phase C2: Past Bugs in the Same Module
|
|
427
442
|
|
|
428
|
-
Mine git log for commit messages referencing issues, then fetch those issues
|
|
443
|
+
Mine git log for commit messages referencing issues, then fetch those issues. Also read commit bodies/diffs directly — local git history is near-free relative to `gh api` round-trips, and commit bodies often explain the "why" without needing to fetch the linked issue at all.
|
|
429
444
|
|
|
430
445
|
```bash
|
|
431
446
|
# Step 1: find issue numbers from git history on affected files
|
|
@@ -444,6 +459,20 @@ Filter: keep only `bug`, `fix`, or `review-finding` labeled issues. Skip feature
|
|
|
444
459
|
|
|
445
460
|
**Max results**: 5 issues.
|
|
446
461
|
|
|
462
|
+
**Direct commit-body read (bounded, prefer over `gh api` when it already answers "why")**: For the top 5 commits touching `{AFFECTED_FILES}`, read the commit subject + body directly instead of round-tripping to `gh issue view` when the body already explains the change:
|
|
463
|
+
```bash
|
|
464
|
+
git log -5 --format='%h %ad %s%n%b' --date=short -- {AFFECTED_FILES}
|
|
465
|
+
```
|
|
466
|
+
If a commit body fully explains the prior bug/fix (common for squashed or fix-up commits with no `#NNN` reference), use it directly as a "Past Bug in This Module" entry — do not require a linked GitHub issue to exist.
|
|
467
|
+
|
|
468
|
+
**Pickaxe pass (has this exact area been fixed before?)** — one bounded pass, capped at 5 hits, keyed on the suspected symbol/string from the Builder Contract or investigation report:
|
|
469
|
+
```bash
|
|
470
|
+
git log -S"{suspected_symbol_or_string}" --oneline -- {AFFECTED_FILES} | head -5
|
|
471
|
+
# Use -G instead of -S for regex patterns
|
|
472
|
+
git log -G"{pattern}" --oneline -- {AFFECTED_FILES} | head -5
|
|
473
|
+
```
|
|
474
|
+
Any hit is a candidate prior fix or reintroduced defect for this exact code area — read `git show {hash}` to confirm scope before including it in the output. This catches regressions the issue-number harvest above misses (e.g. a defect fixed via a squashed commit with no `#NNN` reference).
|
|
475
|
+
|
|
447
476
|
---
|
|
448
477
|
|
|
449
478
|
## Phase C3: Related Code Paths
|
|
@@ -526,8 +555,10 @@ gh issue comment {NUMBER} -R {GH_REPO} --body "<!-- FORGE:CONTEXT -->
|
|
|
526
555
|
- #{NUM}: \"{TITLE}\" — root cause: {ROOT_CAUSE}
|
|
527
556
|
|
|
528
557
|
### Past Bugs in This Module
|
|
529
|
-
<!-- List of closed bug issues from git log mining
|
|
558
|
+
<!-- List of closed bug issues from git log mining, PLUS pickaxe-derived findings (commits with no linked issue, or
|
|
559
|
+
commit bodies read directly per the C2 direct-commit-body step). If none: 'No prior bugs found in git history.' -->
|
|
530
560
|
- #{NUM}: \"{TITLE}\" — root cause: {SNIPPET}
|
|
561
|
+
- {COMMIT_HASH} (no linked issue): {COMMIT_SUBJECT} — {WHY_RELEVANT, from commit body or pickaxe hit}
|
|
531
562
|
|
|
532
563
|
### Related Code Paths (must stay consistent)
|
|
533
564
|
<!-- Files that import or call the changed functions. Builder must read and validate these. -->
|
|
@@ -53,7 +53,17 @@ gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
|
53
53
|
--jq '.[] | select(.body | contains("FORGE:CONTEXT")) | .body'
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
**Resume check**:
|
|
56
|
+
**Resume check**:
|
|
57
|
+
- If `<!-- FORGE:BUILDER:COMPLETE -->` is present in a BUILDER comment → implementation is done — EXIT and return `IMPLEMENT_RESULT: status: ALREADY_DONE` to caller.
|
|
58
|
+
- If `<!-- FORGE:BUILDER -->` exists BUT `<!-- FORGE:BUILDER:COMPLETE -->` is ABSENT → implementation was interrupted after the comment was posted but before the commit (validate.md V5). Delete the partial comment and restart from Phase I2:
|
|
59
|
+
```bash
|
|
60
|
+
PARTIAL_ID=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
61
|
+
--jq '[.[] | select(.body | contains("FORGE:BUILDER") and (contains("FORGE:BUILDER:COMPLETE") | not))] | last | .id // ""')
|
|
62
|
+
if [ -n "$PARTIAL_ID" ]; then
|
|
63
|
+
gh api repos/{GH_REPO}/issues/comments/$PARTIAL_ID -X DELETE
|
|
64
|
+
echo "Deleted partial FORGE:BUILDER comment (no FORGE:BUILDER:COMPLETE) — restarting implementation"
|
|
65
|
+
fi
|
|
66
|
+
```
|
|
57
67
|
|
|
58
68
|
**Primary guide**: If `<!-- FORGE:ARCHITECT -->` is present, it is the **primary implementation input**. Follow its ordered implementation list exactly — it defines which files change, in what order, and what consistency checks must pass. The investigation report and contract are secondary context.
|
|
59
69
|
|
|
@@ -225,6 +235,8 @@ gh issue edit {NUMBER} {GH_FLAG} --body "{UPDATED_BODY}"
|
|
|
225
235
|
|
|
226
236
|
## Phase I6: Post FORGE:BUILDER Comment
|
|
227
237
|
|
|
238
|
+
Post the implementation summary comment. **Do NOT include `<!-- FORGE:BUILDER:COMPLETE -->` here** — that marker is posted by `validate.md` Phase V5 after the commit succeeds. A crash between I6 and V5 would otherwise leave a completion-marked comment with no commit on the branch.
|
|
239
|
+
|
|
228
240
|
```bash
|
|
229
241
|
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:BUILDER -->
|
|
230
242
|
## Implementation Complete
|
|
@@ -247,11 +259,11 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:BUILDER -->
|
|
|
247
259
|
- [ ] {Test scenario 2} [type:unit]
|
|
248
260
|
- [ ] {Test scenario 3} [type:e2e]
|
|
249
261
|
|
|
250
|
-
> **Test-type annotation** (optional): Append `[type:api]`, `[type:unit]`, `[type:e2e]`, or `[type:manual]` to each checklist item. The test gate reads this annotation directly and skips regex inference. Omit it to rely on regex classification fallback.
|
|
251
|
-
|
|
252
|
-
<!-- FORGE:BUILDER:COMPLETE -->"
|
|
262
|
+
> **Test-type annotation** (optional): Append `[type:api]`, `[type:unit]`, `[type:e2e]`, or `[type:manual]` to each checklist item. The test gate reads this annotation directly and skips regex inference. Omit it to rely on regex classification fallback."
|
|
253
263
|
```
|
|
254
264
|
|
|
265
|
+
**Note**: `<!-- FORGE:BUILDER:COMPLETE -->` is intentionally absent from this comment. It is appended to this comment by `validate.md` Phase V5 after the git commit completes — see `validate.md § Phase V5`. This ordering ensures the completion marker only exists when a real commit is present on the branch.
|
|
266
|
+
|
|
255
267
|
---
|
|
256
268
|
|
|
257
269
|
## Output
|
|
@@ -90,7 +90,12 @@ Needs human review before proceeding to commit.
|
|
|
90
90
|
|
|
91
91
|
## Phase V2: Format and Verify
|
|
92
92
|
|
|
93
|
-
Run after quality gate passes. All tool commands are read from `forge.yaml → verification.commands`; each step logs `SKIPPED — not configured` when the corresponding key is absent rather than silently passing
|
|
93
|
+
Run after quality gate passes. All tool commands are read from `forge.yaml → verification.commands`; each step logs `SKIPPED — not configured` when the corresponding key is absent rather than silently passing.
|
|
94
|
+
|
|
95
|
+
**Track skipped checks** — initialize before any check runs:
|
|
96
|
+
```bash
|
|
97
|
+
SKIPPED_CHECKS=""
|
|
98
|
+
```
|
|
94
99
|
|
|
95
100
|
**Python**:
|
|
96
101
|
```bash
|
|
@@ -101,6 +106,7 @@ if [ -n "$PYTHON_FORMAT" ]; then
|
|
|
101
106
|
eval "$PYTHON_FORMAT" 2>&1
|
|
102
107
|
else
|
|
103
108
|
echo "SKIPPED — python.format not configured in verification.commands"
|
|
109
|
+
SKIPPED_CHECKS="${SKIPPED_CHECKS:+$SKIPPED_CHECKS, }python.format"
|
|
104
110
|
fi
|
|
105
111
|
|
|
106
112
|
# Compile check always runs for Python files (no config needed — catches syntax errors)
|
|
@@ -120,6 +126,7 @@ if [ -n "$TS_FORMAT" ]; then
|
|
|
120
126
|
eval "$TS_FORMAT" 2>&1
|
|
121
127
|
else
|
|
122
128
|
echo "SKIPPED — typescript.format not configured in verification.commands"
|
|
129
|
+
SKIPPED_CHECKS="${SKIPPED_CHECKS:+$SKIPPED_CHECKS, }typescript.format"
|
|
123
130
|
fi
|
|
124
131
|
|
|
125
132
|
if [ -n "$TS_TYPECHECK" ]; then
|
|
@@ -130,6 +137,7 @@ elif [ -n "$TS_BUILD" ]; then
|
|
|
130
137
|
TS_EXIT=$?
|
|
131
138
|
else
|
|
132
139
|
echo "SKIPPED — typescript.typecheck and typescript.build not configured in verification.commands"
|
|
140
|
+
SKIPPED_CHECKS="${SKIPPED_CHECKS:+$SKIPPED_CHECKS, }typescript.typecheck/build"
|
|
133
141
|
TS_EXIT=0
|
|
134
142
|
fi
|
|
135
143
|
```
|
|
@@ -172,21 +180,30 @@ When changed files touch database engine configuration, flag for manual connecti
|
|
|
172
180
|
```bash
|
|
173
181
|
cd {WORKTREE_PATH}
|
|
174
182
|
DB_CONFIG_FILES=""
|
|
175
|
-
|
|
183
|
+
# Process substitution (< <(...)), NOT a piped `| while read`, so DB_CONFIG_FILES
|
|
184
|
+
# set inside the loop body survives past the loop (a piped while-read would run
|
|
185
|
+
# in a subshell and silently discard the accumulator).
|
|
186
|
+
while IFS= read -r f; do
|
|
187
|
+
[ -z "$f" ] && continue
|
|
176
188
|
grep -qE "create_async_engine|AsyncSession|connect_args|pool_size|prepared_statement|engine_from_config|sessionmaker" "$f" 2>/dev/null && \
|
|
177
|
-
DB_CONFIG_FILES="$DB_CONFIG_FILES
|
|
178
|
-
done
|
|
189
|
+
DB_CONFIG_FILES="${DB_CONFIG_FILES}${f}"$'\n'
|
|
190
|
+
done < <(echo {CHANGED_FILES} | tr ' ' '\n' | grep -E '\.py$')
|
|
179
191
|
|
|
180
192
|
if [ -n "$DB_CONFIG_FILES" ]; then
|
|
181
|
-
echo "DB CONFIG CHANGE DETECTED in
|
|
193
|
+
echo "DB CONFIG CHANGE DETECTED in:"
|
|
194
|
+
echo "$DB_CONFIG_FILES"
|
|
182
195
|
echo "ACTION: Verify database connectivity after deploy — changes to engine config, connect_args, or session factories can cause silent runtime failures."
|
|
183
196
|
echo "RECOMMENDED: Run a minimal connectivity test (e.g., SELECT 1) through the modified session/engine path."
|
|
184
197
|
|
|
185
198
|
# Check for lambda/callable in connect_args — the exact bug class from PR #14391
|
|
186
|
-
|
|
199
|
+
# $DB_CONFIG_FILES is one path per line (built above) — herestring, not a
|
|
200
|
+
# piped `| while read`, so behavior stays consistent with the other fixes
|
|
201
|
+
# in this sweep even though no accumulator is set inside this particular loop.
|
|
202
|
+
while IFS= read -r f; do
|
|
203
|
+
[ -z "$f" ] && continue
|
|
187
204
|
grep -nE "lambda.*:.*['\"]|=lambda" "$f" 2>/dev/null | grep -iE "connect_args|prepared_statement|pool|engine" && \
|
|
188
205
|
echo "WARNING: Lambda/callable in database configuration in $f — verify callback signature matches library's expected calling convention"
|
|
189
|
-
done
|
|
206
|
+
done <<< "$DB_CONFIG_FILES"
|
|
190
207
|
fi
|
|
191
208
|
```
|
|
192
209
|
|
|
@@ -234,7 +251,7 @@ After the gate passes, commit all staged changes in a single commit. This includ
|
|
|
234
251
|
```bash
|
|
235
252
|
cd {WORKTREE_PATH}
|
|
236
253
|
git add -u
|
|
237
|
-
git commit -m "fix({SCOPE}): {description} (#NUMBER)"
|
|
254
|
+
git commit -s -m "fix({SCOPE}): {description} (#NUMBER)"
|
|
238
255
|
```
|
|
239
256
|
|
|
240
257
|
Where `{SCOPE}` is the command or module scope from the contract (e.g. `work-on`, `quality-gate`), and `{description}` summarises the implementation. Use the commit convention from the contract:
|
|
@@ -278,6 +295,32 @@ If `origin/{PR_BASE}` does not exist yet (new branch), skip this check — no co
|
|
|
278
295
|
git ls-remote --exit-code origin {PR_BASE} >/dev/null 2>&1 || echo "PR_BASE not on origin — skipping ancestry audit"
|
|
279
296
|
```
|
|
280
297
|
|
|
298
|
+
### V5 Post-Commit: Mark Build Complete (MANDATORY)
|
|
299
|
+
|
|
300
|
+
After the ancestry audit passes (or is skipped), append `<!-- FORGE:BUILDER:COMPLETE -->` to the existing FORGE:BUILDER comment. This is the **only** place this marker is written — it signals that a real commit exists on the branch and the build is safe to resume-skip. <!-- Added: forge#1305 -->
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
# Find the FORGE:BUILDER comment posted by implement.md Phase I6
|
|
304
|
+
BUILDER_COMMENT_ID=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
305
|
+
--jq '[.[] | select(.body | contains("FORGE:BUILDER") and (contains("FORGE:BUILDER:COMPLETE") | not))] | last | .id // ""')
|
|
306
|
+
|
|
307
|
+
if [ -n "$BUILDER_COMMENT_ID" ]; then
|
|
308
|
+
# Fetch current body and append the completion marker
|
|
309
|
+
CURRENT_BODY=$(gh api repos/{GH_REPO}/issues/comments/$BUILDER_COMMENT_ID --jq '.body')
|
|
310
|
+
UPDATED_BODY="${CURRENT_BODY}
|
|
311
|
+
|
|
312
|
+
<!-- FORGE:BUILDER:COMPLETE -->"
|
|
313
|
+
gh api repos/{GH_REPO}/issues/comments/$BUILDER_COMMENT_ID \
|
|
314
|
+
-X PATCH \
|
|
315
|
+
--field body="$UPDATED_BODY"
|
|
316
|
+
echo "FORGE:BUILDER:COMPLETE appended to comment $BUILDER_COMMENT_ID"
|
|
317
|
+
else
|
|
318
|
+
echo "WARNING: FORGE:BUILDER comment not found or already marked complete — skipping BUILDER:COMPLETE append"
|
|
319
|
+
fi
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**Why here and not in implement.md**: The commit (`git commit`) runs in this phase (V5). Appending `:COMPLETE` after the commit ensures that a session crash between implement.md I6 (comment posted) and this step (commit) leaves a partial BUILDER comment without `:COMPLETE`. The next resume will detect the partial comment, delete it, and restart the build. See `implement.md § Phase I1 resume check`.
|
|
323
|
+
|
|
281
324
|
---
|
|
282
325
|
|
|
283
326
|
## Output
|
|
@@ -293,6 +336,9 @@ VALIDATE_RESULT:
|
|
|
293
336
|
deploy_completeness_fixes: [{VAR_NAME: location_added}, ...]
|
|
294
337
|
commits_added: [{SHA}, ...] # from V5 if any
|
|
295
338
|
blocker: {description if gate_passed=false}
|
|
339
|
+
verification_skipped: [] # empty when all configured checks ran; list of skipped check names otherwise
|
|
340
|
+
# e.g. ["python.format", "typescript.typecheck/build"]
|
|
341
|
+
# populated from SKIPPED_CHECKS in Phase V2
|
|
296
342
|
```
|
|
297
343
|
|
|
298
344
|
---
|
|
@@ -9,8 +9,8 @@ argument-hint: [issue number] [--repo GH_REPO] [--gh-flag GH_FLAG] [--base PR_BA
|
|
|
9
9
|
|
|
10
10
|
**Input**: $ARGUMENTS
|
|
11
11
|
|
|
12
|
-
**Invoked by**: `work-on.md`
|
|
13
|
-
**Output**: Create worktree, post contract, sequence build subcommands, return result to
|
|
12
|
+
**Invoked by**: `work-on.md` Phase 3 — entered when the issue carries label `workflow:ready-to-build` or `workflow:building` (see Universal Phase Dispatcher in work-on.md).
|
|
13
|
+
**Output**: Create worktree, post contract, sequence build subcommands, return result to work-on.md.
|
|
14
14
|
|
|
15
15
|
**Agent model policy**: Default `model: "sonnet"`. If Sonnet is rate-limited, fall back to `model: "opus"`.
|
|
16
16
|
**NEVER use plan mode (EnterPlanMode).**
|
|
@@ -46,7 +46,16 @@ gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
**Resume check**:
|
|
49
|
-
- If `<!-- FORGE:BUILDER -->` comment
|
|
49
|
+
- If `<!-- FORGE:BUILDER:COMPLETE -->` is present in a BUILDER comment → build already complete. Return `BUILD_RESULT: status: ALREADY_DONE` to router.
|
|
50
|
+
- If `<!-- FORGE:BUILDER -->` exists BUT `<!-- FORGE:BUILDER:COMPLETE -->` is ABSENT → build was interrupted after the comment was posted but before the commit (validate.md V5). Delete the partial comment and restart from Phase B2 (contract): <!-- Added: forge#1305 -->
|
|
51
|
+
```bash
|
|
52
|
+
PARTIAL_ID=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
53
|
+
--jq '[.[] | select(.body | contains("FORGE:BUILDER") and (contains("FORGE:BUILDER:COMPLETE") | not))] | last | .id // ""')
|
|
54
|
+
if [ -n "$PARTIAL_ID" ]; then
|
|
55
|
+
gh api repos/{GH_REPO}/issues/comments/$PARTIAL_ID -X DELETE
|
|
56
|
+
echo "Deleted partial FORGE:BUILDER comment (no FORGE:BUILDER:COMPLETE) — restarting build from Phase B2"
|
|
57
|
+
fi
|
|
58
|
+
```
|
|
50
59
|
- If no `<!-- FORGE:INVESTIGATOR -->` comment with `<!-- INVESTIGATION:COMPLETE -->` → EXIT with `BUILD_RESULT: status: BLOCKED`, blocker: "Investigation not complete — run investigate first".
|
|
51
60
|
|
|
52
61
|
Extract from investigation report:
|
|
@@ -82,6 +91,14 @@ Append `-{NUMBER}` to ensure uniqueness: e.g. `fix/work-on-build-landing-file-85
|
|
|
82
91
|
|
|
83
92
|
- Review-finding issue → parse `**Code branch**: \`{branch}\`` from issue body; branch from `origin/{branch}`
|
|
84
93
|
- **Milestone review-finding hybrid lane** (ONLY when Code branch matches `milestone/*`): This is a high-risk lane. The worktree will carry the full milestone history. The PR target is `staging` (or the base specified). **DANGER: Agents MUST NOT use `git merge` to resolve any conflicts in this lane.** Merge-based conflict resolution will pull the entire milestone commit tree onto staging, contaminating it with unapproved code. Use `git rebase` or `git cherry-pick` only. If conflicts cannot be resolved without a merge, post a comment on the issue, add `needs-human`, and STOP.
|
|
94
|
+
- **Missing ref fallback**: After parsing, verify the Code branch still exists on remote. If not, fall back to the lane default (`staging` for fast lane, `milestone/{slug}` for feature lane) and note the fallback:
|
|
95
|
+
```bash
|
|
96
|
+
SOURCE_BRANCH="{CODE_BRANCH_FROM_ISSUE_BODY}"
|
|
97
|
+
if ! git ls-remote --exit-code origin "$SOURCE_BRANCH" >/dev/null 2>&1; then
|
|
98
|
+
echo "WARNING: Code branch '$SOURCE_BRANCH' not found on remote — falling back to lane default '$PR_BASE'"
|
|
99
|
+
SOURCE_BRANCH="$PR_BASE"
|
|
100
|
+
fi
|
|
101
|
+
```
|
|
85
102
|
- Feature lane (has milestone) → branch from `origin/{PR_BASE}`
|
|
86
103
|
- Fast lane (no milestone) → branch from `origin/staging`
|
|
87
104
|
|
|
@@ -275,13 +292,14 @@ BUILD_RESULT:
|
|
|
275
292
|
|
|
276
293
|
## Integration Point in work-on.md
|
|
277
294
|
|
|
278
|
-
This module runs
|
|
295
|
+
This module runs during **Phase 3** of the work-on.md pipeline (label: `workflow:ready-to-build` or `workflow:building`). The full sequence is defined by the Universal Phase Dispatcher in work-on.md:
|
|
279
296
|
|
|
280
297
|
```
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
298
|
+
Phase 3 (Build) → [THIS MODULE] worktree + contract + context + architect + implement + validate
|
|
299
|
+
→ posts FORGE:BUILDER comment, writes FORGE:CHECKPOINT next_phase=REVIEW
|
|
300
|
+
Phase 4 (PR) → work-on:review — push branch, create PR, set workflow:in-review
|
|
301
|
+
Phase 5 (Review) → work-on:review — invoke /review-pr --auto-merge
|
|
302
|
+
Phase 6 (Close) → work-on:close — trajectory + parent tracker + summary + worktree cleanup
|
|
285
303
|
```
|
|
286
304
|
|
|
287
|
-
|
|
305
|
+
After this module posts `FORGE:BUILDER` and returns, work-on.md's Universal continuation rule re-reads the issue labels. Since the label is not yet terminal (`workflow:merged` / `workflow:invalid` / `needs-human`), it proceeds immediately to Phase 4 (PR Creation) and then Phase 5 (Auto-Review).
|
|
@@ -179,7 +179,7 @@ ISSUE_STATE=$(gh issue view {NUMBER} {GH_FLAG} --json state --jq '.state')
|
|
|
179
179
|
|
|
180
180
|
**If `REMAINING_AFTER > 0`** (multi-phase: uncompleted phases remain):
|
|
181
181
|
```bash
|
|
182
|
-
# Post phase-complete marker —
|
|
182
|
+
# Post phase-complete marker — work-on.md's Universal continuation rule will re-read labels and continue to the next phase
|
|
183
183
|
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:PHASE:COMPLETE -->
|
|
184
184
|
Phase complete. PR #{PR_NUMBER} merged to \`{PR_BASE}\`. ${REMAINING_AFTER} phase item(s) remain — leaving issue open for next pipeline iteration."
|
|
185
185
|
|
|
@@ -279,6 +279,125 @@ Output to stdout (returned to calling agent):
|
|
|
279
279
|
|
|
280
280
|
---
|
|
281
281
|
|
|
282
|
+
## Phase C4.5: Pipeline Summary Card (MANDATORY)
|
|
283
|
+
|
|
284
|
+
The shareable artifact. After the close completes, render a box-drawing summary card to
|
|
285
|
+
stdout (terminal screenshot moment) AND compute a machine-readable twin that Phase C5
|
|
286
|
+
embeds in the `FORGE:TRAJECTORY` comment for platform consumption.
|
|
287
|
+
|
|
288
|
+
**All stats are real — pulled from `gh`/`git`. Every lookup degrades gracefully: a missing
|
|
289
|
+
value renders as `—` and NEVER aborts the card. Do NOT fabricate stats.**
|
|
290
|
+
|
|
291
|
+
### C4.5a: Gather real stats
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
# Commit / diff stats from the merged PR (single API call). Fallbacks to "—" if absent.
|
|
295
|
+
PR_STATS=$(gh pr view {PR_NUMBER} {GH_FLAG} --json commits,additions,deletions,baseRefName,isDraft 2>/dev/null)
|
|
296
|
+
COMMITS=$(echo "$PR_STATS" | jq -r '(.commits | length) // empty' 2>/dev/null); COMMITS=${COMMITS:-—}
|
|
297
|
+
ADDITIONS=$(echo "$PR_STATS" | jq -r '.additions // empty' 2>/dev/null); ADDITIONS=${ADDITIONS:-—}
|
|
298
|
+
DELETIONS=$(echo "$PR_STATS" | jq -r '.deletions // empty' 2>/dev/null); DELETIONS=${DELETIONS:-—}
|
|
299
|
+
PR_TARGET=$(echo "$PR_STATS" | jq -r '.baseRefName // empty' 2>/dev/null); PR_TARGET=${PR_TARGET:-{PR_BASE}}
|
|
300
|
+
IS_DRAFT=$(echo "$PR_STATS" | jq -r '.isDraft // false' 2>/dev/null)
|
|
301
|
+
|
|
302
|
+
# Review summary — count domain-agent verdicts posted by /review-pr on the PR.
|
|
303
|
+
REVIEW_BODIES=$(gh pr view {PR_NUMBER} {GH_FLAG} --json reviews,comments \
|
|
304
|
+
--jq '[.reviews[].body // ""] + [.comments[].body // ""] | .[]' 2>/dev/null)
|
|
305
|
+
# NOTE: `grep -c` already prints `0` on no match (and exits non-zero) — do NOT add
|
|
306
|
+
# `|| echo 0`, which would append a second line ("0\n0") and break the arithmetic
|
|
307
|
+
# and `--argjson` below. Swallow the non-zero exit with `|| true`, then default.
|
|
308
|
+
APPROVED=$(echo "$REVIEW_BODIES" | grep -cE 'APPROVED:' 2>/dev/null || true); APPROVED=${APPROVED:-0}
|
|
309
|
+
CHANGES=$(echo "$REVIEW_BODIES" | grep -cE 'CHANGES REQUESTED:' 2>/dev/null || true); CHANGES=${CHANGES:-0}
|
|
310
|
+
TOTAL_AGENTS=$((APPROVED + CHANGES))
|
|
311
|
+
# Blockers = review-finding issues created by this PR that are still open (best-effort).
|
|
312
|
+
BLOCKERS=$(echo "$REVIEW_BODIES" | grep -ciE 'blocker|merge.?block' 2>/dev/null || true); BLOCKERS=${BLOCKERS:-0}
|
|
313
|
+
if [ "$TOTAL_AGENTS" -gt 0 ]; then
|
|
314
|
+
REVIEW_SUMMARY="${APPROVED}/${TOTAL_AGENTS} agents passed, ${BLOCKERS} blockers"
|
|
315
|
+
else
|
|
316
|
+
REVIEW_SUMMARY="—" # review data unavailable (e.g. review skipped)
|
|
317
|
+
fi
|
|
318
|
+
|
|
319
|
+
# Elapsed wall-clock: first FORGE agent comment → now.
|
|
320
|
+
FIRST_TS=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
321
|
+
--jq '[.[] | select(.body | contains("FORGE:")) | .created_at] | sort | .[0] // empty' 2>/dev/null)
|
|
322
|
+
if [ -n "$FIRST_TS" ]; then
|
|
323
|
+
START_EPOCH=$(date -u -d "$FIRST_TS" +%s 2>/dev/null \
|
|
324
|
+
|| python3 -c "import sys,datetime; ts=sys.argv[1].rstrip('Z'); print(int(datetime.datetime.fromisoformat(ts+'+00:00').timestamp()))" "$FIRST_TS" 2>/dev/null \
|
|
325
|
+
|| echo "")
|
|
326
|
+
NOW_EPOCH=$(date -u +%s)
|
|
327
|
+
if [ -n "$START_EPOCH" ]; then
|
|
328
|
+
ELAPSED_SECS=$((NOW_EPOCH - START_EPOCH))
|
|
329
|
+
ELAPSED=$(printf '%dm %02ds' $((ELAPSED_SECS / 60)) $((ELAPSED_SECS % 60)))
|
|
330
|
+
else ELAPSED="—"; ELAPSED_SECS=0; fi
|
|
331
|
+
else ELAPSED="—"; ELAPSED_SECS=0; fi
|
|
332
|
+
|
|
333
|
+
# Pipeline line + status — reflect the ACTUAL terminal state.
|
|
334
|
+
# merged → investigate → architect → build → review → merge ✓
|
|
335
|
+
# decomposed→ investigate → decompose ⏹ (sub-issues spawned)
|
|
336
|
+
# invalid → investigate → invalid ✗
|
|
337
|
+
# blocked → investigate → … → blocked ⚠ (needs-human)
|
|
338
|
+
# draft PR → append "(draft)" to the merge segment
|
|
339
|
+
case "{TERMINAL_STATE}" in
|
|
340
|
+
decomposed) PIPELINE_LINE="investigate → decompose ⏹"; CARD_STATUS="decomposed" ;;
|
|
341
|
+
invalid) PIPELINE_LINE="investigate → invalid ✗"; CARD_STATUS="invalid" ;;
|
|
342
|
+
blocked) PIPELINE_LINE="investigate → build → blocked ⚠"; CARD_STATUS="blocked" ;;
|
|
343
|
+
*) PIPELINE_LINE="investigate → architect → build → review → merge ✓"; CARD_STATUS="merged" ;;
|
|
344
|
+
esac
|
|
345
|
+
[ "$IS_DRAFT" = "true" ] && PIPELINE_LINE="${PIPELINE_LINE} (draft)"
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### C4.5b: Render the card to stdout
|
|
349
|
+
|
|
350
|
+
Print the card to stdout (the calling agent surfaces it in the terminal). Card inner
|
|
351
|
+
width is **51** columns. Truncate the title with an ellipsis (`…`) if `#{NUMBER} — {TITLE}`
|
|
352
|
+
exceeds the field; pad shorter lines with spaces so the right border `║` stays aligned.
|
|
353
|
+
|
|
354
|
+
```
|
|
355
|
+
╔═══════════════════════════════════════════════════╗
|
|
356
|
+
║ ForgeDock Pipeline Complete ║
|
|
357
|
+
╠═══════════════════════════════════════════════════╣
|
|
358
|
+
║ ║
|
|
359
|
+
║ Issue: #{NUMBER} — {TITLE} ║
|
|
360
|
+
║ Pipeline: {PIPELINE_LINE} ║
|
|
361
|
+
║ Commits: {COMMITS} ({ADDITIONS} additions, {DELETIONS} deletions) ║
|
|
362
|
+
║ PR: #{PR_NUMBER} (merged to {PR_TARGET}) ║
|
|
363
|
+
║ Review: {REVIEW_SUMMARY} ║
|
|
364
|
+
║ Time: {ELAPSED} ║
|
|
365
|
+
║ ║
|
|
366
|
+
╚═══════════════════════════════════════════════════╝
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
**Edge-case rendering**:
|
|
370
|
+
- Decomposed: title line stays; `Pipeline:` shows `investigate → decompose ⏹`; `PR:`, `Review:`, `Commits:` render `—`; the header reads `ForgeDock Pipeline — Decomposed`.
|
|
371
|
+
- Invalid: header `ForgeDock Pipeline — Closed (invalid)`; `Pipeline:` shows `investigate → invalid ✗`; downstream stats `—`.
|
|
372
|
+
- Blocked / needs-human: header `ForgeDock Pipeline — Blocked`; `Review:`/`PR:` reflect last known state; remaining stats `—`.
|
|
373
|
+
- Draft PR: `PR:` line appends `(draft)` and the merge segment is not marked `✓`.
|
|
374
|
+
|
|
375
|
+
### C4.5c: Build the machine-readable twin
|
|
376
|
+
|
|
377
|
+
Assemble the JSON object below (used verbatim by Phase C5). Numeric stats that were `—`
|
|
378
|
+
become `null` in JSON; never emit `"—"` as a number.
|
|
379
|
+
|
|
380
|
+
```bash
|
|
381
|
+
CARD_JSON=$(jq -nc \
|
|
382
|
+
--argjson issue {NUMBER} \
|
|
383
|
+
--arg title "{TITLE}" \
|
|
384
|
+
--arg status "$CARD_STATUS" \
|
|
385
|
+
--arg pipeline "$PIPELINE_LINE" \
|
|
386
|
+
--arg pr "{PR_NUMBER}" \
|
|
387
|
+
--arg target "$PR_TARGET" \
|
|
388
|
+
--arg commits "$COMMITS" --arg adds "$ADDITIONS" --arg dels "$DELETIONS" \
|
|
389
|
+
--arg review "$REVIEW_SUMMARY" --argjson blockers "${BLOCKERS:-0}" \
|
|
390
|
+
--argjson elapsed "${ELAPSED_SECS:-0}" \
|
|
391
|
+
'{issue:$issue, title:$title, status:$status, pipeline:$pipeline,
|
|
392
|
+
pr:($pr|tonumber? // null), pr_target:$target,
|
|
393
|
+
commits:($commits|tonumber? // null),
|
|
394
|
+
additions:($adds|tonumber? // null),
|
|
395
|
+
deletions:($dels|tonumber? // null),
|
|
396
|
+
review:$review, blockers:$blockers, elapsed_seconds:$elapsed}')
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
282
401
|
## Phase C5: Trajectory Log (MANDATORY)
|
|
283
402
|
|
|
284
403
|
Post the `<!-- FORGE:TRAJECTORY -->` comment as the final pipeline record:
|
|
@@ -305,9 +424,18 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:TRAJECTORY -->
|
|
|
305
424
|
|
|
306
425
|
**Anomalies**: None
|
|
307
426
|
|
|
308
|
-
**Pipeline completed**: {TIMESTAMP}
|
|
427
|
+
**Pipeline completed**: {TIMESTAMP}
|
|
428
|
+
|
|
429
|
+
<!-- FORGE:CARD ${CARD_JSON} -->"
|
|
309
430
|
```
|
|
310
431
|
|
|
432
|
+
The `<!-- FORGE:CARD {...} -->` block carries the machine-readable summary computed in
|
|
433
|
+
Phase C4.5c. It is wrapped in an HTML comment so it stays hidden in GitHub's rendered view
|
|
434
|
+
(keeping the trajectory comment clean) while remaining greppable in the raw body for platform
|
|
435
|
+
consumption — `/orchestrate` reads it to build per-issue cards. This block is **additive**:
|
|
436
|
+
all existing `FORGE:TRAJECTORY` consumers select via `contains("FORGE:TRAJECTORY")` and parse
|
|
437
|
+
the markdown table, so the embedded JSON does not affect them.
|
|
438
|
+
|
|
311
439
|
Where:
|
|
312
440
|
- `{PARENT_STATUS}` = `⏭ Skipped` (if no parent) or `✅ Complete` (if parent updated)
|
|
313
441
|
- `{PARENT_NOTES}` = `No parent tracker` or `Checked off in #{PARENT_REF}`
|
|
@@ -449,7 +577,7 @@ CLOSE_RESULT:
|
|
|
449
577
|
parent_closed: {true|false}
|
|
450
578
|
```
|
|
451
579
|
|
|
452
|
-
Where `PHASE_COMPLETE` means the current phase was closed but uncompleted phases remain — the issue is left OPEN for
|
|
580
|
+
Where `PHASE_COMPLETE` means the current phase was closed but uncompleted phases remain — the issue is left OPEN for work-on.md to re-evaluate on the next invocation (via Universal continuation rule: re-read labels, check terminal state, continue to next phase).
|
|
453
581
|
|
|
454
582
|
---
|
|
455
583
|
|
|
@@ -72,13 +72,30 @@ If the issue specifies a **Code branch** (`**Code branch**: \`{branch}\``), chec
|
|
|
72
72
|
1. **Check the right branch** — read from the branch specified in the issue body (`**Code branch**: \`{branch}\``) if present
|
|
73
73
|
2. **Read domain files** — start with the key files for the affected domain
|
|
74
74
|
3. **Verify claims** — does the code actually have the problem described?
|
|
75
|
-
4. **Git blame** — trace when/why the relevant code was written
|
|
75
|
+
4. **Git blame** — trace when/why the relevant code was written. Run bounded, local commands (no network round-trip):
|
|
76
|
+
```bash
|
|
77
|
+
# Introducing commit for each affected file (first commit that added it)
|
|
78
|
+
git log --reverse --format='%h %an %ad %s' --date=short -- {affected_file} | head -1
|
|
79
|
+
# Last-touch commit (most recent change)
|
|
80
|
+
git log -1 --format='%h %an %ad %s' --date=short -- {affected_file}
|
|
81
|
+
# Line-level blame for a specific suspect hunk, if the issue names one
|
|
82
|
+
git blame -L {start},{end} -- {affected_file}
|
|
83
|
+
```
|
|
84
|
+
Record the introducing commit and last-touch commit for each primary affected file — this feeds the mandatory **History findings** field in Phase 1C.
|
|
76
85
|
5. **Domain context discovery** (narrow scope only, 1–5 files):
|
|
77
86
|
```bash
|
|
78
87
|
git log --oneline --all -30 -- {affected_files} | grep -oP '#\d+' | sort -u
|
|
79
88
|
gh issue list -R {GH_REPO} --state closed --limit 8 --search "{function_name}"
|
|
80
89
|
```
|
|
81
90
|
Keep only file/function-level overlap. Max 5 related issues. Everything is a hint to verify, not a fact.
|
|
91
|
+
|
|
92
|
+
**Pickaxe pass (prior fix / regression detection)** — bounded to one pass, capped at 5 hits: search for prior additions/removals of the suspected symbol or literal string named in the issue (a function name, error string, or config key), independent of whether that fix was ever linked to a filed issue:
|
|
93
|
+
```bash
|
|
94
|
+
git log -S"{suspected_symbol_or_string}" --oneline -- {affected_files} | head -5
|
|
95
|
+
# Use -G instead of -S when the target is a regex pattern rather than a literal string
|
|
96
|
+
git log -G"{pattern}" --oneline -- {affected_files} | head -5
|
|
97
|
+
```
|
|
98
|
+
Any hit here is a candidate prior fix or reintroduced defect — read the commit body (`git show {hash}`) to confirm before citing it. Feed confirmed hits into the History findings field and let them inform the verdict (e.g. a defect being reintroduced raises severity).
|
|
82
99
|
6. **Determine root cause** — what's actually broken or missing?
|
|
83
100
|
7. **Identify affected files** — full list of files that need changes
|
|
84
101
|
8. **Fix-approach validation** — if the issue proposes a fix, don't adopt it as spec. Trace through the target system's middleware, auth, routing, config. Cross-domain: if fix in domain A interacts with domain B, read domain B's files too.
|
|
@@ -113,6 +130,12 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:INVESTIGATOR -->
|
|
|
113
130
|
### Evidence
|
|
114
131
|
{specific findings — function names, line numbers, behavior observed}
|
|
115
132
|
|
|
133
|
+
### History Findings
|
|
134
|
+
**Introducing commit**: {hash — author — date — subject, per primary affected file}
|
|
135
|
+
**Last touched**: {hash — author — date — subject}
|
|
136
|
+
**Pickaxe hits (prior fixes / regressions)**: {commit(s) found via \`git log -S\`/\`-G\`, or 'None found' — max 5}
|
|
137
|
+
{This field is MANDATORY — populate from the git blame + pickaxe commands in step 4/5. If a file is newly created (no history), write 'New file — no history.'}
|
|
138
|
+
|
|
116
139
|
### Recommendation
|
|
117
140
|
{what to build/fix, concrete and actionable}
|
|
118
141
|
|