forgedock 1.0.16 → 1.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +21 -3
  2. package/bin/forgedock.mjs +1992 -540
  3. package/bin/github-app-manifest.json.license +2 -0
  4. package/bin/hooks/session-start.mjs +4 -5
  5. package/bin/registry.mjs +20 -0
  6. package/bin/tui.mjs +172 -96
  7. package/commands/adopt.md +433 -0
  8. package/commands/analytics.md +2 -0
  9. package/commands/audit-agents.md +2 -0
  10. package/commands/audit.md +2 -0
  11. package/commands/changelog.md +354 -0
  12. package/commands/cleanup.md +2 -0
  13. package/commands/compat-audit.md +330 -0
  14. package/commands/deploy-info.md +6 -5
  15. package/commands/diagnose.md +483 -0
  16. package/commands/explain.md +349 -0
  17. package/commands/forge-stats.md +2 -0
  18. package/commands/forgedock-init.md +2 -0
  19. package/commands/geo-audit.md +2 -0
  20. package/commands/incident-response.md +2 -0
  21. package/commands/issue.md +2 -0
  22. package/commands/milestone.md +2 -0
  23. package/commands/optimize.md +411 -0
  24. package/commands/orchestrate.md +118 -0
  25. package/commands/pipeline-health.md +60 -1
  26. package/commands/qa-sweep.md +2 -0
  27. package/commands/quality-gate.md +37 -0
  28. package/commands/replay.md +263 -0
  29. package/commands/resume.md +264 -0
  30. package/commands/review-pr-agents.md +5 -1
  31. package/commands/review-pr-staging.md +2 -0
  32. package/commands/review-pr.md +85 -3
  33. package/commands/rollback.md +2 -0
  34. package/commands/scope.md +280 -0
  35. package/commands/security-audit.md +2 -0
  36. package/commands/status.md +262 -0
  37. package/commands/sync-ecosystem.md +2 -0
  38. package/commands/validate.md +2 -0
  39. package/commands/work-on/build/architect.md +128 -18
  40. package/commands/work-on/build/context.md +130 -17
  41. package/commands/work-on/build/implement.md +2 -0
  42. package/commands/work-on/build/validate.md +2 -0
  43. package/commands/work-on/build.md +32 -7
  44. package/commands/work-on/close.md +85 -0
  45. package/commands/work-on/decompose.md +2 -0
  46. package/commands/work-on/investigate.md +30 -3
  47. package/commands/work-on/review.md +13 -1
  48. package/commands/work-on-monolithic.md +2 -0
  49. package/commands/work-on.md +624 -36
  50. package/docs/BRAND.md +51 -0
  51. package/docs/CONFIG.md +94 -0
  52. package/docs/articles/per-repo-adaptive-scripts-token-savings.md +174 -0
  53. package/docs/claude-breakpoints.json +47 -0
  54. package/docs/site/getting-started.md +21 -0
  55. package/package.json +3 -3
  56. package/scripts/classify-lane.sh +145 -0
  57. package/scripts/transition-label.sh +161 -0
  58. package/scripts/validate-pr-target.sh +30 -0
  59. package/templates/devdocs/README.md +72 -0
  60. package/templates/devdocs/agent/using-forgedock.md +3 -0
  61. package/templates/devdocs/agent/using-github.md +3 -0
  62. package/templates/devdocs/index.yaml +162 -0
  63. package/templates/devdocs/project/architecture.md +3 -0
  64. package/templates/devdocs/project/conventions.md +3 -0
  65. package/templates/devdocs/project/custom-instructions.md +3 -0
  66. package/templates/devdocs/project/glossary.md +3 -0
  67. package/templates/devdocs/project/stack.md +3 -0
  68. package/templates/devdocs/templates/commit-convention.md +3 -0
  69. package/templates/devdocs/templates/issue.md +3 -0
  70. package/templates/devdocs/templates/pull-request.md +3 -0
  71. package/templates/devdocs/templates/review-finding.md +3 -0
@@ -2,6 +2,8 @@
2
2
  description: Pre-implementation context gathering — surfaces historical findings, bug patterns, and related code paths before the builder writes any code
3
3
  argument-hint: [issue number] [affected_files...] [--functions function_names...]
4
4
  ---
5
+ <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
6
+ <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
5
7
 
6
8
  # work-on/build/context — Pre-Implementation Context Gathering
7
9
 
@@ -11,6 +13,23 @@ argument-hint: [issue number] [affected_files...] [--functions function_names...
11
13
 
12
14
  ---
13
15
 
16
+ ## COMPLEXITY_BAND Guard (check BEFORE all phases)
17
+
18
+ Read COMPLEXITY_BAND from the `<!-- FORGE:FAST_PATH -->` comment on the issue:
19
+
20
+ ```bash
21
+ COMPLEXITY_BAND=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
22
+ --jq '.[] | select(.body | contains("FORGE:FAST_PATH")) | .body' 2>/dev/null \
23
+ | sed -n 's/.*\*\*COMPLEXITY_BAND\*\*: \([A-Z_]*\).*/\1/p' | head -1)
24
+ COMPLEXITY_BAND="${COMPLEXITY_BAND:-STANDARD}"
25
+ ```
26
+
27
+ **If COMPLEXITY_BAND: TRIVIAL** → skip all phases (C-1 through C4), post NO comment, return empty briefing to caller immediately. Do not query GitHub, do not read files. This is not an error — trivial single-file changes have no institutional memory to surface. <!-- Added: forge#679 -->
28
+
29
+ **If COMPLEXITY_BAND: STANDARD or COMPLEX** → proceed to Phase C-1 below.
30
+
31
+ ---
32
+
14
33
  ## Mission
15
34
 
16
35
  Surface what went wrong in this area before the builder writes a single line of code. The builder starts with the investigator report and contract — this step adds institutional memory: what did review agents catch last time someone touched these files, what bugs recurred, what other paths must stay consistent. When prior investigation Gists are linked in the issue body, fetch and summarize them so the builder has cross-issue context without manual lookups. When a milestone-level index Gist exists, use it to discover all investigation Gists for the milestone — providing full cross-issue context from a single URL. <!-- Updated: forge#341 -->
@@ -75,13 +94,106 @@ fi
75
94
 
76
95
  ### Step 1: Enumerate and filter applicable files
77
96
 
78
- Find all `.md` files under `DEVDOCS_PATH`, parse YAML frontmatter, keep those with `work-on` in `applies_to`. Sort by authority (`required` first, then `recommended`, then `reference`).
97
+ **Index-first path** (preferred when `index.yaml` exists): Read the lightweight index, extract issue labels to filter by domain, load only matching docs. Falls back to O(N) enumerate when index is absent.
79
98
 
80
99
  ```bash
81
100
  DEVDOCS_APPLICABLE="" # list of paths that apply
101
+ INDEX_PATH="${DEVDOCS_PATH}/index.yaml"
102
+
103
+ # --- Read devdocs.index_path override from forge.yaml (optional) ---
104
+ if [ -f "$FORGE_YAML_PATH" ]; then
105
+ INDEX_PATH_OVERRIDE=$(grep -A5 '^devdocs:' "$FORGE_YAML_PATH" \
106
+ | grep '^\s*index_path:' \
107
+ | head -1 \
108
+ | sed 's/.*index_path:\s*//' \
109
+ | tr -d '"'"'"' \
110
+ | tr -d '[:space:]')
111
+ if [ -n "$INDEX_PATH_OVERRIDE" ]; then
112
+ # Resolve relative to REPO_PATH if not absolute
113
+ case "$INDEX_PATH_OVERRIDE" in
114
+ /*) INDEX_PATH="$INDEX_PATH_OVERRIDE" ;;
115
+ *) INDEX_PATH="{REPO_PATH}/${INDEX_PATH_OVERRIDE}" ;;
116
+ esac
117
+ fi
118
+ fi
119
+
120
+ if [ -n "$DEVDOCS_PATH" ] && [ -f "$INDEX_PATH" ]; then
121
+ # --- Index-first loading path ---
122
+ echo "Devdocs index found at ${INDEX_PATH} — using selective domain loading"
123
+
124
+ # Read issue labels to determine domain(s)
125
+ ISSUE_LABELS=$(gh issue view {NUMBER} -R {GH_REPO} --json labels \
126
+ --jq '[.labels[].name] | join(" ")' 2>/dev/null || echo "")
127
+ echo "Issue labels: ${ISSUE_LABELS:-none}"
128
+
129
+ # Read index content
130
+ INDEX_CONTENT=$(cat "$INDEX_PATH" 2>/dev/null || echo "")
131
+
132
+ # Extract always_load paths (lines with "path:" under "always_load:" block)
133
+ ALWAYS_LOAD_PATHS=$(echo "$INDEX_CONTENT" \
134
+ | awk '/^always_load:/,/^[a-z]/' \
135
+ | grep '^\s*-\s*path:' \
136
+ | sed 's/.*path:\s*//' \
137
+ | tr -d '"'"'"' \
138
+ | tr -d '[:space:]')
139
+
140
+ # Extract domain blocks matching any issue label keyword
141
+ DOMAIN_PATHS=""
142
+ for label in $ISSUE_LABELS; do
143
+ # Strip workflow:, priority:, review-finding prefixes — use bare keyword
144
+ KEYWORD=$(echo "$label" | sed 's/^workflow://; s/^priority://; s/^review-finding$//')
145
+ [ -z "$KEYWORD" ] && continue
146
+ # Sanitize KEYWORD before AWK injection — strip chars that would break awk regex delimiters
147
+ SAFE_KEYWORD=$(printf '%s' "$KEYWORD" | tr -cd 'a-zA-Z0-9_-')
148
+ [ -z "$SAFE_KEYWORD" ] && continue
149
+
150
+ # Find the domain block matching this keyword, extract its doc paths
151
+ BLOCK_PATHS=$(echo "$INDEX_CONTENT" \
152
+ | awk "/^ ${SAFE_KEYWORD}:/{found=1; next} found && /^ [a-z]/{found=0} found && /^\s*-\s*path:/{print}" \
153
+ | sed 's/.*path:\s*//' \
154
+ | tr -d '"'"'"' \
155
+ | tr -d '[:space:]')
156
+
157
+ if [ -n "$BLOCK_PATHS" ]; then
158
+ echo "Domain '${SAFE_KEYWORD}' matched — adding docs: $(echo "$BLOCK_PATHS" | tr '\n' ' ')"
159
+ DOMAIN_PATHS="${DOMAIN_PATHS}${BLOCK_PATHS}"$'\n'
160
+ fi
161
+ done
162
+
163
+ # Combine always_load + domain paths (deduplicate)
164
+ ALL_PATHS=$(printf "%s\n%s" "$ALWAYS_LOAD_PATHS" "$DOMAIN_PATHS" \
165
+ | grep -v '^$' | sort -u)
166
+
167
+ # When no domain labels matched, load only authority:required files from always_load
168
+ if [ -z "$DOMAIN_PATHS" ]; then
169
+ echo "No domain labels matched index — loading always_load entries only"
170
+ ALL_PATHS="$ALWAYS_LOAD_PATHS"
171
+ fi
172
+
173
+ # Build DEVDOCS_APPLICABLE list with sort keys
174
+ while IFS= read -r rel_path; do
175
+ [ -z "$rel_path" ] && continue
176
+ ABS_PATH="${DEVDOCS_PATH}/${rel_path}"
177
+ [ -f "$ABS_PATH" ] || { echo "WARN: index references missing file: ${rel_path} — skipping"; continue; }
178
+
179
+ # Extract authority from frontmatter for sort key
180
+ FRONTMATTER=$(awk '/^---/{c++; if(c==1){next} if(c==2){exit}} c==1{print}' "$ABS_PATH" 2>/dev/null)
181
+ AUTHORITY=$(echo "$FRONTMATTER" | grep 'authority:' | head -1 | sed 's/.*authority:\s*//' | tr -d ' ')
182
+ case "$AUTHORITY" in
183
+ required) SORT_KEY="1" ;;
184
+ recommended) SORT_KEY="2" ;;
185
+ reference) SORT_KEY="3" ;;
186
+ *) SORT_KEY="4" ;;
187
+ esac
188
+ DEVDOCS_APPLICABLE="${DEVDOCS_APPLICABLE}${SORT_KEY}|${ABS_PATH}"$'\n'
189
+ done <<< "$ALL_PATHS"
190
+
191
+ DEVDOCS_APPLICABLE=$(printf "%s" "$DEVDOCS_APPLICABLE" | sort | cut -d'|' -f2-)
192
+
193
+ elif [ -n "$DEVDOCS_PATH" ]; then
194
+ # --- Fallback: O(N) enumerate (backward compatible — no index.yaml present) ---
195
+ echo "No index.yaml found at ${INDEX_PATH} — falling back to full enumerate (backward compatible)"
82
196
 
83
- if [ -n "$DEVDOCS_PATH" ]; then
84
- # Find all markdown files recursively
85
197
  while IFS= read -r -d '' mdfile; do
86
198
  # Extract frontmatter block (between first two --- markers)
87
199
  FRONTMATTER=$(awk '/^---/{c++; if(c==1){next} if(c==2){exit}} c==1{print}' "$mdfile" 2>/dev/null)
@@ -96,16 +208,16 @@ if [ -n "$DEVDOCS_PATH" ]; then
96
208
  reference) SORT_KEY="3" ;;
97
209
  *) SORT_KEY="4" ;;
98
210
  esac
99
- DEVDOCS_APPLICABLE="${DEVDOCS_APPLICABLE}${SORT_KEY}|${mdfile}\n"
211
+ DEVDOCS_APPLICABLE="${DEVDOCS_APPLICABLE}${SORT_KEY}|${mdfile}"$'\n'
100
212
  fi
101
213
  done < <(find "$DEVDOCS_PATH" -name "*.md" -print0 2>/dev/null)
102
214
 
103
215
  # Sort by authority key and extract paths
104
- DEVDOCS_APPLICABLE=$(printf "$DEVDOCS_APPLICABLE" | sort | cut -d'|' -f2-)
216
+ DEVDOCS_APPLICABLE=$(printf "%s" "$DEVDOCS_APPLICABLE" | sort | cut -d'|' -f2-)
105
217
  fi
106
218
 
107
219
  if [ -z "$DEVDOCS_APPLICABLE" ]; then
108
- echo "No devdocs files with 'applies_to: work-on' found — skipping Phase C-1 content read"
220
+ echo "No applicable devdocs files found — skipping Phase C-1 content read"
109
221
  fi
110
222
  ```
111
223
 
@@ -165,26 +277,26 @@ INDEX_GIST_URLS=""
165
277
 
166
278
  # Check issue body for milestone index annotation
167
279
  MILESTONE_INDEX_URL=$(echo "$ISSUE_BODY" \
168
- | grep -oP '(?<=<!-- FORGE:MILESTONE_INDEX: )https://[^ ]+(?= -->)' \
280
+ | sed -n 's/.*<!-- FORGE:MILESTONE_INDEX: \(https:\/\/[^ ]*\) -->.*/\1/p' \
169
281
  | head -1)
170
282
 
171
283
  # If not in issue body, check milestone description
172
284
  if [ -z "$MILESTONE_INDEX_URL" ] && [ -n "$MILESTONE_NUM" ]; then
173
285
  MILESTONE_DESC=$(gh api repos/{GH_REPO}/milestones/${MILESTONE_NUM} --jq '.description // ""' 2>/dev/null)
174
286
  MILESTONE_INDEX_URL=$(echo "$MILESTONE_DESC" \
175
- | grep -oP '(?<=<!-- FORGE:MILESTONE_INDEX: )https://[^ ]+(?= -->)' \
287
+ | sed -n 's/.*<!-- FORGE:MILESTONE_INDEX: \(https:\/\/[^ ]*\) -->.*/\1/p' \
176
288
  | head -1)
177
289
  fi
178
290
 
179
291
  # If found, fetch the index and extract individual Gist URLs from table rows
180
292
  if [ -n "$MILESTONE_INDEX_URL" ]; then
181
- INDEX_GIST_ID=$(echo "$MILESTONE_INDEX_URL" | grep -oP '[a-f0-9]{20,}' | tail -1)
293
+ INDEX_GIST_ID=$(echo "$MILESTONE_INDEX_URL" | grep -oE '[a-f0-9]{20,}' | tail -1)
182
294
  if [ -n "$INDEX_GIST_ID" ]; then
183
295
  INDEX_CONTENT=$(timeout 15 gh gist view "$INDEX_GIST_ID" --raw 2>/dev/null)
184
296
  if [ -n "$INDEX_CONTENT" ]; then
185
297
  # Extract Gist URLs from table rows (format: | ... | https://gist.github.com/... | ... |)
186
298
  INDEX_GIST_URLS=$(echo "$INDEX_CONTENT" \
187
- | grep -oP 'https://gist\.github\.com/[a-f0-9/]+' \
299
+ | grep -oE 'https://gist\.github\.com/[a-f0-9/]+' \
188
300
  | head -10)
189
301
  echo "Milestone index fetched: found $(echo "$INDEX_GIST_URLS" | wc -l) investigation Gist(s)"
190
302
  else
@@ -198,7 +310,7 @@ fi
198
310
 
199
311
  ```bash
200
312
  GIST_URLS=$(echo "$ISSUE_BODY" \
201
- | grep -oP '(?<=<!-- FORGE:PRIOR_GIST: )https://[^ ]+(?= -->)' \
313
+ | sed -n 's/.*<!-- FORGE:PRIOR_GIST: \(https:\/\/[^ ]*\) -->.*/\1/p' \
202
314
  | head -5)
203
315
 
204
316
  # Merge with any URLs discovered from milestone index (deduplicate)
@@ -223,7 +335,7 @@ GIST_SUMMARIES=""
223
335
 
224
336
  for url in $GIST_URLS; do
225
337
  # Extract Gist ID from URL (last path segment, strip any trailing slash)
226
- GIST_ID=$(echo "$url" | grep -oP '[a-f0-9]{20,}' | tail -1)
338
+ GIST_ID=$(echo "$url" | grep -oE '[a-f0-9]{20,}' | tail -1)
227
339
 
228
340
  if [ -z "$GIST_ID" ]; then
229
341
  echo "WARNING: Could not extract Gist ID from URL: $url — skipping"
@@ -241,10 +353,10 @@ for url in $GIST_URLS; do
241
353
  fi
242
354
 
243
355
  # Extract key sections for summary (~2K chars target per Gist)
244
- VERDICT=$(echo "$GIST_CONTENT" | grep -oP '(?<=verdict: )\w+' | head -1)
245
- TASK_TYPE=$(echo "$GIST_CONTENT" | grep -oP '(?<=task_type: ).+' | head -1)
246
- SEVERITY=$(echo "$GIST_CONTENT" | grep -oP '(?<=severity: )\w+' | head -1)
247
- SOURCE_ISSUE=$(echo "$GIST_CONTENT" | grep -oP '(?<=issue: )\d+' | head -1)
356
+ VERDICT=$(echo "$GIST_CONTENT" | sed -n 's/.*verdict: \([A-Za-z_]*\).*/\1/p' | head -1)
357
+ TASK_TYPE=$(echo "$GIST_CONTENT" | sed -n 's/.*task_type: \(.*\)/\1/p' | head -1)
358
+ SEVERITY=$(echo "$GIST_CONTENT" | sed -n 's/.*severity: \([A-Za-z_]*\).*/\1/p' | head -1)
359
+ SOURCE_ISSUE=$(echo "$GIST_CONTENT" | sed -n 's/.*issue: \([0-9]*\).*/\1/p' | head -1)
248
360
 
249
361
  # Extract structured sections: Root Cause, Recommendation, Affected Files
250
362
  ROOT_CAUSE=$(echo "$GIST_CONTENT" \
@@ -318,7 +430,7 @@ Mine git log for commit messages referencing issues, then fetch those issues:
318
430
  ```bash
319
431
  # Step 1: find issue numbers from git history on affected files
320
432
  git log --oneline -30 -- {AFFECTED_FILES} \
321
- | grep -oP '#\d+' \
433
+ | grep -oE '#[0-9]+' \
322
434
  | sort -u \
323
435
  | head -8
324
436
 
@@ -448,6 +560,7 @@ gh issue comment {NUMBER} -R {GH_REPO} --body "<!-- FORGE:CONTEXT -->
448
560
  ## Skip Conditions
449
561
 
450
562
  Skip this entire step (post nothing, return empty briefing) if:
563
+ - **COMPLEXITY_BAND: TRIVIAL** — checked via FORGE:FAST_PATH comment at entry (see guard above) <!-- Primary skip path: forge#679 -->
451
564
  - Issue is a 1-file config or docs edit with no code logic
452
565
  - The affected files have zero git history (new files being created)
453
566
  - `{AFFECTED_FILES}` is empty (investigation produced no file list)
@@ -2,6 +2,8 @@
2
2
  description: Implementation agent — writes code, makes commits, posts builder comment
3
3
  argument-hint: [issue number] [--repo GH_REPO] [--gh-flag GH_FLAG] [--worktree PATH] [--branch BRANCH]
4
4
  ---
5
+ <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
6
+ <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
5
7
 
6
8
  # work-on/build/implement — Implementation Subcommand
7
9
 
@@ -2,6 +2,8 @@
2
2
  description: Validation agent — quality gate loop, format/verify, proxy check, deploy check
3
3
  argument-hint: [issue number] [--repo GH_REPO] [--gh-flag GH_FLAG] [--worktree PATH] [--files FILE1 FILE2...]
4
4
  ---
5
+ <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
6
+ <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
5
7
 
6
8
  # work-on/build/validate — Validation Subcommand
7
9
 
@@ -2,6 +2,8 @@
2
2
  description: Build subcommand — create worktree, post contract, sequence context/architect/implement/validate
3
3
  argument-hint: [issue number] [--repo GH_REPO] [--gh-flag GH_FLAG] [--base PR_BASE]
4
4
  ---
5
+ <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
6
+ <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
5
7
 
6
8
  # work-on/build — Build Phase Orchestrator
7
9
 
@@ -13,7 +15,7 @@ argument-hint: [issue number] [--repo GH_REPO] [--gh-flag GH_FLAG] [--base PR_BA
13
15
  **Agent model policy**: Default `model: "sonnet"`. If Sonnet is rate-limited, fall back to `model: "opus"`.
14
16
  **NEVER use plan mode (EnterPlanMode).**
15
17
 
16
- **CRITICAL: You MUST execute ALL phases B0–B6 in order. After each Skill() call returns, you MUST continue to the next phase. Do NOT skip phases B3 (context) or B4 (architect) — they post mandatory `FORGE:CONTEXT` and `FORGE:ARCHITECT` comments that the implement phase reads as its primary input. Skipping them degrades build quality.**
18
+ **CRITICAL: You MUST execute ALL phases B0–B6 in order. After each Skill() call returns, you MUST continue to the next phase. Phases B3 (context) and B4 (architect) are skipped ONLY when COMPLEXITY_BAND: TRIVIAL (read from FORGE:FAST_PATH comment in Phase B0). For STANDARD and COMPLEX tasks they are NOT optional — skipping them degrades build quality.**
17
19
 
18
20
  ---
19
21
 
@@ -53,6 +55,16 @@ Extract from investigation report:
53
55
  - Recommendation
54
56
  - Task type (Bug Fix / Feature / Refactor / Maintenance / UI/UX / Full-Stack)
55
57
 
58
+ **Read COMPLEXITY_BAND** (from FORGE:FAST_PATH comment posted by Phase 3B):
59
+ ```bash
60
+ COMPLEXITY_BAND=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
61
+ --jq '.[] | select(.body | contains("FORGE:FAST_PATH")) | .body' 2>/dev/null \
62
+ | sed -n 's/.*\*\*COMPLEXITY_BAND\*\*: \([A-Z_]*\).*/\1/p' | head -1)
63
+ # Default to STANDARD if not found (conservative — runs full pipeline)
64
+ COMPLEXITY_BAND="${COMPLEXITY_BAND:-STANDARD}"
65
+ echo "COMPLEXITY_BAND: $COMPLEXITY_BAND"
66
+ ```
67
+
56
68
  ---
57
69
 
58
70
  ## Phase B1: Create Worktree & Branch
@@ -146,7 +158,7 @@ After posting the Builder Contract, extract the primary function/class names fro
146
158
  FUNCTION_NAMES=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
147
159
  --jq '.[] | select(.body | contains("FORGE:CONTRACT")) | .body' \
148
160
  | awk '/^### Deliverables/{p=1; next} /^### /{p=0} p' \
149
- | grep -oP '`[A-Za-z_][A-Za-z0-9_]*`' \
161
+ | grep -oE '`[A-Za-z_][A-Za-z0-9_]*`' \
150
162
  | tr -d '`' \
151
163
  | sort -u \
152
164
  | tr '\n' ' ' \
@@ -161,9 +173,11 @@ If `FUNCTION_NAMES` is non-empty, it will be passed via `--functions` to the con
161
173
 
162
174
  ---
163
175
 
164
- ## Phase B3: Context Gathering (MANDATORY Subcommand)
176
+ ## Phase B3: Context Gathering (MANDATORY for STANDARD/COMPLEX — skip for TRIVIAL)
177
+
178
+ **Skip if COMPLEXITY_BAND: TRIVIAL** (read from FORGE:FAST_PATH in Phase B0) — do not invoke the context subcommand. Proceed directly to Phase B4.
165
179
 
166
- **This phase is NOT optional.** Always invoke it regardless of issue size or complexity. Do NOT invent skip heuristics (e.g. "FAST_PATH") — the context subcommand handles trivial cases gracefully and returns quickly.
180
+ **For STANDARD and COMPLEX tasks**: Always invoke. Do NOT skip without a TRIVIAL COMPLEXITY_BAND.
167
181
 
168
182
  Invoke the context subcommand to surface historical review findings and bug patterns:
169
183
 
@@ -184,9 +198,11 @@ Skill("work-on:build:context", args="{NUMBER} --repo {GH_REPO} --gh-flag {GH_FLA
184
198
 
185
199
  ---
186
200
 
187
- ## Phase B4: Architecture Planning (MANDATORY Subcommand)
201
+ ## Phase B4: Architecture Planning (MANDATORY for STANDARD/COMPLEX — skip for TRIVIAL)
188
202
 
189
- **This phase is NOT optional.** Always invoke it regardless of issue size or complexity. Do NOT invent skip heuristics — the architect subcommand handles simple changes gracefully and returns quickly. Even a 1-file fix benefits from cross-path consistency checks.
203
+ **Skip if COMPLEXITY_BAND: TRIVIAL** (read from FORGE:FAST_PATH in Phase B0) — do not invoke the architect subcommand. Proceed directly to Phase B5.
204
+
205
+ **For STANDARD and COMPLEX tasks**: Always invoke. Even a 1-file STANDARD fix benefits from cross-path consistency checks. Do NOT skip without a TRIVIAL COMPLEXITY_BAND.
190
206
 
191
207
  Invoke the architect subcommand to trace all affected code paths and produce an ordered implementation plan:
192
208
 
@@ -229,9 +245,18 @@ Skill("work-on:build:validate", args="{NUMBER} --repo {GH_REPO} --gh-flag {GH_FL
229
245
  Where `{CHANGED_FILES}` is the space-separated list of files changed by the implement subcommand (read from `IMPLEMENT_RESULT` or from the `<!-- FORGE:BUILDER -->` comment).
230
246
 
231
247
  **After subcommand returns**:
232
- - `VALIDATE_RESULT: gate_passed: true` → build complete, return `BUILD_RESULT: status: COMPLETE`
248
+ - `VALIDATE_RESULT: gate_passed: true` → write checkpoint, then return `BUILD_RESULT: status: COMPLETE`
233
249
  - `VALIDATE_RESULT: gate_passed: false` → subcommand has already posted comment and added `needs-human` label; return `BUILD_RESULT: status: BLOCKED`
234
250
 
251
+ **When gate_passed is true — write machine-readable phase checkpoint before returning (MANDATORY)**:
252
+ ```bash
253
+ CHECKPOINT_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
254
+ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:CHECKPOINT -->
255
+ \`\`\`json
256
+ {\"phase\": \"BUILD\", \"status\": \"COMPLETE\", \"next_phase\": \"REVIEW\", \"timestamp\": \"${CHECKPOINT_TIMESTAMP}\"}
257
+ \`\`\`"
258
+ ```
259
+
235
260
  ---
236
261
 
237
262
  ## Output
@@ -2,6 +2,8 @@
2
2
  description: Close subcommand — update project board, final issue body, parent tracker, summary report, trajectory log
3
3
  argument-hint: [issue number] [--repo GH_REPO] [--gh-flag GH_FLAG] [--pr PR_NUMBER] [--base PR_BASE] [--branch BRANCH] [--worktree WORKTREE_PATH]
4
4
  ---
5
+ <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
6
+ <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
5
7
 
6
8
  # work-on/close — Close & Trajectory Subcommand
7
9
 
@@ -314,6 +316,88 @@ Where:
314
316
 
315
317
  ---
316
318
 
319
+ ## Phase C5.5: Graph Decision Record (MANDATORY when PR exists)
320
+
321
+ **Skip if**: `{PR_NUMBER}` is empty OR `<!-- FORGE:DECISION_RECORD -->` already posted on the PR.
322
+
323
+ Post a consolidated provenance artifact to the PR that proves the merge was backed by citable evidence. Mirrors the Phase 7C step in `work-on.md` — must stay in sync.
324
+
325
+ **Idempotency check**:
326
+ ```bash
327
+ GDR_EXISTS=$(gh api repos/{GH_REPO}/issues/{PR_NUMBER}/comments \
328
+ --jq '[.[] | select(.body | contains("FORGE:DECISION_RECORD"))] | length > 0' 2>/dev/null || echo "false")
329
+ ```
330
+
331
+ **Extract context edge counts** from FORGE:CONTEXT comment:
332
+ ```bash
333
+ CONTEXT_COMMENT=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
334
+ --jq '.[] | select(.body | contains("FORGE:CONTEXT")) | .body' 2>/dev/null | head -1)
335
+ REVIEW_FINDING_COUNT=$(echo "$CONTEXT_COMMENT" | grep -oP '#\d+' | wc -l | tr -d ' ')
336
+ REVIEW_FINDING_COUNT=${REVIEW_FINDING_COUNT:-0}
337
+ ```
338
+
339
+ **Extract review verdict** from PR review summary:
340
+ ```bash
341
+ REVIEW_SUMMARY=$(gh api repos/{GH_REPO}/issues/{PR_NUMBER}/comments \
342
+ --jq '[.[] | select(.body | contains("FORGE:REVIEWER") or (.body | test("APPROVED:|CHANGES REQUESTED:"; "i")))] | last | .body // ""' 2>/dev/null || echo '')
343
+ REVIEW_VERDICT=$(echo "$REVIEW_SUMMARY" | grep -oP '(?<=Verdict: )(APPROVED|CHANGES REQUESTED)' | head -1 || echo "APPROVED")
344
+ FINDINGS_COUNT=$(echo "$REVIEW_SUMMARY" | grep -oP '\d+(?= findings)' | head -1 || echo "0")
345
+ AGENTS_RUN=$(echo "$REVIEW_SUMMARY" | grep -oP '\d+(?= agents)' | head -1 || echo "0")
346
+ ```
347
+
348
+ **Post GDR to PR** (not to issue — PR comment is the permanent artifact):
349
+ ```bash
350
+ if [ "$GDR_EXISTS" != "true" ] && [ -n "{PR_NUMBER}" ]; then
351
+ GDR_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
352
+ HEAD_SHA=$(gh pr view {PR_NUMBER} {GH_FLAG} --json headRefOid --jq '.headRefOid' 2>/dev/null || echo "")
353
+ MERGE_COMMIT=$(gh pr view {PR_NUMBER} {GH_FLAG} --json mergeCommit --jq '.mergeCommit.oid // ""' 2>/dev/null || echo "")
354
+
355
+ gh pr comment {PR_NUMBER} {GH_FLAG} --body "<!-- FORGE:DECISION_RECORD -->
356
+ ## Graph Decision Record — Issue #${NUMBER} / PR #${PR_NUMBER}
357
+
358
+ \`\`\`json
359
+ {
360
+ \"schema_version\": \"1\",
361
+ \"issue\": ${NUMBER},
362
+ \"pr\": ${PR_NUMBER},
363
+ \"repo\": \"{GH_REPO}\",
364
+ \"lane\": \"feature\",
365
+ \"pr_base\": \"{PR_BASE}\",
366
+ \"branch\": \"{BRANCH}\",
367
+ \"head_sha\": \"${HEAD_SHA}\",
368
+ \"merge_commit\": \"${MERGE_COMMIT}\",
369
+ \"investigation\": {
370
+ \"verdict\": \"{VERDICT}\",
371
+ \"confidence\": \"{CONFIDENCE}\",
372
+ \"task_type\": \"{TASK_TYPE}\"
373
+ },
374
+ \"context\": {
375
+ \"historical_edges_referenced\": ${REVIEW_FINDING_COUNT},
376
+ \"forge_annotations_read\": [\"FORGE:INVESTIGATOR\", \"FORGE:CONTRACT\", \"FORGE:CONTEXT\", \"FORGE:ARCHITECT\", \"FORGE:BUILDER\"]
377
+ },
378
+ \"build\": {
379
+ \"files_changed\": {FILES_CHANGED},
380
+ \"quality_gate\": \"{pass|fail}\",
381
+ \"quality_gate_iterations\": {GATE_ITERATIONS}
382
+ },
383
+ \"review\": {
384
+ \"verdict\": \"${REVIEW_VERDICT:-APPROVED}\",
385
+ \"findings_created\": ${FINDINGS_COUNT},
386
+ \"agents_run\": ${AGENTS_RUN}
387
+ },
388
+ \"merge\": {
389
+ \"merged_at\": \"${GDR_TIMESTAMP}\",
390
+ \"justification\": \"Investigation confirmed ({VERDICT}/{CONFIDENCE}), quality gate passed, review ${REVIEW_VERDICT:-approved}\"
391
+ }
392
+ }
393
+ \`\`\`"
394
+ fi
395
+ ```
396
+
397
+ <!-- Added: forge#776 -->
398
+
399
+ ---
400
+
317
401
  ## Phase C6: Worktree & Branch Cleanup
318
402
 
319
403
  Remove the git worktree and delete the local feature branch after the PR has merged. This prevents worktree accumulation across pipeline runs.
@@ -360,6 +444,7 @@ CLOSE_RESULT:
360
444
  status: COMPLETE | ALREADY_DONE | PHASE_COMPLETE
361
445
  issue_state: closed | open
362
446
  trajectory_url: {url of FORGE:TRAJECTORY comment}
447
+ decision_record_url: {url of FORGE:DECISION_RECORD comment on PR, or "" if skipped}
363
448
  parent_updated: {true|false}
364
449
  parent_closed: {true|false}
365
450
  ```
@@ -2,6 +2,8 @@
2
2
  description: Decompose subcommand — break a complex issue into ordered sub-issues, post FORGE:DECOMPOSED, stop
3
3
  argument-hint: [issue number] [--repo GH_REPO] [--gh-flag GH_FLAG]
4
4
  ---
5
+ <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
6
+ <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
5
7
 
6
8
  # work-on/decompose — Decomposition Subcommand
7
9
 
@@ -2,6 +2,8 @@
2
2
  description: Investigate a GitHub issue — validate it's real, determine root cause, post findings
3
3
  argument-hint: [issue number] [--repo {owner}/{repo}] [--gh-flag "-R {owner}/{repo}"]
4
4
  ---
5
+ <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
6
+ <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
5
7
 
6
8
  # /work-on:investigate — Issue Investigation Subcommand
7
9
 
@@ -352,8 +354,13 @@ ${INDEX_ENTRY}"
352
354
  fi
353
355
 
354
356
  # Update the Gist
355
- echo "$UPDATED_INDEX" | gh gist edit "$INDEX_GIST_ID" -f "$INDEX_FILENAME" - 2>/dev/null
356
- if [ $? -eq 0 ]; then
357
+ # gh gist edit does not support stdin via '-'; use a temp file instead
358
+ TMPFILE=$(mktemp --suffix=.md)
359
+ echo "$UPDATED_INDEX" > "$TMPFILE"
360
+ gh gist edit "$INDEX_GIST_ID" -f "$INDEX_FILENAME" "$TMPFILE" 2>/dev/null
361
+ EDIT_EXIT=$?
362
+ rm -f "$TMPFILE"
363
+ if [ $EDIT_EXIT -eq 0 ]; then
357
364
  INDEX_URL="$EXISTING_INDEX_URL"
358
365
  echo "Milestone index Gist updated: ${INDEX_URL}"
359
366
  else
@@ -400,6 +407,16 @@ fi
400
407
  ```bash
401
408
  gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:ready-to-build" --remove-label "workflow:investigating"
402
409
  ```
410
+
411
+ Write machine-readable phase checkpoint (MUST execute immediately after label update, before returning):
412
+ ```bash
413
+ CHECKPOINT_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
414
+ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:CHECKPOINT -->
415
+ \`\`\`json
416
+ {\"phase\": \"INVESTIGATION\", \"status\": \"COMPLETE\", \"next_phase\": \"BUILD\", \"timestamp\": \"${CHECKPOINT_TIMESTAMP}\"}
417
+ \`\`\`"
418
+ ```
419
+
403
420
  Return verdict to caller (work-on routing loop proceeds to build).
404
421
 
405
422
  **CONFIRMED or PARTIAL with decompose: YES**:
@@ -407,6 +424,16 @@ Return verdict to caller (work-on routing loop proceeds to build).
407
424
  gh issue edit {NUMBER} {GH_FLAG} --remove-label "workflow:investigating"
408
425
  ```
409
426
  Do NOT add `workflow:ready-to-build` — the routing loop will invoke `work-on:decompose` based on the `decompose: YES` return value.
427
+
428
+ Write machine-readable phase checkpoint (MUST execute immediately after label update, before returning):
429
+ ```bash
430
+ CHECKPOINT_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
431
+ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:CHECKPOINT -->
432
+ \`\`\`json
433
+ {\"phase\": \"INVESTIGATION\", \"status\": \"COMPLETE\", \"next_phase\": \"DECOMPOSE\", \"timestamp\": \"${CHECKPOINT_TIMESTAMP}\"}
434
+ \`\`\`"
435
+ ```
436
+
410
437
  Return verdict to caller (work-on routing loop proceeds to decompose).
411
438
 
412
439
  **INVALID**:
@@ -414,7 +441,7 @@ Return verdict to caller (work-on routing loop proceeds to decompose).
414
441
  gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:invalid" --remove-label "workflow:investigating"
415
442
  gh issue close {NUMBER} {GH_FLAG} --comment "Closing as invalid: {reason from investigation}"
416
443
  ```
417
- Return INVALID to caller (work-on stops).
444
+ Return INVALID to caller (work-on stops). No checkpoint written — INVALID is terminal.
418
445
 
419
446
  ---
420
447
 
@@ -2,6 +2,8 @@
2
2
  description: Review subcommand — push branch, create PR, invoke /review-pr with --auto-merge
3
3
  argument-hint: [issue number] [--repo GH_REPO] [--gh-flag GH_FLAG] [--worktree PATH] [--branch BRANCH] [--base PR_BASE]
4
4
  ---
5
+ <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
6
+ <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
5
7
 
6
8
  # work-on/review — Review & PR Creation Subcommand
7
9
 
@@ -208,7 +210,17 @@ gh issue view {NUMBER} {GH_FLAG} --json state --jq '.state'
208
210
  ```
209
211
 
210
212
  **Cases**:
211
- - PR MERGED (issue OPEN or CLOSED) → `REVIEW_RESULT: status: COMPLETE` — do NOT close the issue or add labels here; the router will route to `work-on:close` which handles issue closure, label updates, project board, trajectory log, and worktree cleanup.
213
+ - PR MERGED (issue OPEN or CLOSED) → write checkpoint, then return `REVIEW_RESULT: status: COMPLETE` — do NOT close the issue or add labels here; the router will route to `work-on:close` which handles issue closure, label updates, project board, trajectory log, and worktree cleanup.
214
+
215
+ Write machine-readable phase checkpoint before returning (MANDATORY when PR is MERGED):
216
+ ```bash
217
+ CHECKPOINT_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
218
+ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:CHECKPOINT -->
219
+ \`\`\`json
220
+ {\"phase\": \"REVIEW\", \"status\": \"COMPLETE\", \"next_phase\": \"CLOSE\", \"timestamp\": \"${CHECKPOINT_TIMESTAMP}\"}
221
+ \`\`\`"
222
+ ```
223
+
212
224
  - PR NOT MERGED → attempt manual merge:
213
225
  ```bash
214
226
  gh pr merge {PR_NUMBER} {GH_FLAG} --merge --auto
@@ -2,6 +2,8 @@
2
2
  description: "[BENCHMARK] Monolithic work-on — single-prompt pipeline with no Skill sub-phase boundaries"
3
3
  argument-hint: [issue number]
4
4
  ---
5
+ <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
6
+ <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
5
7
 
6
8
  # /work-on-monolithic — Full Issue Pipeline (Single Prompt)
7
9