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
@@ -0,0 +1,262 @@
1
+ ---
2
+ description: Pipeline-wide situational awareness — groups open issues by workflow state, shows active PRs, flags stale items, and reports milestone progress
3
+ argument-hint: [--repo <owner/repo> | --stale-days <N>]
4
+ ---
5
+
6
+ # /status — Pipeline Status
7
+
8
+ **Input**: $ARGUMENTS (optional: `--repo <owner/repo>`, `--stale-days <N>`)
9
+
10
+ Read-only snapshot of the ForgeDock pipeline. Shows open issues grouped by workflow label, active PRs with age, stale items, and milestone progress.
11
+
12
+ **This command is fully read-only.** It makes no label changes, posts no comments, and creates no issues.
13
+
14
+ **NEVER use plan mode (EnterPlanMode)** — it breaks execution context.
15
+
16
+ ---
17
+
18
+ ## Config Resolution
19
+
20
+ ```bash
21
+ CONFIG_FILE="${FORGE_CONFIG:-forge.yaml}"
22
+ GH_REPO=$(yq '.project.owner + "/" + .project.repo' "$CONFIG_FILE")
23
+ GH_FLAG="-R $GH_REPO"
24
+ STAGING_BRANCH=$(yq '.branches.staging' "$CONFIG_FILE")
25
+
26
+ # Optional: --repo flag overrides the default repo
27
+ if echo "$ARGUMENTS" | grep -q -- "--repo"; then
28
+ GH_REPO=$(echo "$ARGUMENTS" | sed 's/.*--repo[[:space:]]\([^[:space:]]*\).*/\1/')
29
+ GH_FLAG="-R $GH_REPO"
30
+ fi
31
+
32
+ # Optional: --stale-days flag (default: 7)
33
+ STALE_DAYS=7
34
+ if echo "$ARGUMENTS" | grep -q -- "--stale-days"; then
35
+ STALE_DAYS=$(echo "$ARGUMENTS" | sed 's/.*--stale-days[[:space:]]\([^[:space:]]*\).*/\1/')
36
+ fi
37
+
38
+ STALE_THRESHOLD=$(date -d "${STALE_DAYS} days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
39
+ || date -v-${STALE_DAYS}d +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
40
+ || echo "")
41
+
42
+ echo "Repo: $GH_REPO"
43
+ echo "Staging branch: $STAGING_BRANCH"
44
+ echo "Stale threshold: ${STALE_DAYS} days"
45
+ ```
46
+
47
+ ---
48
+
49
+ ## Phase 1: Issues by Workflow State
50
+
51
+ Fetch all open issues with `workflow:*` labels. Group and count by state.
52
+
53
+ ```bash
54
+ echo ""
55
+ echo "=== Pipeline Issues — $(date +%Y-%m-%d) ==="
56
+ echo ""
57
+
58
+ # Workflow states to display (in pipeline order)
59
+ STATES="workflow:investigating workflow:building workflow:in-review workflow:ready-to-build needs-human"
60
+
61
+ for LABEL in $STATES; do
62
+ ISSUES=$(gh issue list $GH_FLAG \
63
+ --state open \
64
+ --label "$LABEL" \
65
+ --limit 50 \
66
+ --json number,title,updatedAt,assignees \
67
+ --jq '.[] | "#\(.number) \(.title[:60]) [updated: \(.updatedAt[:10])]"' \
68
+ 2>/dev/null || echo "")
69
+
70
+ COUNT=$(echo "$ISSUES" | grep -c '#' 2>/dev/null || echo 0)
71
+ [ "$COUNT" = "0" ] && [ -z "$ISSUES" ] && COUNT=0
72
+
73
+ if [ -n "$ISSUES" ] && [ "$COUNT" -gt 0 ]; then
74
+ echo "[$LABEL] ($COUNT open)"
75
+ echo "$ISSUES" | while IFS= read -r line; do
76
+ echo " $line"
77
+ done
78
+ echo ""
79
+ else
80
+ echo "[$LABEL] — none"
81
+ fi
82
+ done
83
+
84
+ # Issues with no workflow label (untracked / not yet entered pipeline)
85
+ UNTRACKED=$(gh issue list $GH_FLAG \
86
+ --state open \
87
+ --limit 20 \
88
+ --json number,title,labels,createdAt \
89
+ --jq '.[] | select(
90
+ (.labels | map(.name) | any(startswith("workflow:"))) | not
91
+ ) | "#\(.number) \(.title[:60]) [created: \(.createdAt[:10])]"' \
92
+ 2>/dev/null || echo "")
93
+
94
+ UNTRACKED_COUNT=$(echo "$UNTRACKED" | grep -c '#' 2>/dev/null || echo 0)
95
+ if [ -n "$UNTRACKED" ] && [ "$UNTRACKED_COUNT" -gt 0 ]; then
96
+ echo "[untracked] ($UNTRACKED_COUNT — not yet in pipeline)"
97
+ echo "$UNTRACKED" | while IFS= read -r line; do
98
+ echo " $line"
99
+ done
100
+ echo ""
101
+ fi
102
+ ```
103
+
104
+ ---
105
+
106
+ ## Phase 2: Active PRs
107
+
108
+ Show open PRs targeting `staging` or any `milestone/*` branch, with age and review status.
109
+
110
+ ```bash
111
+ echo "=== Active PRs ==="
112
+ echo ""
113
+
114
+ gh pr list $GH_FLAG \
115
+ --state open \
116
+ --limit 30 \
117
+ --json number,title,headRefName,baseRefName,createdAt,reviews,isDraft \
118
+ --jq '.[] | {
119
+ number: .number,
120
+ title: .title[:55],
121
+ head: .headRefName,
122
+ base: .baseRefName,
123
+ age_days: (((now - (.createdAt | fromdateiso8601)) / 86400) | floor),
124
+ draft: .isDraft,
125
+ review_state: (
126
+ if (.reviews | length) == 0 then "no review"
127
+ elif (.reviews | map(select(.state == "APPROVED")) | length) > 0 then "APPROVED"
128
+ elif (.reviews | map(select(.state == "CHANGES_REQUESTED")) | length) > 0 then "CHANGES REQUESTED"
129
+ else "COMMENTED"
130
+ end
131
+ )
132
+ } | " PR #\(.number) \(.title) [\(.age_days)d old] [\(.review_state)]\(if .draft then " [DRAFT]" else "" end)\n → \(.head) → \(.base)"' \
133
+ 2>/dev/null || echo " (none)"
134
+
135
+ echo ""
136
+ ```
137
+
138
+ ---
139
+
140
+ ## Phase 3: Stale Items
141
+
142
+ Flag issues and PRs with no activity in the last `$STALE_DAYS` days.
143
+
144
+ ```bash
145
+ echo "=== Stale Items (no activity > ${STALE_DAYS} days) ==="
146
+ echo ""
147
+
148
+ if [ -z "$STALE_THRESHOLD" ]; then
149
+ echo " (skipped — could not compute stale threshold on this platform)"
150
+ else
151
+ # Stale open issues with workflow labels
152
+ STALE_ISSUES=$(gh issue list $GH_FLAG \
153
+ --state open \
154
+ --limit 100 \
155
+ --json number,title,updatedAt,labels \
156
+ --jq --arg threshold "$STALE_THRESHOLD" \
157
+ '[.[] |
158
+ select(
159
+ (.updatedAt < $threshold) and
160
+ (.labels | map(.name) | any(startswith("workflow:")))
161
+ )
162
+ ] | .[] | " ISSUE #\(.number) \(.title[:55]) [last updated: \(.updatedAt[:10])] [\(.labels | map(select(.name | startswith("workflow:"))) | .[0].name // "no-workflow")]"' \
163
+ 2>/dev/null || echo "")
164
+
165
+ STALE_PRS=$(gh pr list $GH_FLAG \
166
+ --state open \
167
+ --limit 50 \
168
+ --json number,title,updatedAt \
169
+ --jq --arg threshold "$STALE_THRESHOLD" \
170
+ '[.[] | select(.updatedAt < $threshold)] |
171
+ .[] | " PR #\(.number) \(.title[:55]) [last updated: \(.updatedAt[:10])]"' \
172
+ 2>/dev/null || echo "")
173
+
174
+ if [ -z "$STALE_ISSUES" ] && [ -z "$STALE_PRS" ]; then
175
+ echo " None — pipeline is moving."
176
+ else
177
+ [ -n "$STALE_ISSUES" ] && echo "$STALE_ISSUES"
178
+ [ -n "$STALE_PRS" ] && echo "$STALE_PRS"
179
+ fi
180
+ fi
181
+
182
+ echo ""
183
+ ```
184
+
185
+ ---
186
+
187
+ ## Phase 4: Milestone Progress
188
+
189
+ Show open milestones with open/closed issue counts and percentage complete.
190
+
191
+ ```bash
192
+ echo "=== Milestone Progress ==="
193
+ echo ""
194
+
195
+ MILESTONES=$(gh api repos/$GH_REPO/milestones \
196
+ --jq '.[] | select(.state == "open") | {
197
+ title: .title,
198
+ number: .number,
199
+ open: .open_issues,
200
+ closed: .closed_issues,
201
+ total: (.open_issues + .closed_issues),
202
+ due: (.due_on // "no due date")[:10],
203
+ pct: (if (.open_issues + .closed_issues) > 0 then
204
+ ((.closed_issues / (.open_issues + .closed_issues)) * 100) | floor
205
+ else 0 end)
206
+ } | "\(.title) [\(.pct)% done — \(.closed)/\(.total) issues] [due: \(.due)]"' \
207
+ 2>/dev/null || echo "")
208
+
209
+ if [ -z "$MILESTONES" ]; then
210
+ echo " No active milestones."
211
+ else
212
+ echo "$MILESTONES" | while IFS= read -r line; do
213
+ echo " $line"
214
+ done
215
+ fi
216
+
217
+ echo ""
218
+ ```
219
+
220
+ ---
221
+
222
+ ## Phase 5: Bottleneck Summary
223
+
224
+ Highlight states where issues are piling up or stuck.
225
+
226
+ ```bash
227
+ echo "=== Bottleneck Summary ==="
228
+ echo ""
229
+
230
+ INVESTIGATING=$(gh issue list $GH_FLAG --state open --label "workflow:investigating" --limit 100 --json number --jq '. | length' 2>/dev/null || echo 0)
231
+ BUILDING=$(gh issue list $GH_FLAG --state open --label "workflow:building" --limit 100 --json number --jq '. | length' 2>/dev/null || echo 0)
232
+ IN_REVIEW=$(gh issue list $GH_FLAG --state open --label "workflow:in-review" --limit 100 --json number --jq '. | length' 2>/dev/null || echo 0)
233
+ BLOCKED=$(gh issue list $GH_FLAG --state open --label "needs-human" --limit 100 --json number --jq '. | length' 2>/dev/null || echo 0)
234
+ READY=$(gh issue list $GH_FLAG --state open --label "workflow:ready-to-build" --limit 100 --json number --jq '. | length' 2>/dev/null || echo 0)
235
+
236
+ TOTAL_INFLIGHT=$((INVESTIGATING + BUILDING + IN_REVIEW + READY))
237
+
238
+ echo " investigating : $INVESTIGATING"
239
+ echo " ready-to-build : $READY"
240
+ echo " building : $BUILDING"
241
+ echo " in-review : $IN_REVIEW"
242
+ echo " needs-human : $BLOCKED $([ "$BLOCKED" -gt 0 ] && echo '← ACTION REQUIRED' || true)"
243
+ echo ""
244
+ echo " Total in-flight: $TOTAL_INFLIGHT"
245
+
246
+ if [ "$BLOCKED" -gt 0 ]; then
247
+ echo ""
248
+ echo " ⚠ $BLOCKED issue(s) need human attention:"
249
+ gh issue list $GH_FLAG --state open --label "needs-human" --limit 20 \
250
+ --json number,title \
251
+ --jq '.[] | " #\(.number) \(.title[:70])"' \
252
+ 2>/dev/null || true
253
+ fi
254
+
255
+ if [ "$IN_REVIEW" -gt 3 ]; then
256
+ echo ""
257
+ echo " ⚠ Review queue depth is $IN_REVIEW — consider running /review-pr or /orchestrate fast-lane"
258
+ fi
259
+
260
+ echo ""
261
+ echo "=== Status complete — $(date +%Y-%m-%d\ %H:%M:%S) ==="
262
+ ```
@@ -2,6 +2,8 @@
2
2
  description: Detect API changes, sync satellite repos, and publish releases
3
3
  argument-hint: [check | auto | status | publish | PR-number]
4
4
  ---
5
+ <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
6
+ <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
5
7
 
6
8
  # /sync-ecosystem — Cross-Project Sync & Publish
7
9
 
@@ -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
- Read all other `*.md` files under `DEVDOCS_PATH` where `applies_to` contains `work-on`. Skip `project/custom-instructions.md` (already read). Sort by authority (`required` first).
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
- while IFS= read -r mdfile; do
158
- [ -z "$mdfile" ] && continue
159
- TOTAL_LINES=$(wc -l < "$mdfile" 2>/dev/null || echo 0)
160
- if [ "$TOTAL_LINES" -gt 200 ]; then
161
- FILE_CONTENT=$(head -200 "$mdfile")
162
- TRUNCATION_NOTE="_[Truncated at 200 lines — ${TOTAL_LINES} total.]_"
163
- else
164
- FILE_CONTENT=$(cat "$mdfile")
165
- TRUNCATION_NOTE=""
166
- fi
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
- REL_PATH="${mdfile#${DEVDOCS_PATH}/}"
169
- PROJECT_CONVENTIONS="${PROJECT_CONVENTIONS}
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
- done <<< "$APPLICABLE_FILES"
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:"