@garygentry/feature-forge 0.2.14 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/README.md +6 -3
  2. package/adapters/GENERATION-REPORT.md +20 -0
  3. package/adapters/claude/.feature-forge-bundle.json +1 -1
  4. package/adapters/claude/references/forge-config-schema.json +2 -2
  5. package/adapters/claude/scripts/forge-root.sh +47 -3
  6. package/adapters/claude/scripts/forge-session.py +30 -8
  7. package/adapters/claude/skills/forge-4-backlog/references/forge-config-schema.json +2 -2
  8. package/adapters/claude/skills/forge-5-loop/references/forge-config-schema.json +2 -2
  9. package/adapters/claude/skills/forge-guide/references/forge-config-schema.json +2 -2
  10. package/adapters/codex/.feature-forge-bundle.json +1 -1
  11. package/adapters/codex/references/forge-config-schema.json +2 -2
  12. package/adapters/codex/scripts/forge-root.sh +47 -3
  13. package/adapters/codex/scripts/forge-session.py +30 -8
  14. package/adapters/codex/skills/forge-4-backlog/references/forge-config-schema.json +2 -2
  15. package/adapters/codex/skills/forge-5-loop/references/forge-config-schema.json +2 -2
  16. package/adapters/codex/skills/forge-guide/references/forge-config-schema.json +2 -2
  17. package/adapters/copilot/.feature-forge-bundle.json +1 -1
  18. package/adapters/copilot/references/forge-config-schema.json +2 -2
  19. package/adapters/copilot/scripts/forge-root.sh +47 -3
  20. package/adapters/copilot/scripts/forge-session.py +30 -8
  21. package/adapters/copilot/skills/forge-4-backlog/references/forge-config-schema.json +2 -2
  22. package/adapters/copilot/skills/forge-5-loop/references/forge-config-schema.json +2 -2
  23. package/adapters/copilot/skills/forge-guide/references/forge-config-schema.json +2 -2
  24. package/adapters/cursor/.feature-forge-bundle.json +1 -1
  25. package/adapters/cursor/references/forge-config-schema.json +2 -2
  26. package/adapters/cursor/scripts/forge-root.sh +47 -3
  27. package/adapters/cursor/scripts/forge-session.py +30 -8
  28. package/adapters/cursor/skills/forge-4-backlog/references/forge-config-schema.json +2 -2
  29. package/adapters/cursor/skills/forge-5-loop/references/forge-config-schema.json +2 -2
  30. package/adapters/cursor/skills/forge-guide/references/forge-config-schema.json +2 -2
  31. package/adapters/gemini/.feature-forge-bundle.json +1 -1
  32. package/adapters/gemini/gemini-extension.json +1 -1
  33. package/adapters/gemini/references/forge-config-schema.json +2 -2
  34. package/adapters/gemini/scripts/forge-root.sh +47 -3
  35. package/adapters/gemini/scripts/forge-session.py +30 -8
  36. package/adapters/gemini/skills/forge-4-backlog/references/forge-config-schema.json +2 -2
  37. package/adapters/gemini/skills/forge-5-loop/references/forge-config-schema.json +2 -2
  38. package/adapters/gemini/skills/forge-guide/references/forge-config-schema.json +2 -2
  39. package/adapters/pi/.feature-forge-bundle.json +6 -0
  40. package/adapters/pi/agents/forge-researcher.md +139 -0
  41. package/adapters/pi/agents/forge-spec-writer.md +116 -0
  42. package/adapters/pi/agents/forge-verifier.md +126 -0
  43. package/adapters/pi/extensions/ask-user-question/LICENSE +21 -0
  44. package/adapters/pi/extensions/ask-user-question/README.md +91 -0
  45. package/adapters/pi/extensions/ask-user-question/ask-user-question.ts +298 -0
  46. package/adapters/pi/extensions/ask-user-question/config.ts +78 -0
  47. package/adapters/pi/extensions/ask-user-question/events.ts +57 -0
  48. package/adapters/pi/extensions/ask-user-question/index.ts +61 -0
  49. package/adapters/pi/extensions/ask-user-question/locales/de.json +27 -0
  50. package/adapters/pi/extensions/ask-user-question/locales/en.json +27 -0
  51. package/adapters/pi/extensions/ask-user-question/locales/es.json +27 -0
  52. package/adapters/pi/extensions/ask-user-question/locales/fr.json +27 -0
  53. package/adapters/pi/extensions/ask-user-question/locales/pt-BR.json +27 -0
  54. package/adapters/pi/extensions/ask-user-question/locales/pt.json +27 -0
  55. package/adapters/pi/extensions/ask-user-question/locales/ru.json +27 -0
  56. package/adapters/pi/extensions/ask-user-question/locales/uk.json +27 -0
  57. package/adapters/pi/extensions/ask-user-question/locales/zh.json +29 -0
  58. package/adapters/pi/extensions/ask-user-question/reconcile.ts +49 -0
  59. package/adapters/pi/extensions/ask-user-question/rpc-fallback.ts +168 -0
  60. package/adapters/pi/extensions/ask-user-question/state/build-questionnaire.ts +302 -0
  61. package/adapters/pi/extensions/ask-user-question/state/i18n-bridge.ts +53 -0
  62. package/adapters/pi/extensions/ask-user-question/state/key-router.ts +277 -0
  63. package/adapters/pi/extensions/ask-user-question/state/questionnaire-session.ts +234 -0
  64. package/adapters/pi/extensions/ask-user-question/state/row-intent.ts +145 -0
  65. package/adapters/pi/extensions/ask-user-question/state/selectors/contract.ts +26 -0
  66. package/adapters/pi/extensions/ask-user-question/state/selectors/derivations.ts +42 -0
  67. package/adapters/pi/extensions/ask-user-question/state/selectors/focus.ts +19 -0
  68. package/adapters/pi/extensions/ask-user-question/state/selectors/projections.ts +101 -0
  69. package/adapters/pi/extensions/ask-user-question/state/state-reducer.ts +292 -0
  70. package/adapters/pi/extensions/ask-user-question/state/state.ts +55 -0
  71. package/adapters/pi/extensions/ask-user-question/tool/format-answer.ts +31 -0
  72. package/adapters/pi/extensions/ask-user-question/tool/response-envelope.ts +49 -0
  73. package/adapters/pi/extensions/ask-user-question/tool/types.ts +147 -0
  74. package/adapters/pi/extensions/ask-user-question/tool/validate-questionnaire.ts +58 -0
  75. package/adapters/pi/extensions/ask-user-question/vendor-config-shim.ts +65 -0
  76. package/adapters/pi/extensions/ask-user-question/view/component-binding.ts +47 -0
  77. package/adapters/pi/extensions/ask-user-question/view/components/inline-input.ts +98 -0
  78. package/adapters/pi/extensions/ask-user-question/view/components/multi-select-view.ts +193 -0
  79. package/adapters/pi/extensions/ask-user-question/view/components/option-list-view.ts +70 -0
  80. package/adapters/pi/extensions/ask-user-question/view/components/preview/markdown-content-cache.ts +79 -0
  81. package/adapters/pi/extensions/ask-user-question/view/components/preview/preview-block-renderer.ts +111 -0
  82. package/adapters/pi/extensions/ask-user-question/view/components/preview/preview-box-renderer.ts +88 -0
  83. package/adapters/pi/extensions/ask-user-question/view/components/preview/preview-layout-decider.ts +202 -0
  84. package/adapters/pi/extensions/ask-user-question/view/components/preview/preview-pane.ts +228 -0
  85. package/adapters/pi/extensions/ask-user-question/view/components/submit-picker.ts +67 -0
  86. package/adapters/pi/extensions/ask-user-question/view/components/tab-bar.ts +59 -0
  87. package/adapters/pi/extensions/ask-user-question/view/components/wrapping-select.ts +293 -0
  88. package/adapters/pi/extensions/ask-user-question/view/dialog-builder.ts +224 -0
  89. package/adapters/pi/extensions/ask-user-question/view/props-adapter.ts +125 -0
  90. package/adapters/pi/extensions/ask-user-question/view/stateful-view.ts +26 -0
  91. package/adapters/pi/extensions/ask-user-question/view/tab-components.ts +18 -0
  92. package/adapters/pi/extensions/ask-user-question/view/tab-content-strategy.ts +252 -0
  93. package/adapters/pi/package.json +26 -0
  94. package/adapters/pi/references/epic-manifest-schema.json +125 -0
  95. package/adapters/pi/references/forge-config-schema.json +236 -0
  96. package/adapters/pi/references/pipeline-state-schema.json +191 -0
  97. package/adapters/pi/references/portable-root.md +71 -0
  98. package/adapters/pi/references/process-overview.md +143 -0
  99. package/adapters/pi/references/ralph-loop-contract.md +221 -0
  100. package/adapters/pi/references/shared-conventions.md +295 -0
  101. package/adapters/pi/references/skill-frontmatter.schema.json +17 -0
  102. package/adapters/pi/references/stack-resolution.md +54 -0
  103. package/adapters/pi/references/stacks/_generic.md +111 -0
  104. package/adapters/pi/references/stacks/go.md +157 -0
  105. package/adapters/pi/references/stacks/python.md +184 -0
  106. package/adapters/pi/references/stacks/rust.md +170 -0
  107. package/adapters/pi/references/stacks/typescript.md +134 -0
  108. package/adapters/pi/references/stage-exit-protocol.md +258 -0
  109. package/adapters/pi/references/templates/specs-hygiene/AGENTS.md +32 -0
  110. package/adapters/pi/references/templates/specs-hygiene/CLAUDE.md +31 -0
  111. package/adapters/pi/references/vendor-construct-inventory.md +50 -0
  112. package/adapters/pi/scripts/epic-manifest.py +1694 -0
  113. package/adapters/pi/scripts/forge-bootstrap.py +1070 -0
  114. package/adapters/pi/scripts/forge-init.sh +58 -0
  115. package/adapters/pi/scripts/forge-root.sh +179 -0
  116. package/adapters/pi/scripts/forge-session.py +1888 -0
  117. package/adapters/pi/scripts/validate-traceability.py +150 -0
  118. package/adapters/pi/skills/forge/SKILL.md +243 -0
  119. package/adapters/pi/skills/forge/references/pipeline-state-schema.json +191 -0
  120. package/adapters/pi/skills/forge/references/process-overview.md +143 -0
  121. package/adapters/pi/skills/forge/references/shared-conventions.md +295 -0
  122. package/adapters/pi/skills/forge/references/stage-exit-protocol.md +258 -0
  123. package/adapters/pi/skills/forge-0-epic/SKILL.md +308 -0
  124. package/adapters/pi/skills/forge-0-epic/references/edit-mode.md +266 -0
  125. package/adapters/pi/skills/forge-0-epic/references/epic-manifest-subcommands.md +75 -0
  126. package/adapters/pi/skills/forge-0-epic/references/pipeline-state-schema.json +191 -0
  127. package/adapters/pi/skills/forge-0-epic/references/portable-root.md +71 -0
  128. package/adapters/pi/skills/forge-0-epic/references/shared-conventions.md +295 -0
  129. package/adapters/pi/skills/forge-0-epic/references/stage-exit-protocol.md +258 -0
  130. package/adapters/pi/skills/forge-1-prd/SKILL.md +164 -0
  131. package/adapters/pi/skills/forge-1-prd/references/pipeline-state-schema.json +191 -0
  132. package/adapters/pi/skills/forge-1-prd/references/prd-template.md +106 -0
  133. package/adapters/pi/skills/forge-1-prd/references/shared-conventions.md +295 -0
  134. package/adapters/pi/skills/forge-1-prd/references/stage-exit-protocol.md +258 -0
  135. package/adapters/pi/skills/forge-2-tech/SKILL.md +225 -0
  136. package/adapters/pi/skills/forge-2-tech/references/pipeline-state-schema.json +191 -0
  137. package/adapters/pi/skills/forge-2-tech/references/shared-conventions.md +295 -0
  138. package/adapters/pi/skills/forge-2-tech/references/stack-discovery-checklist.md +95 -0
  139. package/adapters/pi/skills/forge-2-tech/references/stack-resolution.md +54 -0
  140. package/adapters/pi/skills/forge-2-tech/references/stacks/_generic.md +111 -0
  141. package/adapters/pi/skills/forge-2-tech/references/stacks/go.md +157 -0
  142. package/adapters/pi/skills/forge-2-tech/references/stacks/python.md +184 -0
  143. package/adapters/pi/skills/forge-2-tech/references/stacks/rust.md +170 -0
  144. package/adapters/pi/skills/forge-2-tech/references/stacks/typescript.md +134 -0
  145. package/adapters/pi/skills/forge-2-tech/references/stage-exit-protocol.md +258 -0
  146. package/adapters/pi/skills/forge-3-specs/SKILL.md +178 -0
  147. package/adapters/pi/skills/forge-3-specs/references/pipeline-state-schema.json +191 -0
  148. package/adapters/pi/skills/forge-3-specs/references/shared-conventions.md +295 -0
  149. package/adapters/pi/skills/forge-3-specs/references/spec-archetypes.md +106 -0
  150. package/adapters/pi/skills/forge-3-specs/references/spec-examples.md +71 -0
  151. package/adapters/pi/skills/forge-3-specs/references/stacks/_generic.md +111 -0
  152. package/adapters/pi/skills/forge-3-specs/references/stacks/go.md +157 -0
  153. package/adapters/pi/skills/forge-3-specs/references/stacks/python.md +184 -0
  154. package/adapters/pi/skills/forge-3-specs/references/stacks/rust.md +170 -0
  155. package/adapters/pi/skills/forge-3-specs/references/stacks/typescript.md +134 -0
  156. package/adapters/pi/skills/forge-3-specs/references/stage-exit-protocol.md +258 -0
  157. package/adapters/pi/skills/forge-4-backlog/SKILL.md +175 -0
  158. package/adapters/pi/skills/forge-4-backlog/references/forge-config-schema.json +236 -0
  159. package/adapters/pi/skills/forge-4-backlog/references/pipeline-state-schema.json +191 -0
  160. package/adapters/pi/skills/forge-4-backlog/references/shared-conventions.md +295 -0
  161. package/adapters/pi/skills/forge-4-backlog/references/stage-exit-protocol.md +258 -0
  162. package/adapters/pi/skills/forge-5-loop/SKILL.md +314 -0
  163. package/adapters/pi/skills/forge-5-loop/references/forge-config-schema.json +236 -0
  164. package/adapters/pi/skills/forge-5-loop/references/ralph-loop-contract.md +221 -0
  165. package/adapters/pi/skills/forge-5-loop/references/result-reporting.md +85 -0
  166. package/adapters/pi/skills/forge-5-loop/references/runner-contract.md +341 -0
  167. package/adapters/pi/skills/forge-5-loop/references/shared-conventions.md +295 -0
  168. package/adapters/pi/skills/forge-5-loop/references/stage-exit-protocol.md +258 -0
  169. package/adapters/pi/skills/forge-6-docs/SKILL.md +202 -0
  170. package/adapters/pi/skills/forge-6-docs/references/doc-conventions.md +126 -0
  171. package/adapters/pi/skills/forge-6-docs/references/pipeline-state-schema.json +191 -0
  172. package/adapters/pi/skills/forge-6-docs/references/shared-conventions.md +295 -0
  173. package/adapters/pi/skills/forge-bootstrap/SKILL.md +250 -0
  174. package/adapters/pi/skills/forge-bootstrap/references/templates/ci/github-actions.yml +12 -0
  175. package/adapters/pi/skills/forge-bootstrap/references/templates/generic/run.sh +3 -0
  176. package/adapters/pi/skills/forge-bootstrap/references/templates/generic/test.sh +13 -0
  177. package/adapters/pi/skills/forge-bootstrap/references/templates/go/go.mod +3 -0
  178. package/adapters/pi/skills/forge-bootstrap/references/templates/go/main.go +12 -0
  179. package/adapters/pi/skills/forge-bootstrap/references/templates/go/main_test.go +11 -0
  180. package/adapters/pi/skills/forge-bootstrap/references/templates/hygiene/AGENTS.md +35 -0
  181. package/adapters/pi/skills/forge-bootstrap/references/templates/hygiene/CLAUDE.md +36 -0
  182. package/adapters/pi/skills/forge-bootstrap/references/templates/hygiene/README.md +11 -0
  183. package/adapters/pi/skills/forge-bootstrap/references/templates/licenses/Apache-2.0/LICENSE +198 -0
  184. package/adapters/pi/skills/forge-bootstrap/references/templates/licenses/MIT/LICENSE +21 -0
  185. package/adapters/pi/skills/forge-bootstrap/references/templates/python/pyproject.toml +24 -0
  186. package/adapters/pi/skills/forge-bootstrap/references/templates/python/src/{{PKG}}/__init__.py +5 -0
  187. package/adapters/pi/skills/forge-bootstrap/references/templates/python/src/{{PKG}}/main.py +13 -0
  188. package/adapters/pi/skills/forge-bootstrap/references/templates/python/tests/test_smoke.py +8 -0
  189. package/adapters/pi/skills/forge-bootstrap/references/templates/rust/Cargo.toml +15 -0
  190. package/adapters/pi/skills/forge-bootstrap/references/templates/rust/src/lib.rs +7 -0
  191. package/adapters/pi/skills/forge-bootstrap/references/templates/rust/src/main.rs +5 -0
  192. package/adapters/pi/skills/forge-bootstrap/references/templates/rust/tests/smoke.rs +6 -0
  193. package/adapters/pi/skills/forge-bootstrap/references/templates/typescript/package.json +15 -0
  194. package/adapters/pi/skills/forge-bootstrap/references/templates/typescript/src/index.ts +4 -0
  195. package/adapters/pi/skills/forge-bootstrap/references/templates/typescript/test/smoke.test.ts +6 -0
  196. package/adapters/pi/skills/forge-bootstrap/references/templates/typescript/tsconfig.json +14 -0
  197. package/adapters/pi/skills/forge-fix/SKILL.md +98 -0
  198. package/adapters/pi/skills/forge-fix/references/shared-conventions.md +295 -0
  199. package/adapters/pi/skills/forge-fix/references/stage-exit-protocol.md +258 -0
  200. package/adapters/pi/skills/forge-guide/SKILL.md +192 -0
  201. package/adapters/pi/skills/forge-guide/references/forge-config-schema.json +236 -0
  202. package/adapters/pi/skills/forge-guide/references/process-overview.md +143 -0
  203. package/adapters/pi/skills/forge-guide/references/ralph-loop-contract.md +221 -0
  204. package/adapters/pi/skills/forge-guide/references/shared-conventions.md +295 -0
  205. package/adapters/pi/skills/forge-guide/references/stack-resolution.md +54 -0
  206. package/adapters/pi/skills/forge-guide/references/stacks/_generic.md +111 -0
  207. package/adapters/pi/skills/forge-guide/references/stacks/go.md +157 -0
  208. package/adapters/pi/skills/forge-guide/references/stacks/python.md +184 -0
  209. package/adapters/pi/skills/forge-guide/references/stacks/rust.md +170 -0
  210. package/adapters/pi/skills/forge-guide/references/stacks/typescript.md +134 -0
  211. package/adapters/pi/skills/forge-init/SKILL.md +72 -0
  212. package/adapters/pi/skills/forge-verify/SKILL.md +273 -0
  213. package/adapters/pi/skills/forge-verify/references/pipeline-state-schema.json +191 -0
  214. package/adapters/pi/skills/forge-verify/references/shared-conventions.md +295 -0
  215. package/adapters/pi/skills/forge-verify/references/verification-checklists.md +477 -0
  216. package/dist/agent-targets.d.ts +1 -1
  217. package/dist/agent-targets.js +23 -3
  218. package/dist/detect.d.ts +1 -1
  219. package/dist/detect.js +2 -1
  220. package/dist/manifest.d.ts +1 -1
  221. package/dist/manifest.js +2 -2
  222. package/dist/placements.js +5 -1
  223. package/dist/rauf.d.ts +4 -4
  224. package/dist/rauf.js +3 -3
  225. package/dist/types.d.ts +31 -6
  226. package/dist/types.js +6 -3
  227. package/package.json +14 -3
@@ -0,0 +1,314 @@
1
+ ---
2
+ # GENERATED — DO NOT EDIT. Source: skills/forge-5-loop/SKILL.md. Regenerate: python3 scripts/build-adapters.py
3
+ name: forge-5-loop
4
+ description: Execute the autonomous coding loop (rauf by default) against a forge feature's backlog. Use when user runs /skill:forge-5-loop or /skill:forge-5-rauf-loop, or asks to run rauf / run the loop / implement a forge feature after the backlog is created and verified. Do NOT trigger for general rauf usage, standalone loop runs, or implementation tasks outside the forge pipeline.
5
+ ---
6
+
7
+ # forge-5-loop — Autonomous Loop Executor
8
+
9
+ Execute the autonomous coding loop against a forge feature's backlog. The loop
10
+ spawns a fresh agent session per backlog item, implementing each task with full
11
+ verification.
12
+
13
+ The loop **runner** is configured, not hardcoded. feature-forge talks to it
14
+ through the `loopRunner` block in `forge.config.json`; rauf is the default and
15
+ reference implementation (see `references/ralph-loop-contract.md`). Every command
16
+ below is rendered from `loopRunner` with token substitution — there are no
17
+ hardcoded `rauf …` commands in this skill, and even the human log filename is
18
+ tokenized as `{loopRunner.logFile}`.
19
+
20
+ ## Resolve the loop runner
21
+
22
+ Read `forge.config.json`. Build the effective `loopRunner` by taking its
23
+ `loopRunner` block (if present) and filling any missing field from the defaults
24
+ in `references/forge-config-schema.json`. **If `forge.config.json` has no
25
+ `loopRunner` block at all, state plainly: "No loopRunner configured — defaulting
26
+ to the rauf loop runner."** then proceed with the full default block.
27
+
28
+ Token substitution applies to every `*Command` string. Substitute:
29
+
30
+ - `{bin}` → `loopRunner.bin` (default `rauf`)
31
+ - `{backlogDir}` → the resolved backlog directory (Step 1d / 2b), relative to project root
32
+ - `{specsDir}` → `specsDir` from config
33
+ - `{iterations}` → the computed iteration count (Step 2a)
34
+
35
+ Whenever this skill says "run the **run command**" / "**status command**" /
36
+ etc., it means the corresponding substituted `loopRunner.*Command`.
37
+
38
+ **Turn structure reminder:** Output analysis/context as text, then route ALL questions through `AskUserQuestion`. Never embed questions in text output — the user will not be prompted and the session will stall.
39
+
40
+ ## Step 1: Validate Prerequisites
41
+
42
+ Read and follow `references/shared-conventions.md` for feature name validation, configuration reading, and force mode handling.
43
+
44
+ ### 1a. Pipeline State Check
45
+
46
+ **Resolve the feature directory first.** Invoke the **Feature Directory Resolution** block in `references/shared-conventions.md` to turn the bare feature name into `{resolvedFeatureDir}` (exit 0 → stdout is the absolute dir; exit ≥ 1 → STOP and surface the finding verbatim). Read state from `{resolvedFeatureDir}/` everywhere this skill previously wrote `{specsDir}/{feature}/` (the 1e backlog path and the Step 3a / Step 5 state writes). Standalone features resolve to their flat path exactly as today.
47
+
48
+ Read `{resolvedFeatureDir}/.pipeline-state.json`. If not in force mode, `stages.forge-4-backlog` must be `complete`. If not, STOP and tell the user: "Backlog hasn't been created yet. Run `/skill:forge-4-backlog {feature}` first."
49
+
50
+ ### 1b. Verification Check
51
+
52
+ Check if `stages.forge-verify-backlog` exists and has status `passed` or `findings-applied`. If not, use `AskUserQuestion` to warn with the cost of skipping:
53
+
54
+ "Backlog hasn't been verified yet. Recommended: run `/skill:forge-verify {feature}` first — the loop implements items autonomously and commits as it goes, so a bad item (wrong scope, missing dependency, untestable acceptance criteria) is far cheaper to catch now than after several commits build on it. Continue anyway?" Offer **Verify first (recommended)** · **Continue without verifying**.
55
+
56
+ ### 1b-epic. Epic Dependency Gate
57
+
58
+ Read the resolved feature's `.pipeline-state.json`. **If it has no `epic` key, skip this sub-step entirely** (standalone feature — REQ-COMPAT-01; standalone runs are unchanged). Otherwise:
59
+
60
+ 1. Run `render-status "{epic}" --specs-dir "{specsDir}" --json` via the helper:
61
+
62
+ ```bash
63
+ R="$(bash -c 'for d in "${FEATURE_FORGE_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
64
+ [ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
65
+ python3 "$R/scripts/epic-manifest.py" \
66
+ render-status "{epic}" --specs-dir "{specsDir}" --json
67
+ ```
68
+
69
+ 2. Find this feature's entry; read its `unmetDeps` (the direct `dependsOn` not yet complete-for-orchestration per `00-core-definitions.md §7`).
70
+ 3. **If `unmetDeps` is empty**, proceed to 1c with no prompt.
71
+ 4. **If `unmetDeps` is non-empty**, use `AskUserQuestion` (do NOT inline the question as prose) to warn that the feature depends on the unmet dependencies, which are not yet complete, and that running the loop now means implementing against contracts that may still change:
72
+
73
+ > "{feature} depends on {unmetDeps joined}, which are not yet complete. Running the loop now means implementing against contracts that may still change. Proceed anyway, or stop and finish the dependencies first?"
74
+
75
+ Require an **explicit "Proceed anyway"** choice to continue (REQ-ORCH-04). "Stop" aborts before any runner setup. `--force` (shared-conventions Force Mode) also bypasses this gate with the standard force warning.
76
+ 5. If `render-status` fails, **STOP** — do not silently run a loop whose dependency state is unverifiable (REQ-ROBUST-02). Surface per the exit-1/exit-2 split in the **Feature Directory Resolution** block of `references/shared-conventions.md` (exit 1 → parse `{findings[]}` from stdout; exit 2 → surface the plain `Error:` stderr line verbatim — no findings JSON to parse).
77
+
78
+ This gate runs **before** the runner version/setup gates (1c/1d) so a blocked feature stops early, before any runner side-effects.
79
+
80
+ ### 1c. Runner Version Gate
81
+
82
+ Enforce `loopRunner.minRunnerVersion` **before** doing anything else with the runner. This is what turns "the runner is missing or too old" into a clear, actionable stop instead of a cryptic mid-run failure.
83
+
84
+ 1. Run the **version command** (`loopRunner.versionCommand`, default `rauf version --json`) via Bash.
85
+ 2. Parse `{ "version": "<semver>" }` from stdout. Do NOT use plain `rauf version` (its human output is `rauf v0.6.0` with a `v` prefix) — always the `--json` form.
86
+ 3. **Semver-compare** (NOT string-compare) the reported version against `loopRunner.minRunnerVersion` (default `0.6.0`), numerically by major, then minor, then patch.
87
+
88
+ **Any of the following is a HARD GATE FAILURE — do NOT proceed to run the loop.** STOP, show `loopRunner.installHint`, and include the raw command output for diagnosis:
89
+
90
+ - The version command is not found or exits non-zero (the binary isn't installed).
91
+ - Its stdout is not valid JSON, has no `version` field, or `version` is not a valid semver string.
92
+ - The reported version is **< `minRunnerVersion`**.
93
+
94
+ For the version-too-old case, phrase it concretely, e.g.: "Your rauf is {reported}, but feature-forge needs ≥ {minRunnerVersion} — 0.6.0 is the floor that ships the agent-selection surface (`--agent` / `rauf agents`) the loop relies on. {installHint}". When the gate fails because the output couldn't be parsed, say so and show what the command printed before the `installHint`.
95
+
96
+ > `installHint` points at the runner **CLI** install/upgrade — distinct from
97
+ > `setupHint` (1d), which installs the runner's per-project artifacts.
98
+
99
+ ### 1d. Runner Setup Check (precondition file)
100
+
101
+ Check that `loopRunner.preconditionFile` (default `.rauf.json`) exists in the project root. If not:
102
+
103
+ - **If `loopRunner.name == "rauf"` and a legacy `.ralph.json` (or `.ralph/` directory) exists**, this is an un-migrated Ralph project. STOP: "This project is still on the legacy **Ralph** layout. Run `rauf migrate .` first (the loop runner only understands `.rauf/` and `RAUF_*` signals), then re-run `/skill:forge-5-loop {feature}`."
104
+ - **Otherwise**, STOP and show `loopRunner.setupHint` (default: "Run `rauf install .` …"), e.g. "The loop runner isn't set up in this project ({preconditionFile} is missing). {setupHint}"
105
+
106
+ ### 1e. Backlog File Check
107
+
108
+ Resolve the backlog file path (matching forge-4-backlog's composition rule, item 015 / §6.2):
109
+ - If `backlogDir` is set in `forge.config.json`: use `{backlogDir}/{feature}/backlog.json` (the per-feature subpath, so each epic member's backlog stays independent — the `{feature}` segment prevents collisions across a multi-feature epic)
110
+ - Otherwise: use `{resolvedFeatureDir}/backlog.json`
111
+
112
+ Verify the file exists on disk. If not, STOP and tell the user: "No backlog.json found at {path}. Run `/skill:forge-4-backlog {feature}` to generate it."
113
+
114
+ ### 1f. Branch Pre-flight (if using git)
115
+
116
+ The runner commits each item onto the current branch. Skip if not a git repo or `branchPerFeature` is false. Otherwise run the **Branch Reconciliation** block in `references/shared-conventions.md` (it runs `reconcile-branch` and, on `warn-drift` — you are on the default branch — strongly recommends creating `{branchPrefix}{feature}` via `AskUserQuestion` before the loop commits; on `adopt-current` it updates the recorded branch to the current one, never pushing you back to a stale/imposed branch). Never hard-stop.
117
+
118
+ ## Step 2: Construct the Loop Command
119
+
120
+ ### 2a. Analyze Backlog
121
+
122
+ Run the **list command** (`loopRunner.listCommand`, default `rauf backlog list . --backlog {backlogDir} --json`) and count items by status: `pending`, `in_progress`, `done`, `blocked`.
123
+
124
+ Calculate the iteration count: `ceil((pending + in_progress) * loopIterationMultiplier)` where `loopIterationMultiplier` comes from `forge.config.json` (default: 1.5). This headroom allows retries without exhausting iterations.
125
+
126
+ If there are no pending or in_progress items, STOP and tell the user: "All backlog items are already done or blocked. Nothing to run."
127
+
128
+ If there are `blocked` items, note them — the user may want `--retry-blocked`.
129
+
130
+ ### 2b. Resolve Backlog Directory
131
+
132
+ `{backlogDir}` is a **directory path** (not a file path), relative to the project root.
133
+
134
+ - If `backlogDir` is set in config: use the per-feature subpath `{backlogDir}/{feature}` (matching the 1e composition rule and forge-4-backlog §6.2).
135
+ - Otherwise: use `{resolvedFeatureDir}` (the directory containing `backlog.json`).
136
+
137
+ **Example:** If `specsDir` is `./specs` and feature is `auth`, `{backlogDir}` is `specs/auth`.
138
+
139
+ ### 2c. Build Command
140
+
141
+ Render the **run command** (`loopRunner.runCommand`) with token substitution, e.g. the rauf default becomes:
142
+
143
+ ```
144
+ rauf loop run . --backlog specs/auth --iterations 15
145
+ ```
146
+
147
+ ### 2d. Confirm with User
148
+
149
+ Use `AskUserQuestion` to present the rendered run command and options. The following block is the content for `AskUserQuestion` — do NOT output it as text:
150
+
151
+ ```
152
+ Ready to run the loop for {feature}:
153
+
154
+ {rendered runCommand} # + " --review" when the recommended Run-mode option (below) is picked
155
+
156
+ Backlog summary:
157
+ - Pending: {pending}
158
+ - In progress: {in_progress}
159
+ - Done: {done}
160
+ - Blocked: {blocked}
161
+ - Iterations: {iterationCount} ({activeItems} items x {loopIterationMultiplier} multiplier)
162
+
163
+ For the model-selection precedence (item.model > --model/options > project default >
164
+ provider default) and the full optional-flags catalog, read references/runner-contract.md.
165
+ ```
166
+
167
+ **Run mode (gated on `loopRunner.name == "rauf"`).** When the runner is rauf, add a **"Run mode"** question to this same `AskUserQuestion` surface with these options **in this exact order** (do NOT improvise — deterministic ordering is the point): **(1) "Run with review pass (recommended)"** — append `--review`, and this is the default; **(2) "Run without review"** — the bare rendered command; **(3, only when 2a counted blocked items) "Review + retry blocked"** — append `--review --retry-blocked`. `AskUserQuestion`'s built-in "Other" covers ad-hoc flags (`--model`/`--timeout`); add no separate open-ended option. The command line shown above renders `--review` (the recommended default). **When the runner is not rauf**, add NO Run-mode question — present the bare rendered command and let the user adjust via "Other" (byte-identical to today). Verbatim option labels: `## Run mode (Step 2d, rauf)` in `references/runner-contract.md`.
168
+
169
+ For the full loop-runner contract — event-stream vs. log-fallback launch, the live-supervision/monitor rules, and the model-selection precedence — read `references/runner-contract.md`. Whichever Run-mode option (or "Other") the user picks, append its flags to the rendered run command before Step 3.
170
+
171
+ #### Agent selection (gated on `loopRunner.agentArgument`)
172
+
173
+ **Capability gate.** Everything below applies **only when** the effective `loopRunner.agentArgument` is present and non-empty. **When it is absent or empty, Step 2d is exactly the confirmation above — no probe, no agent question, no availability listing, no `Agent:` line — byte-identical to today** (REQ-PLUG-02, REQ-COMPAT-01). The full algorithm, precedence, and verbatim message shapes are in `## Agent selection` of `references/runner-contract.md`; read it. When the gate is on, augment Step 2d in order:
174
+
175
+ - **(a) Probe once.** Before confirming, run `loopRunner.agentsProbeCommand` (default `{bin} agents --json`) **exactly once** (no retries, no second probe); it exits 0 with `{ agents: [...] }`. Parse `agents[]`; build the advertised set `{ row.id }` — this one parsed array drives (b)–(d).
176
+ - **(b) Agent question.** Add an **"agent"** question to the same `AskUserQuestion` surface: **one option per advertised row** labelled `"{displayName} ({id}) — available/not found"`, **plus an explicit `"default (claude-cli)"` choice mapping to `run_selection = None`**. Resolve the pick (run > project, empty/whitespace unset, an explicit runner-default pick collapses to the default path) into `{resolved.agent, resolved.source}`. Precedence: `item.provider > --agent > project defaultAgent > runner default` (forge never reads a backlog item's provider).
177
+ - **(c) Availability listing.** From the **same** parsed `agents[]` (no second probe), list `id` / `displayName` / available (`yes`/`no`, `detail` on unavailable rows).
178
+ - **(d) Verdict** — only for a **non-default** resolved agent (default path `None`/`claude-cli` → no probe, byte-identical to today). Classify by **membership** then `available` (never by exit code): **UNKNOWN** (`∉` set) → **hard-reject BEFORE any loop side-effect**, error lists the **sorted** valid ids, **NO proceed-anyway**; **UNAVAILABLE** (member, `available False`) → warn with `detail`, `AskUserQuestion` offering **proceed-anyway OR choose-another** (re-presents the same `agents[]`), never silent; **AVAILABLE** → proceed, the validated id fills `{agent}`; **probe failure** (non-zero exit / unparseable / missing or empty `agents[]` / row lacking `id`) → surface it, offer **choose-another OR abort**, **never launch the non-default agent unvalidated** and never silently fall back to the default.
179
+ - **(d-model) Claude-only model-alias guard.** Runs **only** when the resolved agent is **non-default** (not the default / `claude-cli` path). Read the backlog.json (Step 1e path); collect items whose `model` is a **Claude-specific alias** (tier `opus`/`sonnet`/`haiku` or a `claude-*` id). **If none, skip silently.** Otherwise warn before launch via `AskUserQuestion` (NOT prose): `item.model` outranks `--agent`, so the alias is forwarded verbatim to `{agent}`, which will likely reject it (e.g. codex 400 *"The 'sonnet' model is not supported…"*) — every spawn exits 1 and rauf circuit-breaks (*"3 consecutive infra failures — halting"*) with no hint of the cause. Offer: **(1) Strip `model` for this run (recommended)** — rewrite backlog.json removing the `model` key from each affected item (persistent edit; re-run forge-4-backlog to restore), then proceed; **(2) Proceed as-is** — only safe if `{agent}` understands the pinned ids. forge touches only `model`, never `provider`. Full rationale: `references/runner-contract.md`.
180
+ - **(e) Optional-flags line.** Augment the confirmation block's closing flags/precedence pointer to list `--agent <id>` first plus the agent precedence pointer (`item.provider > --agent > project defaultAgent > runner default`) alongside the model precedence.
181
+ - **(f) Resolved-agent line.** Add to the confirmation block: `Agent: {resolved.agent or claude-cli} (source: {sourceLabel})` — `sourceLabel`: `RUN` → `"per-run selection"`, `PROJECT` → `"project default (loopRunner.defaultAgent)"`, `DEFAULT` → `"runner default — claude-cli"`.
182
+
183
+ ## Step 3: Execute the Loop
184
+
185
+ ### 3a. Update Pipeline State
186
+
187
+ Before launching, update `{resolvedFeatureDir}/.pipeline-state.json`:
188
+ - Set `stages.forge-5-loop.status` to `in-progress`
189
+ - Set `stages.forge-5-loop.startedAt` to current ISO timestamp
190
+ - Set `currentStage` to `forge-5-loop`
191
+ - Update `updatedAt`
192
+
193
+ Then commit this state write before launching (mandatory). The runner refuses to run with uncommitted changes (*"…pass --force"*), and this marker is itself one — so an otherwise-clean repo fails its first launch unless committed. Commit it via the shared-conventions **Git Commit Protocol** (epic members: stage `{specsDir}/{epic}/`): `{commitPrefix}({feature}): forge-5-loop in-progress` — a launch precondition, required regardless of `gitCommitAfterStage`. Unrelated leftover changes still trip the refusal; surface it, never auto-pass `--force`. See `references/runner-contract.md`.
194
+
195
+ ### 3b. Launch Background Process
196
+
197
+ Launch the loop **backgrounded** (the host's background-execution mechanism) so it survives session end and does not block the session. For a runner that **persists its own structured event file** (the default — rauf writes `{stateDir}/events.ndjson` natively and rotates it per run), launch the **plain `runCommand`** with **no stdout redirect** and supervise the runner's **native** `events.ndjson` directly; do **not** redirect `--ndjson` into `{stateDir}` (it is redundant and collides with the runner's own writer — see `references/runner-contract.md`). Only a stdout-only runner (no native event file) uses `eventStreamCommand`, redirected to a file **outside** `{stateDir}`. The background task's exit notification is the single authoritative terminal signal (Step 4). Loop runs can take significant time (minutes to hours depending on backlog size). For the exact launch commands (incl. the `mkdir -p` state-dir guard and the root→`IS_SANDBOX` sandbox guard) and the self-persisting vs. stdout-only detail, read `references/runner-contract.md`.
198
+
199
+ ### 3c. Inform User
200
+
201
+ Tell the user the run has started and that **this session is now actively
202
+ supervising it** — they don't need to babysit a terminal — and surface the rendered
203
+ `loopRunner` monitoring commands (`statusCommand` / `followCommand` / `logCommand` /
204
+ `listCommand`) and the state-file locations under
205
+ `{backlogDir}/{loopRunner.stateDir}/` so they can watch directly if they like. The
206
+ verbatim "Loop started…" inform-user output template is in
207
+ `references/runner-contract.md`.
208
+
209
+ ### 3d. Arm a Monitor on the event stream, and react to events
210
+
211
+ Arm the **host's monitoring mechanism** on the structured event stream (the NDJSON file, or the
212
+ human log as fallback) so events flow back into this session as they happen. Use
213
+ **`persistent: true`** — runs can exceed the host's monitoring mechanism's maximum `timeout_ms` (1 hour),
214
+ and a bounded timeout would silently stop watching a still-running loop. The filter
215
+ MUST match every terminal and exception state, not just the happy path (silence is
216
+ not success). Monitor the **structured** surface, never raw `RAUF_*` tokens.
217
+
218
+ Each Monitor event arrives as a message; react per type — surface `needs_human` /
219
+ `loop_error` immediately with a `PushNotification`, coalesce `item_completed` into
220
+ milestones, and treat `llm_stuck_warning` as a hang warning. A `needs_human` /
221
+ `blocked` signal does **not** pause the loop — the runner sets the item aside and
222
+ keeps going.
223
+
224
+ For the exact Monitor commands (NDJSON `jq` filter and the log-fallback `grep`
225
+ prefixes), the coverage-complete filter event list, and the full per-event reaction
226
+ rules, read `references/runner-contract.md`.
227
+
228
+ ### 3f. Reach completion
229
+
230
+ Step 4 is reached when the backgrounded process exits (its completion notification is
231
+ authoritative); the `loop_completed` / `loop_error` / `loop_cancelled` event is the live
232
+ heads-up that it's imminent. Stop the Monitor (it ends on its own when `tail` sees the
233
+ process-ended log, or via `TaskStop`) and proceed to Step 4. Do NOT foreground-sleep
234
+ or poll — the harness drives both the Monitor events and the completion notification.
235
+
236
+ ## Step 4: Check Results
237
+
238
+ When the background process completes (its exit notification):
239
+
240
+ ### 4a. Get Final Backlog State
241
+
242
+ Run the **status-json command** (`loopRunner.statusJsonCommand`) and read
243
+ `backlogSummary` for the authoritative counts — it separates the three non-done
244
+ outcomes: genuine `blocked`, `needsHuman`, and runner-`deferred` ("false blocks").
245
+ Fall back to the **list command** (`loopRunner.listCommand`) if `statusJsonCommand`
246
+ is not configured. You will already have most of this from the live tally in 3e. If the run used a review flag (e.g. rauf's `--review`), also read any `review_completed` event (event stream, or `{loopRunner.stateDir}/events.ndjson`) for its `itemsCreated`/`summary` to surface in 4b — see `references/result-reporting.md`.
247
+
248
+ ### 4b. Report Results
249
+
250
+ Present a summary to the user. Pick **every** branch that applies (a run can be both
251
+ blocked and needs-human) and render its report. The five verbatim result-report
252
+ output templates — **all-done**, **needs-human**, **blocked**, **deferred**, and
253
+ **pending** (iteration limit reached) — are in `references/result-reporting.md`.
254
+
255
+ ## Step 5: Update Pipeline State
256
+
257
+ Update `{resolvedFeatureDir}/.pipeline-state.json`:
258
+
259
+ 1. Set `stages.forge-5-loop`: `status` = `"complete"` if all backlog items are `done`, else `"in-progress"`; `completedAt` = current ISO timestamp (only if complete); `basedOnVersions` = `{"forge-4-backlog": <current version from pipeline state>}`; `artifacts` = `["{backlogDir}/{loopRunner.stateDir}/state.json"]`.
260
+ 2. If all items complete: set `currentStage` to `"forge-6-docs"`
261
+ 3. Update `updatedAt`
262
+
263
+ **No git commit is needed** — the loop runner commits implementation code atomically per completed item during the run. (Step 6's commit, epic members only, is of pipeline state / manifest — a distinct artifact.)
264
+
265
+ ## Step 5b: Offer Impl-Verify (standalone path)
266
+
267
+ **Gate:** run only if (a) the feature's `.pipeline-state.json` has **no** `epic` key **and** (b) Step 5 set `stages.forge-5-loop.status` to `complete`. Otherwise **skip** — partial runs end as today, and epic members get the equivalent offer in Step 6.1 (do **not** prompt twice). This standalone counterpart to Step 6.1 nudges verification interactively rather than via the easily-missed "Next steps" text. Use `AskUserQuestion` (NOT inline prose) to offer: *"{feature}'s loop is complete. Recommended: run `/skill:forge-verify {feature} impl` to audit the implementation before generating docs. Run it now, or skip to forge-6-docs?"* On **run**, hand off to `/skill:forge-verify {feature} impl`. On **skip**, record `stages.forge-verify-impl.status` as `"skipped"` (mirrors `forge-4-backlog`'s skip handling) and point the user at `/skill:forge-6-docs {feature}` — the forge-6-docs backstop re-surfaces the skip.
268
+
269
+ ## Step 6: Epic Handoff
270
+
271
+ **Gate:** only run this step if (a) the resolved feature's `.pipeline-state.json` has an `epic` key **and** (b) Step 5 set `stages.forge-5-loop.status` to `complete` (all backlog items done). If either is false, **skip** — standalone completed features are handled by Step 5b, and partial runs end as today (REQ-COMPAT-01).
272
+
273
+ 1. **Offer impl-verify first (recommended, skippable).** Per the completion rule (`00-core-definitions.md §7`), a feature whose `forge-verify-impl.status == findings-reported` does **not** unblock dependents. Use `AskUserQuestion` (NOT inline prose) to offer: *"{feature}'s loop is done. Recommended: run `/skill:forge-verify {feature} impl` before unblocking dependents. Run it now, or skip and continue the handoff?"* The user may skip (then completion is judged on the §7 rule with impl-verify absent).
274
+ 2. **Recompute and announce.** Run `render-status "{epic}" --specs-dir "{specsDir}" --json`. Announce the feature's completion and the epic rollup (e.g. "2/4 features complete") — derived live from disk, never re-computed in prose.
275
+ 3. **Identify the next actionable feature(s).** Read `render-status`'s `actionable` set (every dependency now complete, not itself complete) and `nextCommand`. **None actionable:** say so — if `rollup.total > 0` **AND** `rollup.complete == rollup.total`, suggest `/skill:forge-6-docs {feature}` and note the epic-level documentation offer (§10) (the `rollup.total > 0` guard prevents an **empty epic** `0 == 0` from reading as complete); otherwise list what is still blocked and on which dependencies, then end (do not prompt to start a feature that cannot start). **One or more actionable:** use `AskUserQuestion` presenting **each actionable feature** as an option (plus "stop here"). Execution is **serial** — the user picks exactly one (REQ-ORCH-03); do **not** autonomously chain into the next pipeline.
276
+ 4. **Begin the chosen feature.** **PRD absent** (no `PRD.md`, or `stages.forge-1-prd` not complete): offer to author it now — "Start `/skill:forge-1-prd {chosen}`?" (REQ-ORCH-02); on yes, hand off to forge-1-prd (which injects epic context per §5.1). **PRD present:** point the user at the chosen feature's `nextCommand` from render-status.
277
+ 5. **Commit (REQ-OBS-01).** When `gitCommitAfterStage` is true, commit the Step 5 completion write (and any manifest `updatedAt` bump) via the shared-conventions **Git Commit Protocol**, staging the epic subtree so the member state change commits atomically: `git add {specsDir}/{epic}/` then `{commitPrefix}({feature}): complete loop`. If `gitCommitAfterStage` is false, skip the commit.
278
+ 6. **Close the handoff with the Stage Exit Protocol.** Finishing feature `{feature}` → starting the picked feature `{chosen}`'s PRD is a **cold** stage boundary (single-sourced in `references/stage-exit-protocol.md`). Feature `{feature}`'s impl-verify was already offered in Step 6.1, so step 1's gate self-suppresses when it ran or was skipped — the block collapses to `/new` → next-command. Present it only when the chosen feature's PRD is absent (a PRD-present pick just runs its `nextCommand`):
279
+
280
+ **This stage is done — walk the user through the Stage Exit Protocol** before moving on. The order is fixed, and step 2 is something only the user can do:
281
+
282
+ 1. **Verify feature {feature}'s loop first — if it isn't already verified.** If verify already ran in this session — via the in-stage auto-verify on the authoring stages, or the interactive impl-verify offered above on the loop — or is already fresh on record, or the stage was explicitly skipped, say so and go straight to step 2. Only when `autoVerify` is off for this stage **and** verify is **missing or stale** do you present the **Standard Verify Gate**: verify **now, before clearing**, using `AskUserQuestion` with exactly these three options — but only when the host has a question mechanism **and** the clean-room path is available (the host's subagent mechanism plus a dispatchable `forge-verifier` subagent):
283
+ - **Verify feature {feature}'s loop now** *(recommended)* — dispatch the clean-room `forge-verifier` subagent from this session in require-clean mode; the digest returns here so any fix decision keeps its context. One-time — it does **not** change config.
284
+ - **Verify now + enable auto-verify going forward** — verify now **and** patch `"autoVerify": true` into `forge.config.json` in place (preserve formatting and every other key) so future stages verify automatically, no prompt. This complements the `forge-init` opt-in. **Do not auto-commit this config change** — treat it like `notes`: a user-facing edit the user commits on their own cadence, never folded into a stage's artifact commit.
285
+ - **Skip for now** — go straight to `/new` and the next command without verifying. Record this stage's verify status as `"skipped"` in pipeline state (mirroring the existing skip handling) **only** on an explicit skip — a skip does not go stale.
286
+
287
+ **Host / clean-room fallback (not a user-selectable option):** if the question mechanism, the host's subagent mechanism, or the `forge-verifier` subagent is unavailable, do **not** run clean-room — degrade to printing `/skill:forge-verify {feature} impl` for the user to run inline/manually (mirroring `autoInvokeNextStage`), and offer the auto-verify enable as plain text only if a config write is possible.
288
+ 2. **Then `/new`.** Recommended **unconditionally** at this boundary for a clean start — independent of how full the context window is. Every artifact is on disk, so the work survives the clear. **I can't `/new` for you — you have to run it yourself.**
289
+ 3. **Then run the next command** in the fresh session — or re-run `/skill:forge` to let the navigator resume from disk:
290
+
291
+ ```
292
+ /skill:forge-1-prd {chosen}
293
+ ```
294
+
295
+ ## Gotchas
296
+
297
+ - **Plugin-root discovery (1b-epic helper) covers installed paths, not workspace-dev checkouts.** The `forge-root.sh` search in 1b-epic probes `~/.claude/skills/feature-forge`, `~/.claude/plugins/cache/*/feature-forge/*` (marketplace-cache installs), `~/.claude/plugins/*/feature-forge`, and `./.agents/skills/feature-forge` — the locations of an **installed** plugin. A feature-forge **source checkout** (e.g. `~/workspace/feature-forge`) is not on that list, so the helper exits "cannot locate plugin root." That is expected in a dev environment, not a bug; run the epic-manifest script from the checkout directly (`python3 <checkout>/scripts/epic-manifest.py …`). The bootstrap prelude wraps its candidate loop in `bash -c` so the `~/.claude/plugins/*/feature-forge` glob is zsh-safe: an empty expansion no longer aborts the loop under zsh's `nomatch`.
298
+ - `{backlogDir}` is a **directory path**, not a file path. Pass `specs/auth`, not `specs/auth/backlog.json`.
299
+ - rauf resolves `RAUF.md` with fallback (`{backlogDir}/.rauf/RAUF.md` first, then the project's `.rauf/RAUF.md`) — found as long as the runner is installed in the project. State files (state.json, {loopRunner.logFile}, etc.) are created at `{backlogDir}/{loopRunner.stateDir}/`, within the feature's spec directory (expected) and isolated per backlog dir, so concurrent features don't collide.
300
+ - If the session disconnects during a long-running loop, the runner process continues independently — the user can check results later with the status / list commands. If a previous run left a stale lock, the user may need to pass `--force` to clear it (rauf reports this error clearly).
301
+ - Never run the run command in the foreground (without the host's background-execution mechanism) — it blocks and will hit the Bash tool timeout for any non-trivial backlog. "Don't block the foreground" is NOT "stay silent": supervise via the host's monitoring mechanism (3d), never `sleep`/poll in the foreground. The host's monitoring mechanism must use `persistent: true` (not a bounded `timeout_ms`), watch the **structured** surface (`events.ndjson`), and never filter on raw `RAUF_*` tokens — they appear in agent prose and false-match. A `needs_human`/`blocked`/`review` signal does **not** pause the loop — the runner sets the item aside and keeps going; surface it live but don't tell the user the loop is waiting. See `references/runner-contract.md` for the full monitoring rules.
302
+ - The version gate (1c) uses the `--json` form on purpose; never parse `rauf version`'s human output.
303
+ - **Implementation artifacts must not cite specs.** The loop should **read** the specs and `backlog.json` freely — they are the source of truth for what to build, and the backlog rightly references specs for provenance. But the artifacts the loop **writes into the target repo** (source code, generated `SKILL.md`/agent files, configs, code comments) must be **self-contained**: they must NOT reference feature-forge spec files (no `See specs/{feature}/NN-*.md`, no "source spec" provenance notes in shipped output). Specs are pre-implementation inputs that may be archived or deleted once the feature ships; the implementation must stand on its own. This applies only to shipped implementation output — never to the backlog or spec documents, which should keep citing specs.
304
+
305
+ ---
306
+
307
+ ## Host execution notes (Pi)
308
+
309
+ This Pi bundle preserves Claude's `AskUserQuestion` references because it ships a Pi compatibility extension registering an `AskUserQuestion` tool. On Pi:
310
+
311
+ - **User input:** use `AskUserQuestion` for genuine user decisions. It supports multiple questions, option descriptions, recommended ordering, multi-select, previews, and free-form Other/custom answers.
312
+ - **Skill dispatch:** Pi uses `/skill:<name>` commands. If you cannot invoke a skill directly, print the exact `/skill:<name> ...` command for the user to run.
313
+ - **Subagents:** this bundle declares its custom agents (`forge-researcher`, `forge-spec-writer`, `forge-verifier`) as package agents. If a `subagent` tool is registered, dispatch one with `{ agent: "forge-verifier", task: "..." }`, or fan several out concurrently with `{ tasks: [{ agent: "forge-spec-writer", task: "..." }, ...] }`. If no `subagent` tool is available, run that step inline yourself.
314
+ - **Background / monitoring:** run long-lived commands in the foreground and report progress as it arrives.
@@ -0,0 +1,236 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "Feature Forge Configuration",
4
+ "description": "Project-level configuration for the feature-forge plugin. Place as forge.config.json in your project root.",
5
+ "type": "object",
6
+ "properties": {
7
+ "specsDir": {
8
+ "type": "string",
9
+ "default": "./specs",
10
+ "description": "Root directory for feature spec documents. Each feature gets a subdirectory."
11
+ },
12
+ "docsDir": {
13
+ "type": "string",
14
+ "default": "./docs/architecture",
15
+ "description": "Root directory for generated architecture documentation."
16
+ },
17
+ "backlogDir": {
18
+ "type": ["string", "null"],
19
+ "default": null,
20
+ "description": "Optional override for the backlog root. If set, the backlog is written to {backlogDir}/{feature}/backlog.json — the {feature} subdirectory is always composed in so multi-feature epics never collide. Default behavior (null): backlog.json is written to {specsDir}/{feature}/backlog.json with the feature specs."
21
+ },
22
+ "gitCommitAfterStage": {
23
+ "type": "boolean",
24
+ "default": true,
25
+ "description": "Automatically commit after each pipeline stage completes."
26
+ },
27
+ "commitPrefix": {
28
+ "type": "string",
29
+ "default": "forge",
30
+ "description": "Prefix for conventional commit messages, e.g., forge(auth): complete PRD"
31
+ },
32
+ "branchPerFeature": {
33
+ "type": "boolean",
34
+ "default": true,
35
+ "description": "Offer to create an isolated git branch when a feature/epic starts on the default branch (main/master). Gated only on the project using git — independent of gitCommitAfterStage. When false, forge never prompts for a branch and works on whatever branch is checked out. See the Branch Setup block in references/shared-conventions.md."
36
+ },
37
+ "branchPrefix": {
38
+ "type": "string",
39
+ "default": "forge/",
40
+ "description": "Prefix for the branch name suggested by Branch Setup, e.g. 'forge/' yields 'forge/{feature}' or 'forge/{epic}'. Ignored when branchPerFeature is false."
41
+ },
42
+ "stack": {
43
+ "type": ["string", "null"],
44
+ "description": "Detected or configured project stack identifier. Selects guidance from references/stacks/{stack}.md. Set during forge-2-tech or manually (null until then). Examples: 'typescript', 'python', 'go', 'rust'."
45
+ },
46
+ "typeCheckCommand": {
47
+ "type": ["string", "null"],
48
+ "description": "Command to verify type correctness or lint. Null until set. Examples: 'bun run typecheck', 'mypy .', 'go vet ./...'. Used in acceptance criteria and verification."
49
+ },
50
+ "testCommand": {
51
+ "type": ["string", "null"],
52
+ "description": "Command to run tests. Null until set. Examples: 'bun test', 'pytest', 'go test ./...'. Used in acceptance criteria and verification."
53
+ },
54
+ "smokeCommand": {
55
+ "type": ["string", "null"],
56
+ "default": null,
57
+ "description": "Optional end-to-end smoke command that boots the wired application entrypoint and drives one happy-path request, exiting 0 on success. DISTINCT from testCommand (unit tests, which may self-bootstrap) and loopRunner.runCommand (the loop launcher). Consumed by impl-verify's runnability check (CHECK-I21): when set it is executed (pass iff exit 0); when null the check degrades to an advisory not-applicable finding, never a hard fail (mirrors how a null typeCheckCommand is treated). Examples: 'npm run smoke', './scripts/smoke.sh', 'curl -fsS localhost:3000/health'."
58
+ },
59
+ "loopIterationMultiplier": {
60
+ "type": "number",
61
+ "default": 1.5,
62
+ "minimum": 1,
63
+ "description": "Multiplier applied to pending backlog item count to calculate loop iterations. Higher values allow more retries. Default: 1.5 (e.g., 10 items = 15 iterations)."
64
+ },
65
+ "autoInvokeNextStage": {
66
+ "type": "boolean",
67
+ "default": true,
68
+ "description": "When true (default), the /skill:forge navigator auto-invokes the next pipeline stage via the Skill tool after the user confirms it, instead of only printing the command to copy. Set false to keep the old copy-paste behavior (the navigator suggests the command but never launches it). Ignored on non-Claude hosts, which always fall back to printing the command."
69
+ },
70
+ "autoVerify": {
71
+ "type": "boolean",
72
+ "default": false,
73
+ "description": "When true, the /skill:forge navigator automatically runs forge-verify after a stage completes, with no prompt. forge-verify runs in a fresh forge-verifier subagent (clean-room), so it never needs a context clear and costs the current session only a compact findings digest. Default false preserves today's manual-gate behavior. Ignored on non-Claude hosts, which always fall back to printing the verify command. Per-stage overrides in autoVerifyStages take precedence."
74
+ },
75
+ "autoVerifyStages": {
76
+ "type": "object",
77
+ "default": {},
78
+ "description": "Per-stage overrides for autoVerify. Maps a production stage id to a boolean; the effective value for a stage is autoVerifyStages[stage] if present, else autoVerify. Keys are constrained to the five verify-capable stages so a typo (e.g. 'forge-1-prod') is a schema error, not a silent no-op. forge-6-docs has no verify step and is not a valid key.",
79
+ "propertyNames": {
80
+ "enum": ["forge-1-prd", "forge-2-tech", "forge-3-specs", "forge-4-backlog", "forge-5-loop"]
81
+ },
82
+ "additionalProperties": {
83
+ "type": "boolean"
84
+ }
85
+ },
86
+ "autoFix": {
87
+ "type": "boolean",
88
+ "default": false,
89
+ "description": "When true, the navigator chains forge-fix automatically after an auto-verify that finds issues. Honored ONLY when auto-verify is effectively on for the stage, and ONLY when preconditions hold (findings doc has zero unresolved decision points, working tree is clean, and a mandatory re-verify passes) — otherwise it falls back to surfacing the findings digest and prompting. Default false keeps fixing human-gated."
90
+ },
91
+ "contextWindowTokens": {
92
+ "type": ["integer", "null"],
93
+ "default": null,
94
+ "description": "Context window size (tokens) used by the navigator's context-usage check to compute how full the current session is. Null (default) lets the helper infer from the session model and fall back to 200000 — and if observed usage already exceeds 200000 it auto-bumps the assumed window to 1000000 (proof a 1M-beta window is active). Set this explicitly to your model's window (e.g. 1000000 for a 1M-context model) for accurate percentages below 200000 too, since 1M cannot be detected from the transcript until usage crosses 200000."
95
+ },
96
+ "contextWarnThreshold": {
97
+ "type": "number",
98
+ "default": 0.7,
99
+ "minimum": 0,
100
+ "maximum": 1,
101
+ "description": "Fraction of the context window (0-1) past which the navigator recommends starting the next stage in a clean session rather than continuing. Default: 0.7."
102
+ },
103
+ "workspaces": {
104
+ "type": "array",
105
+ "description": "Monorepo members. Absent for single-package projects.",
106
+ "items": {
107
+ "type": "object",
108
+ "required": ["name", "path", "stack"],
109
+ "additionalProperties": false,
110
+ "properties": {
111
+ "name": {"type": "string"},
112
+ "path": {"type": "string", "description": "Repo-relative member dir"},
113
+ "stack": {"type": "string"},
114
+ "typeCheckCommand": {"type": ["string", "null"]},
115
+ "testCommand": {"type": ["string", "null"]}
116
+ }
117
+ }
118
+ },
119
+ "loopRunner": {
120
+ "type": "object",
121
+ "description": "The autonomous loop runner feature-forge drives. Defaults to rauf when absent (forge-5 states 'defaulting to rauf loop runner'). Every command is a template — {bin}, {backlogDir}, {specsDir}, {iterations} are substituted at call time — so an alternative ralph-style runner conforming to rauf's SPEC-BACKLOG-TOOL-CONTRACT.md can be swapped in without editing any skill. See references/ralph-loop-contract.md.",
122
+ "properties": {
123
+ "name": {
124
+ "type": "string",
125
+ "default": "rauf",
126
+ "description": "Display name of the loop runner."
127
+ },
128
+ "bin": {
129
+ "type": "string",
130
+ "default": "rauf",
131
+ "description": "The runner executable. Assumed on PATH; may be an absolute path. Substituted as {bin} in every command."
132
+ },
133
+ "runCommand": {
134
+ "type": "string",
135
+ "default": "{bin} loop run . --backlog {backlogDir} --iterations {iterations}",
136
+ "description": "Run the loop. Launched in the background by forge-5. Human-formatted output; used as the fallback launch command when eventStreamCommand is absent."
137
+ },
138
+ "eventStreamCommand": {
139
+ "type": "string",
140
+ "default": "{bin} loop run . --backlog {backlogDir} --iterations {iterations} --ndjson",
141
+ "description": "Stdout NDJSON launch command for a runner that does NOT persist its own event file — same as runCommand but emits one machine-readable JSON event per stdout line: item_completed / item_blocked / needs_human / signal_parsed / loop_completed / loop_error / loop_cancelled / llm_stuck_warning, each with {type, timestamp, projectPath} plus payload (a circuit-breaker halt surfaces as loop_error). NOTE: rauf (the default runner) ALREADY persists {stateDir}/events.ndjson natively and rotates it per run, so forge-5 launches the plain runCommand and monitors that native file — it does NOT use this field, and must NOT redirect --ndjson into {stateDir} (redundant, and it collides with the runner's own writer / archive rotation). This field is only for a stdout-only runner with no native event file; forge-5 then redirects its stdout to a file OUTSIDE {stateDir} and monitors that. Omit it entirely for a runner that self-persists or cannot emit NDJSON."
142
+ },
143
+ "validateCommand": {
144
+ "type": "string",
145
+ "default": "{bin} backlog validate . --backlog {backlogDir} --specs-dir {specsDir} --json",
146
+ "description": "Validate a backlog. MUST exit 0=valid, 1=findings, 2=usage/IO, and emit { valid, findings[] } with --json. `specReferences` are project-root-relative (resolved against the project root); `--specs-dir` only gates the existence check, so passing the specs root ({specsDir}) is sufficient."
147
+ },
148
+ "statusCommand": {
149
+ "type": "string",
150
+ "default": "{bin} status . --backlog {backlogDir}",
151
+ "description": "One-shot loop status, human-formatted. Shown to the user as a monitoring hint."
152
+ },
153
+ "statusJsonCommand": {
154
+ "type": "string",
155
+ "default": "{bin} status . --backlog {backlogDir} --json",
156
+ "description": "Machine-readable derived status used by forge-5 for milestone tallies and the final summary. Emits { loopState, iteration, maxIterations, currentItem, lastSignal, backlogSummary{pending,inProgress,blocked,needsHuman,deferred,done,total}, lock{...} }. Distinguishes the three non-done outcomes (genuine blocked vs needsHuman vs runner-deferred 'false blocks')."
157
+ },
158
+ "listCommand": {
159
+ "type": "string",
160
+ "default": "{bin} backlog list . --backlog {backlogDir} --json",
161
+ "description": "List backlog items as JSON."
162
+ },
163
+ "followCommand": {
164
+ "type": "string",
165
+ "default": "{bin} follow . --backlog {backlogDir}",
166
+ "description": "Stream live loop events, HUMAN-formatted (pretty-printed / log tail) — for a person watching in another terminal, NOT a machine-readable surface. forge-5 supervises via eventStreamCommand (NDJSON) instead."
167
+ },
168
+ "logCommand": {
169
+ "type": "string",
170
+ "default": "{bin} log . --backlog {backlogDir} --follow",
171
+ "description": "Tail the runner log, human-formatted. Monitoring hint for the user."
172
+ },
173
+ "watchCommand": {
174
+ "type": "string",
175
+ "default": "{bin} status . --backlog {backlogDir} --json",
176
+ "description": "Machine-readable status used by forge-5 for stall detection. rauf's `loop watch` verb was removed in v0.5.0, so this now points at `status --json`; forge-5 keys off the iteration-status `stuckWarning` flag (read from `status --json` / `iteration-status.json`) rather than guessing liveness from state.json timestamps."
177
+ },
178
+ "versionCommand": {
179
+ "type": "string",
180
+ "default": "{bin} version --json",
181
+ "description": "Report runner version as { version: <semver> }. Used to enforce minRunnerVersion before running."
182
+ },
183
+ "agentArgument": {
184
+ "type": "string",
185
+ "default": "--agent {agent}",
186
+ "description": "Tokenized argument appended to the launch command (eventStreamCommand/runCommand) when forge resolves a non-default coding agent for the run. {agent} is substituted ONLY with a validated, advertised agent id (a member of the set agentsProbeCommand reports). PRESENCE of this field advertises the runner's agent surface: when present and non-empty, forge-5 offers the per-run agent selector, honors defaultAgent, and may run agentsProbeCommand; OMIT it for a runner with no agent dimension and forge skips agent selection entirely — no selector, no probe, no {agent} substitution, no agent argument sent (byte-identical to today). Distinct from the version gate (minRunnerVersion)."
187
+ },
188
+ "agentsProbeCommand": {
189
+ "type": "string",
190
+ "default": "{bin} agents --json",
191
+ "description": "Coding-agent availability probe. MUST emit { agents: [{ id, displayName, available, ... }] } and exit 0 (it always exits 0: an unknown id simply never appears; a known-unavailable one appears with available:false). forge-5 runs it ONCE (no retries) before launching a non-default agent to (a) validate the resolved id against the advertised id set and (b) report availability in the pre-launch confirmation. Ignored on the default path and when agentArgument is absent."
192
+ },
193
+ "defaultAgent": {
194
+ "type": "string",
195
+ "default": "",
196
+ "description": "Project-default coding agent id, so a project can fix its agent once without specifying it every run. Empty string ⇒ no project default (the runner's own default — claude-cli for rauf — applies, behaving exactly as today). Overridden by the per-run agent selector (run > project precedence, resolved inside forge before the single --agent is emitted). Ignored when agentArgument is absent."
197
+ },
198
+ "preconditionFile": {
199
+ "type": "string",
200
+ "default": ".rauf.json",
201
+ "description": "Project-root marker file that must exist (runner installed into the target project)."
202
+ },
203
+ "stateDir": {
204
+ "type": "string",
205
+ "default": ".rauf",
206
+ "description": "Per-backlog state directory name created under the backlog dir."
207
+ },
208
+ "logFile": {
209
+ "type": "string",
210
+ "default": "rauf.log",
211
+ "description": "Human-readable event log filename written under {stateDir}. Substituted as {loopRunner.logFile} in the log-tail fallback Monitor command when eventStreamCommand (events.ndjson) is unavailable. The structured NDJSON path uses the contract-standard name events.ndjson and is not configurable here."
212
+ },
213
+ "setupHint": {
214
+ "type": "string",
215
+ "default": "Run `rauf install .` to install rauf's per-project artifacts (.rauf/, RAUF.md, schema), then re-run forge-5.",
216
+ "description": "Shown when preconditionFile is missing — how to set up the runner IN THIS PROJECT (per-project artifacts)."
217
+ },
218
+ "installHint": {
219
+ "type": "string",
220
+ "default": "Provision rauf for a multi-agent setup with the cross-agent installer: `npx @garygentry/feature-forge install` (records the pinned @garygentry/rauf@0.13.0 default). Or install/upgrade just the rauf CLI: `npx @garygentry/rauf@0.13.0 --version`, or `curl -fsSL https://raw.githubusercontent.com/garygentry/rauf/main/scripts/install-binary.sh | bash`.",
221
+ "description": "Shown when the runner BINARY is missing or too old (version gate fails, minRunnerVersion floor) — how to obtain/upgrade the CLI itself. Names two distinct binary-provisioning paths: (1) the cross-agent installer (`npx @garygentry/feature-forge install`, the multi-agent provisioning path that pins @garygentry/rauf@0.13.0), and (2) the direct rauf-CLI install/upgrade one-liner. Distinct from setupHint (which installs per-project artifacts); a version-gate failure is ALWAYS this hint, never setupHint."
222
+ },
223
+ "schemaVersion": {
224
+ "type": "string",
225
+ "default": "1",
226
+ "description": "Backlog schemaVersion this runner config targets."
227
+ },
228
+ "minRunnerVersion": {
229
+ "type": "string",
230
+ "default": "0.6.0",
231
+ "description": "Minimum runner version (semver). 0.6.0 is the AGENT-SURFACE FLOOR: the rauf version that ships the coding-agent selection surface (the --agent flag, the `agents` availability probe, and the preset agent registry) that this config's agentArgument/agentsProbeCommand consume. Flooring here guarantees a successful gate implies those surfaces exist. (0.5.0 was the prior grammar/contract-flip floor — unified exit codes, `loop run --detached`, explicit `review` signal, versioned events.ndjson — which predates the agent surface and so could not guarantee it.) forge-5 enforces this via versionCommand before any loop side-effects."
232
+ }
233
+ }
234
+ }
235
+ }
236
+ }