forgedock 1.0.16 → 1.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -3
- package/bin/cross-publish.mjs +344 -0
- package/bin/forgedock.mjs +2205 -541
- package/bin/github-app-manifest.json.license +2 -0
- package/bin/hooks/session-start.mjs +4 -5
- package/bin/registry.mjs +20 -0
- package/bin/tui.mjs +172 -96
- package/commands/adopt.md +433 -0
- package/commands/analytics.md +2 -0
- package/commands/audit-agents.md +2 -0
- package/commands/audit.md +2 -0
- package/commands/autopilot.md +77 -0
- package/commands/changelog.md +354 -0
- package/commands/cleanup.md +2 -0
- package/commands/compat-audit.md +330 -0
- package/commands/deploy-info.md +6 -5
- package/commands/design-bench.md +176 -0
- package/commands/design-render-critique-loop.md +103 -0
- package/commands/design.md +259 -0
- package/commands/diagnose.md +483 -0
- package/commands/explain.md +349 -0
- package/commands/forge-stats.md +2 -0
- package/commands/forgedock-init.md +2 -0
- package/commands/geo-audit.md +2 -0
- package/commands/incident-response.md +2 -0
- package/commands/issue.md +9 -3
- package/commands/milestone.md +2 -0
- package/commands/optimize.md +411 -0
- package/commands/orchestrate.md +239 -0
- package/commands/pipeline-health.md +60 -1
- package/commands/qa-sweep.md +39 -2
- package/commands/quality-gate.md +67 -0
- package/commands/replay.md +263 -0
- package/commands/resume.md +264 -0
- package/commands/review-pr-agents.md +5 -1
- package/commands/review-pr-staging.md +160 -2
- package/commands/review-pr.md +85 -3
- package/commands/rollback.md +2 -0
- package/commands/scope.md +280 -0
- package/commands/security-audit.md +2 -0
- package/commands/status.md +262 -0
- package/commands/sync-ecosystem.md +2 -0
- package/commands/test-gate.md +929 -0
- package/commands/validate.md +2 -0
- package/commands/work-on/build/architect.md +128 -18
- package/commands/work-on/build/context.md +130 -17
- package/commands/work-on/build/implement.md +7 -3
- package/commands/work-on/build/validate.md +2 -0
- package/commands/work-on/build.md +32 -7
- package/commands/work-on/close.md +85 -0
- package/commands/work-on/decompose.md +2 -0
- package/commands/work-on/investigate.md +30 -3
- package/commands/work-on/review.md +13 -1
- package/commands/work-on-monolithic.md +2 -0
- package/commands/work-on.md +676 -38
- package/docs/BRAND.md +51 -0
- package/docs/CI.md +177 -0
- package/docs/CONFIG.md +94 -0
- package/docs/FORGE-PROTOCOL.md +324 -0
- package/docs/articles/b-vs-c-baseline.md +210 -0
- package/docs/articles/full-abc-benchmark.md +205 -0
- package/docs/articles/per-repo-adaptive-scripts-token-savings.md +174 -0
- package/docs/articles/selective-spec-loading-token-savings.md +140 -0
- package/docs/claude-breakpoints.json +47 -0
- package/docs/design/abc-benchmark.md +195 -0
- package/docs/design/design-architect-rationale.md +123 -0
- package/docs/design/design-memory.md +61 -0
- package/docs/design/design-spec-schema.md +248 -0
- package/docs/design/design-system-lint.md +150 -0
- package/docs/design/divergent-generation.md +106 -0
- package/docs/design/effects-appropriateness.md +133 -0
- package/docs/design/fixtures/briefs/README.md +47 -0
- package/docs/design/fixtures/briefs/cadence.md +39 -0
- package/docs/design/fixtures/briefs/plume.md +39 -0
- package/docs/design/fixtures/briefs/slipstream.md +39 -0
- package/docs/design/fixtures/briefs/tender.md +39 -0
- package/docs/design/fixtures/briefs/voltage.md +39 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/cadence/arm-b/run-1.html +364 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/cadence/arm-b/run-2.html +420 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/cadence/arm-b/run-3.html +506 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/plume/arm-b/run-1.html +611 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/plume/arm-b/run-2.html +594 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/plume/arm-b/run-3.html +817 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/runs.json +175 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/scorecard.json +138 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/slipstream/arm-b/run-1.html +373 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/slipstream/arm-b/run-2.html +398 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/slipstream/arm-b/run-3.html +595 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/tender/arm-b/run-1.html +531 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/tender/arm-b/run-2.html +565 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/tender/arm-b/run-3.html +674 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/voltage/arm-b/run-1.html +388 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/voltage/arm-b/run-2.html +463 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/voltage/arm-b/run-3.html +568 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-a/run-1.html +562 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-a/run-2.html +618 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-a/run-3.html +618 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-b/run-1.html +364 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-b/run-2.html +420 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-b/run-3.html +506 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-a/run-1.html +527 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-a/run-2.html +445 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-a/run-3.html +483 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-b/run-1.html +611 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-b/run-2.html +594 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-b/run-3.html +817 -0
- package/docs/design/fixtures/runs/full-abc/runs.json +190 -0
- package/docs/design/fixtures/runs/full-abc/scorecard.json +144 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-a/run-1.html +496 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-a/run-2.html +447 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-a/run-3.html +564 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-b/run-1.html +373 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-b/run-2.html +398 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-b/run-3.html +595 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-a/run-1.html +622 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-a/run-2.html +475 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-a/run-3.html +660 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-b/run-1.html +531 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-b/run-2.html +565 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-b/run-3.html +674 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-a/run-1.html +525 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-a/run-2.html +446 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-a/run-3.html +463 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-b/run-1.html +388 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-b/run-2.html +463 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-b/run-3.html +568 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-a/run-1.html +1068 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-a/run-2.html +1423 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-a/run-3.html +1245 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-b/run-1.html +305 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-b/run-2.html +327 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-b/run-3.html +428 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-a/run-1.html +1270 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-a/run-2.html +1172 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-a/run-3.html +1532 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-b/run-1.html +315 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-b/run-2.html +342 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-b/run-3.html +353 -0
- package/docs/design/fixtures/runs/full-abc-opus/runs.json +190 -0
- package/docs/design/fixtures/runs/full-abc-opus/scorecard.json +144 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-a/run-1.html +345 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-a/run-2.html +396 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-a/run-3.html +502 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-b/run-1.html +408 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-b/run-2.html +446 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-b/run-3.html +496 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-a/run-1.html +813 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-a/run-2.html +711 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-a/run-3.html +721 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-b/run-1.html +280 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-b/run-2.html +291 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-b/run-3.html +366 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-a/run-1.html +1010 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-a/run-2.html +836 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-a/run-3.html +930 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-b/run-1.html +407 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-b/run-2.html +396 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-b/run-3.html +529 -0
- package/docs/design/foundations/brutalist.css +294 -0
- package/docs/design/foundations/editorial.css +300 -0
- package/docs/design/foundations/minimal-luxury.css +295 -0
- package/docs/design/foundations/technical.css +293 -0
- package/docs/design/foundations/warm-photo.css +299 -0
- package/docs/design/reference-corpus.md +1726 -0
- package/docs/design/render-critique-loop.md +57 -0
- package/docs/site/getting-started.md +21 -0
- package/docs/spec-graph-schema.md +397 -0
- package/package.json +3 -3
- package/scripts/bench-scorecard.mjs +239 -0
- package/scripts/build-spec-graph.mjs +509 -0
- package/scripts/classify-lane.sh +138 -0
- package/scripts/design-system-lint.mjs +993 -0
- package/scripts/graph-query.sh +486 -0
- package/scripts/transition-label.sh +169 -0
- package/scripts/validate-pr-target.sh +30 -0
- package/scripts/validate-spec-graph.sh +423 -0
- package/templates/devdocs/README.md +72 -0
- package/templates/devdocs/agent/using-forgedock.md +3 -0
- package/templates/devdocs/agent/using-github.md +3 -0
- package/templates/devdocs/index.yaml +162 -0
- package/templates/devdocs/project/architecture.md +3 -0
- package/templates/devdocs/project/conventions.md +3 -0
- package/templates/devdocs/project/custom-instructions.md +3 -0
- package/templates/devdocs/project/glossary.md +3 -0
- package/templates/devdocs/project/stack.md +3 -0
- package/templates/devdocs/templates/commit-convention.md +3 -0
- package/templates/devdocs/templates/issue.md +3 -0
- package/templates/devdocs/templates/pull-request.md +3 -0
- package/templates/devdocs/templates/review-finding.md +3 -0
- package/templates/workflows/forgedock-review.yml +96 -0
package/commands/validate.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
description: Independently verify if a reported issue is actually a problem before making code changes.
|
|
3
3
|
argument-hint: [issue description or #number]
|
|
4
4
|
---
|
|
5
|
+
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
6
|
+
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
5
7
|
|
|
6
8
|
# /validate — Independent Issue Verification
|
|
7
9
|
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
description: Pre-implementation architecture planning — traces ALL affected code paths and produces an ordered implementation plan before any code is written
|
|
3
3
|
argument-hint: [issue number] [--repo GH_REPO] [--gh-flag GH_FLAG] [--files AFFECTED_FILES]
|
|
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/architect — Multi-Path Implementation Planning
|
|
7
9
|
|
|
@@ -11,6 +13,23 @@ argument-hint: [issue number] [--repo GH_REPO] [--gh-flag GH_FLAG] [--files AFFE
|
|
|
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 (A0 through A5), post NO comment, return empty plan to caller immediately. Do not read any files. This is not an error — trivial single-file changes have no cross-path consistency risk. <!-- Added: forge#679 -->
|
|
28
|
+
|
|
29
|
+
**If COMPLEXITY_BAND: STANDARD or COMPLEX** → proceed to Phase A0 below.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
14
33
|
## Mission
|
|
15
34
|
|
|
16
35
|
Eliminate cross-path inconsistency bugs before any code is written. The single biggest class of production bugs is a change applied to one code path but not all sibling paths that share the same logic. This agent traces the FULL request/data flow from every entry point, identifies every file that must change, and produces an ordered implementation plan the builder follows exactly.
|
|
@@ -127,12 +146,103 @@ fi
|
|
|
127
146
|
|
|
128
147
|
### Step 2: Secondary reads — other applicable project files
|
|
129
148
|
|
|
130
|
-
|
|
149
|
+
**Index-first path** (preferred when `index.yaml` exists): Use domain-filtered selective loading instead of O(N) enumerate. Falls back to full enumerate when index is absent. Skip `project/custom-instructions.md` (already read in Step 1). Sort by authority (`required` first).
|
|
131
150
|
|
|
132
151
|
```bash
|
|
133
152
|
PROJECT_CONVENTIONS=""
|
|
153
|
+
INDEX_PATH="${DEVDOCS_PATH}/index.yaml"
|
|
154
|
+
|
|
155
|
+
# --- Read devdocs.index_path override from forge.yaml (optional) ---
|
|
156
|
+
if [ -f "$FORGE_YAML_PATH" ]; then
|
|
157
|
+
INDEX_PATH_OVERRIDE=$(grep -A5 '^devdocs:' "$FORGE_YAML_PATH" \
|
|
158
|
+
| grep '^\s*index_path:' \
|
|
159
|
+
| head -1 \
|
|
160
|
+
| sed 's/.*index_path:\s*//' \
|
|
161
|
+
| tr -d '"'"'"' \
|
|
162
|
+
| tr -d '[:space:]')
|
|
163
|
+
if [ -n "$INDEX_PATH_OVERRIDE" ]; then
|
|
164
|
+
case "$INDEX_PATH_OVERRIDE" in
|
|
165
|
+
/*) INDEX_PATH="$INDEX_PATH_OVERRIDE" ;;
|
|
166
|
+
*) INDEX_PATH="{REPO_PATH}/${INDEX_PATH_OVERRIDE}" ;;
|
|
167
|
+
esac
|
|
168
|
+
fi
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
if [ -n "$DEVDOCS_PATH" ] && [ -f "$INDEX_PATH" ]; then
|
|
172
|
+
# --- Index-first loading path ---
|
|
173
|
+
echo "Devdocs index found at ${INDEX_PATH} — using selective domain loading"
|
|
174
|
+
|
|
175
|
+
# Read issue labels to determine domain(s)
|
|
176
|
+
ISSUE_LABELS=$(gh issue view {NUMBER} -R {GH_REPO} --json labels \
|
|
177
|
+
--jq '[.labels[].name] | join(" ")' 2>/dev/null || echo "")
|
|
178
|
+
echo "Issue labels: ${ISSUE_LABELS:-none}"
|
|
179
|
+
|
|
180
|
+
# Read index content
|
|
181
|
+
INDEX_CONTENT=$(cat "$INDEX_PATH" 2>/dev/null || echo "")
|
|
182
|
+
|
|
183
|
+
# Extract always_load paths (lines with "path:" under "always_load:" block)
|
|
184
|
+
ALWAYS_LOAD_PATHS=$(echo "$INDEX_CONTENT" \
|
|
185
|
+
| awk '/^always_load:/,/^[a-z]/' \
|
|
186
|
+
| grep '^\s*-\s*path:' \
|
|
187
|
+
| sed 's/.*path:\s*//' \
|
|
188
|
+
| tr -d '"'"'"' \
|
|
189
|
+
| tr -d '[:space:]')
|
|
190
|
+
|
|
191
|
+
# Extract domain blocks matching any issue label keyword
|
|
192
|
+
DOMAIN_PATHS=""
|
|
193
|
+
for label in $ISSUE_LABELS; do
|
|
194
|
+
KEYWORD=$(echo "$label" | sed 's/^workflow://; s/^priority://; s/^review-finding$//')
|
|
195
|
+
[ -z "$KEYWORD" ] && continue
|
|
196
|
+
# Sanitize KEYWORD before AWK injection — strip chars that would break awk regex delimiters
|
|
197
|
+
SAFE_KEYWORD=$(printf '%s' "$KEYWORD" | tr -cd 'a-zA-Z0-9_-')
|
|
198
|
+
[ -z "$SAFE_KEYWORD" ] && continue
|
|
199
|
+
|
|
200
|
+
BLOCK_PATHS=$(echo "$INDEX_CONTENT" \
|
|
201
|
+
| awk "/^ ${SAFE_KEYWORD}:/{found=1; next} found && /^ [a-z]/{found=0} found && /^\s*-\s*path:/{print}" \
|
|
202
|
+
| sed 's/.*path:\s*//' \
|
|
203
|
+
| tr -d '"'"'"' \
|
|
204
|
+
| tr -d '[:space:]')
|
|
205
|
+
|
|
206
|
+
if [ -n "$BLOCK_PATHS" ]; then
|
|
207
|
+
echo "Domain '${SAFE_KEYWORD}' matched — adding docs: $(echo "$BLOCK_PATHS" | tr '\n' ' ')"
|
|
208
|
+
DOMAIN_PATHS="${DOMAIN_PATHS}${BLOCK_PATHS}"$'\n'
|
|
209
|
+
fi
|
|
210
|
+
done
|
|
211
|
+
|
|
212
|
+
# Combine always_load + domain paths (deduplicate)
|
|
213
|
+
ALL_PATHS=$(printf "%s\n%s" "$ALWAYS_LOAD_PATHS" "$DOMAIN_PATHS" \
|
|
214
|
+
| grep -v '^$' | sort -u)
|
|
215
|
+
|
|
216
|
+
# When no domain labels matched, load only always_load entries
|
|
217
|
+
if [ -z "$DOMAIN_PATHS" ]; then
|
|
218
|
+
echo "No domain labels matched index — loading always_load entries only"
|
|
219
|
+
ALL_PATHS="$ALWAYS_LOAD_PATHS"
|
|
220
|
+
fi
|
|
221
|
+
|
|
222
|
+
APPLICABLE_FILES=""
|
|
223
|
+
while IFS= read -r rel_path; do
|
|
224
|
+
[ -z "$rel_path" ] && continue
|
|
225
|
+
# Skip custom-instructions — already read in Step 1
|
|
226
|
+
[ "$rel_path" = "project/custom-instructions.md" ] && continue
|
|
227
|
+
ABS_PATH="${DEVDOCS_PATH}/${rel_path}"
|
|
228
|
+
[ -f "$ABS_PATH" ] || { echo "WARN: index references missing file: ${rel_path} — skipping"; continue; }
|
|
229
|
+
|
|
230
|
+
FRONTMATTER=$(awk '/^---/{c++; if(c==1){next} if(c==2){exit}} c==1{print}' "$ABS_PATH" 2>/dev/null)
|
|
231
|
+
AUTHORITY=$(echo "$FRONTMATTER" | grep 'authority:' | head -1 | sed 's/.*authority:\s*//' | tr -d ' ')
|
|
232
|
+
case "$AUTHORITY" in
|
|
233
|
+
required) SORT_KEY="1" ;;
|
|
234
|
+
recommended) SORT_KEY="2" ;;
|
|
235
|
+
*) SORT_KEY="3" ;;
|
|
236
|
+
esac
|
|
237
|
+
APPLICABLE_FILES="${APPLICABLE_FILES}${SORT_KEY}|${ABS_PATH}"$'\n'
|
|
238
|
+
done <<< "$ALL_PATHS"
|
|
239
|
+
|
|
240
|
+
APPLICABLE_FILES=$(printf "%s" "$APPLICABLE_FILES" | sort | cut -d'|' -f2-)
|
|
241
|
+
|
|
242
|
+
elif [ -n "$DEVDOCS_PATH" ]; then
|
|
243
|
+
# --- Fallback: O(N) enumerate (backward compatible — no index.yaml present) ---
|
|
244
|
+
echo "No index.yaml found at ${INDEX_PATH} — falling back to full enumerate (backward compatible)"
|
|
134
245
|
|
|
135
|
-
if [ -n "$DEVDOCS_PATH" ]; then
|
|
136
246
|
APPLICABLE_FILES=""
|
|
137
247
|
|
|
138
248
|
while IFS= read -r -d '' mdfile; do
|
|
@@ -148,32 +258,31 @@ if [ -n "$DEVDOCS_PATH" ]; then
|
|
|
148
258
|
recommended) SORT_KEY="2" ;;
|
|
149
259
|
*) SORT_KEY="3" ;;
|
|
150
260
|
esac
|
|
151
|
-
APPLICABLE_FILES="${APPLICABLE_FILES}${SORT_KEY}|${mdfile}\n
|
|
261
|
+
APPLICABLE_FILES="${APPLICABLE_FILES}${SORT_KEY}|${mdfile}"$'\n'
|
|
152
262
|
fi
|
|
153
263
|
done < <(find "$DEVDOCS_PATH" -name "*.md" -print0 2>/dev/null)
|
|
154
264
|
|
|
155
265
|
APPLICABLE_FILES=$(printf "$APPLICABLE_FILES" | sort | cut -d'|' -f2-)
|
|
266
|
+
fi
|
|
156
267
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
268
|
+
while IFS= read -r mdfile; do
|
|
269
|
+
[ -z "$mdfile" ] && continue
|
|
270
|
+
TOTAL_LINES=$(wc -l < "$mdfile" 2>/dev/null || echo 0)
|
|
271
|
+
if [ "$TOTAL_LINES" -gt 200 ]; then
|
|
272
|
+
FILE_CONTENT=$(head -200 "$mdfile")
|
|
273
|
+
TRUNCATION_NOTE="_[Truncated at 200 lines — ${TOTAL_LINES} total.]_"
|
|
274
|
+
else
|
|
275
|
+
FILE_CONTENT=$(cat "$mdfile")
|
|
276
|
+
TRUNCATION_NOTE=""
|
|
277
|
+
fi
|
|
167
278
|
|
|
168
|
-
|
|
169
|
-
|
|
279
|
+
REL_PATH="${mdfile#${DEVDOCS_PATH}/}"
|
|
280
|
+
PROJECT_CONVENTIONS="${PROJECT_CONVENTIONS}
|
|
170
281
|
|
|
171
282
|
#### \`${REL_PATH}\`
|
|
172
283
|
${FILE_CONTENT}
|
|
173
284
|
${TRUNCATION_NOTE}"
|
|
174
|
-
|
|
175
|
-
fi
|
|
176
|
-
```
|
|
285
|
+
done <<< "$APPLICABLE_FILES"
|
|
177
286
|
|
|
178
287
|
### Step 3: Precedence rules to enforce
|
|
179
288
|
|
|
@@ -411,6 +520,7 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:ARCHITECT -->
|
|
|
411
520
|
## Skip Conditions
|
|
412
521
|
|
|
413
522
|
Skip this entire step (post nothing, return empty plan to caller) if:
|
|
523
|
+
- **COMPLEXITY_BAND: TRIVIAL** — checked via FORGE:FAST_PATH comment at entry (see guard above) <!-- Primary skip path: forge#679 -->
|
|
414
524
|
- Issue creates only **new files** with no callers to find (e.g. a new command file with no existing integration point yet)
|
|
415
525
|
- Issue is a 1-file config or docs edit with no code logic
|
|
416
526
|
- Issue title starts with "docs:" or "chore:"
|
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
|
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
|
-
|
|
|
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 -
|
|
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 -
|
|
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
|
-
|
|
|
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 -
|
|
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" |
|
|
245
|
-
TASK_TYPE=$(echo "$GIST_CONTENT" |
|
|
246
|
-
SEVERITY=$(echo "$GIST_CONTENT" |
|
|
247
|
-
SOURCE_ISSUE=$(echo "$GIST_CONTENT" |
|
|
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 -
|
|
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
|
|
|
@@ -241,9 +243,11 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:BUILDER -->
|
|
|
241
243
|
{Checklist of each criterion from the contract, marked ✅ or ❌}
|
|
242
244
|
|
|
243
245
|
### Testing Checklist
|
|
244
|
-
- [ ] {Test scenario 1}
|
|
245
|
-
- [ ] {Test scenario 2}
|
|
246
|
-
- [ ] {Test scenario 3}
|
|
246
|
+
- [ ] {Test scenario 1} [type:api]
|
|
247
|
+
- [ ] {Test scenario 2} [type:unit]
|
|
248
|
+
- [ ] {Test scenario 3} [type:e2e]
|
|
249
|
+
|
|
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.
|
|
247
251
|
|
|
248
252
|
<!-- FORGE:BUILDER:COMPLETE -->"
|
|
249
253
|
```
|
|
@@ -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.
|
|
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 -
|
|
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
|
|
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
|
-
**
|
|
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
|
|
201
|
+
## Phase B4: Architecture Planning (MANDATORY for STANDARD/COMPLEX — skip for TRIVIAL)
|
|
188
202
|
|
|
189
|
-
**
|
|
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` →
|
|
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
|