@garygentry/feature-forge 0.3.2 → 0.3.4

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 (384) hide show
  1. package/adapters/claude/.feature-forge-bundle.json +1 -1
  2. package/adapters/claude/agents/forge-verifier.md +3 -1
  3. package/adapters/claude/references/decisions/single-writer-threat-model.md +53 -0
  4. package/adapters/claude/references/epic-state-schema.json +50 -0
  5. package/adapters/claude/references/forge-config-schema.json +18 -0
  6. package/adapters/claude/references/forge-decisions-schema.json +33 -0
  7. package/adapters/claude/references/pipeline-state-schema.json +34 -2
  8. package/adapters/claude/references/ralph-loop-contract.md +6 -3
  9. package/adapters/claude/references/shared-conventions.md +15 -4
  10. package/adapters/claude/references/stage-exit-protocol.md +55 -11
  11. package/adapters/claude/scripts/epic-manifest.py +82 -4
  12. package/adapters/claude/scripts/fix-sweep.py +1180 -0
  13. package/adapters/claude/scripts/forge-session.py +1124 -26
  14. package/adapters/claude/skills/forge/SKILL.md +5 -5
  15. package/adapters/claude/skills/forge/references/pipeline-state-schema.json +34 -2
  16. package/adapters/claude/skills/forge/references/shared-conventions.md +15 -4
  17. package/adapters/claude/skills/forge/references/stage-exit-protocol.md +55 -11
  18. package/adapters/claude/skills/forge-0-epic/references/edit-mode.md +5 -1
  19. package/adapters/claude/skills/forge-0-epic/references/epic-manifest-subcommands.md +5 -0
  20. package/adapters/claude/skills/forge-0-epic/references/pipeline-state-schema.json +34 -2
  21. package/adapters/claude/skills/forge-0-epic/references/shared-conventions.md +15 -4
  22. package/adapters/claude/skills/forge-0-epic/references/stage-exit-protocol.md +55 -11
  23. package/adapters/claude/skills/forge-1-prd/SKILL.md +3 -1
  24. package/adapters/claude/skills/forge-1-prd/references/shared-conventions.md +15 -4
  25. package/adapters/claude/skills/forge-1-prd/references/stage-exit-protocol.md +55 -11
  26. package/adapters/claude/skills/forge-2-tech/SKILL.md +5 -1
  27. package/adapters/claude/skills/forge-2-tech/references/shared-conventions.md +15 -4
  28. package/adapters/claude/skills/forge-2-tech/references/stage-exit-protocol.md +55 -11
  29. package/adapters/claude/skills/forge-3-specs/SKILL.md +5 -1
  30. package/adapters/claude/skills/forge-3-specs/references/shared-conventions.md +15 -4
  31. package/adapters/claude/skills/forge-3-specs/references/stage-exit-protocol.md +55 -11
  32. package/adapters/claude/skills/forge-4-backlog/SKILL.md +41 -3
  33. package/adapters/claude/skills/forge-4-backlog/references/shared-conventions.md +15 -4
  34. package/adapters/claude/skills/forge-4-backlog/references/stage-exit-protocol.md +55 -11
  35. package/adapters/claude/skills/forge-5-loop/SKILL.md +36 -36
  36. package/adapters/claude/skills/forge-5-loop/references/agent-selection.md +16 -0
  37. package/adapters/claude/skills/forge-5-loop/references/ralph-loop-contract.md +6 -3
  38. package/adapters/claude/skills/forge-5-loop/references/recovery-procedure.md +349 -0
  39. package/adapters/claude/skills/forge-5-loop/references/result-reporting.md +40 -11
  40. package/adapters/claude/skills/forge-5-loop/references/runner-contract.md +22 -4
  41. package/adapters/claude/skills/forge-5-loop/references/shared-conventions.md +15 -4
  42. package/adapters/claude/skills/forge-5-loop/references/stage-exit-protocol.md +55 -11
  43. package/adapters/claude/skills/forge-6-docs/SKILL.md +29 -6
  44. package/adapters/claude/skills/forge-6-docs/references/shared-conventions.md +15 -4
  45. package/adapters/claude/skills/forge-6-docs/references/stage-exit-protocol.md +55 -11
  46. package/adapters/claude/skills/forge-fix/SKILL.md +34 -0
  47. package/adapters/claude/skills/forge-fix/references/shared-conventions.md +15 -4
  48. package/adapters/claude/skills/forge-fix/references/stage-exit-protocol.md +55 -11
  49. package/adapters/claude/skills/forge-guide/references/forge-config-schema.json +18 -0
  50. package/adapters/claude/skills/forge-guide/references/ralph-loop-contract.md +6 -3
  51. package/adapters/claude/skills/forge-guide/references/shared-conventions.md +15 -4
  52. package/adapters/claude/skills/forge-verify/SKILL.md +10 -11
  53. package/adapters/claude/skills/forge-verify/references/decisions/single-writer-threat-model.md +53 -0
  54. package/adapters/claude/skills/forge-verify/references/findings-template.md +30 -0
  55. package/adapters/claude/skills/forge-verify/references/shared-conventions.md +15 -4
  56. package/adapters/claude/skills/forge-verify/references/stage-exit-protocol.md +55 -11
  57. package/adapters/claude/skills/forge-verify/references/verification-checklists/backlog.md +73 -0
  58. package/adapters/claude/skills/forge-verify/references/verification-checklists/impl.md +85 -0
  59. package/adapters/claude/skills/forge-verify/references/verification-checklists/specs.md +43 -1
  60. package/adapters/codex/.feature-forge-bundle.json +1 -1
  61. package/adapters/codex/agents/forge-verifier.toml +3 -1
  62. package/adapters/codex/references/decisions/single-writer-threat-model.md +53 -0
  63. package/adapters/codex/references/epic-state-schema.json +50 -0
  64. package/adapters/codex/references/forge-config-schema.json +18 -0
  65. package/adapters/codex/references/forge-decisions-schema.json +33 -0
  66. package/adapters/codex/references/pipeline-state-schema.json +34 -2
  67. package/adapters/codex/references/process-overview.md +2 -2
  68. package/adapters/codex/references/ralph-loop-contract.md +6 -3
  69. package/adapters/codex/references/shared-conventions.md +44 -33
  70. package/adapters/codex/references/stage-exit-protocol.md +64 -20
  71. package/adapters/codex/scripts/epic-manifest.py +82 -4
  72. package/adapters/codex/scripts/fix-sweep.py +1180 -0
  73. package/adapters/codex/scripts/forge-session.py +1124 -26
  74. package/adapters/codex/skills/forge/SKILL.md +8 -8
  75. package/adapters/codex/skills/forge/references/pipeline-state-schema.json +34 -2
  76. package/adapters/codex/skills/forge/references/process-overview.md +2 -2
  77. package/adapters/codex/skills/forge/references/shared-conventions.md +44 -33
  78. package/adapters/codex/skills/forge/references/stage-exit-protocol.md +64 -20
  79. package/adapters/codex/skills/forge-0-epic/references/edit-mode.md +14 -10
  80. package/adapters/codex/skills/forge-0-epic/references/epic-manifest-subcommands.md +8 -3
  81. package/adapters/codex/skills/forge-0-epic/references/pipeline-state-schema.json +34 -2
  82. package/adapters/codex/skills/forge-0-epic/references/shared-conventions.md +44 -33
  83. package/adapters/codex/skills/forge-0-epic/references/stage-exit-protocol.md +64 -20
  84. package/adapters/codex/skills/forge-1-prd/SKILL.md +3 -1
  85. package/adapters/codex/skills/forge-1-prd/references/shared-conventions.md +44 -33
  86. package/adapters/codex/skills/forge-1-prd/references/stage-exit-protocol.md +64 -20
  87. package/adapters/codex/skills/forge-2-tech/SKILL.md +5 -1
  88. package/adapters/codex/skills/forge-2-tech/references/shared-conventions.md +44 -33
  89. package/adapters/codex/skills/forge-2-tech/references/stage-exit-protocol.md +64 -20
  90. package/adapters/codex/skills/forge-3-specs/SKILL.md +5 -1
  91. package/adapters/codex/skills/forge-3-specs/references/shared-conventions.md +44 -33
  92. package/adapters/codex/skills/forge-3-specs/references/stage-exit-protocol.md +64 -20
  93. package/adapters/codex/skills/forge-4-backlog/SKILL.md +41 -3
  94. package/adapters/codex/skills/forge-4-backlog/references/shared-conventions.md +44 -33
  95. package/adapters/codex/skills/forge-4-backlog/references/stage-exit-protocol.md +64 -20
  96. package/adapters/codex/skills/forge-5-loop/SKILL.md +36 -36
  97. package/adapters/codex/skills/forge-5-loop/references/agent-selection.md +17 -1
  98. package/adapters/codex/skills/forge-5-loop/references/ralph-loop-contract.md +6 -3
  99. package/adapters/codex/skills/forge-5-loop/references/recovery-procedure.md +349 -0
  100. package/adapters/codex/skills/forge-5-loop/references/result-reporting.md +40 -11
  101. package/adapters/codex/skills/forge-5-loop/references/runner-contract.md +26 -8
  102. package/adapters/codex/skills/forge-5-loop/references/shared-conventions.md +44 -33
  103. package/adapters/codex/skills/forge-5-loop/references/stage-exit-protocol.md +64 -20
  104. package/adapters/codex/skills/forge-6-docs/SKILL.md +29 -6
  105. package/adapters/codex/skills/forge-6-docs/references/shared-conventions.md +44 -33
  106. package/adapters/codex/skills/forge-6-docs/references/stage-exit-protocol.md +64 -20
  107. package/adapters/codex/skills/forge-fix/SKILL.md +34 -0
  108. package/adapters/codex/skills/forge-fix/references/shared-conventions.md +44 -33
  109. package/adapters/codex/skills/forge-fix/references/stage-exit-protocol.md +64 -20
  110. package/adapters/codex/skills/forge-guide/SKILL.md +1 -1
  111. package/adapters/codex/skills/forge-guide/references/forge-config-schema.json +18 -0
  112. package/adapters/codex/skills/forge-guide/references/process-overview.md +2 -2
  113. package/adapters/codex/skills/forge-guide/references/ralph-loop-contract.md +6 -3
  114. package/adapters/codex/skills/forge-guide/references/shared-conventions.md +44 -33
  115. package/adapters/codex/skills/forge-init/SKILL.md +1 -1
  116. package/adapters/codex/skills/forge-verify/SKILL.md +11 -12
  117. package/adapters/codex/skills/forge-verify/references/decisions/single-writer-threat-model.md +53 -0
  118. package/adapters/codex/skills/forge-verify/references/findings-template.md +32 -2
  119. package/adapters/codex/skills/forge-verify/references/shared-conventions.md +44 -33
  120. package/adapters/codex/skills/forge-verify/references/stage-exit-protocol.md +64 -20
  121. package/adapters/codex/skills/forge-verify/references/verification-checklists/backlog.md +73 -0
  122. package/adapters/codex/skills/forge-verify/references/verification-checklists/epic.md +1 -1
  123. package/adapters/codex/skills/forge-verify/references/verification-checklists/impl.md +85 -0
  124. package/adapters/codex/skills/forge-verify/references/verification-checklists/specs.md +43 -1
  125. package/adapters/copilot/.feature-forge-bundle.json +1 -1
  126. package/adapters/copilot/agents/forge-verifier.md +3 -1
  127. package/adapters/copilot/references/decisions/single-writer-threat-model.md +53 -0
  128. package/adapters/copilot/references/epic-state-schema.json +50 -0
  129. package/adapters/copilot/references/forge-config-schema.json +18 -0
  130. package/adapters/copilot/references/forge-decisions-schema.json +33 -0
  131. package/adapters/copilot/references/pipeline-state-schema.json +34 -2
  132. package/adapters/copilot/references/process-overview.md +2 -2
  133. package/adapters/copilot/references/ralph-loop-contract.md +6 -3
  134. package/adapters/copilot/references/shared-conventions.md +44 -33
  135. package/adapters/copilot/references/stage-exit-protocol.md +64 -20
  136. package/adapters/copilot/scripts/epic-manifest.py +82 -4
  137. package/adapters/copilot/scripts/fix-sweep.py +1180 -0
  138. package/adapters/copilot/scripts/forge-session.py +1124 -26
  139. package/adapters/copilot/skills/forge/forge.md +8 -8
  140. package/adapters/copilot/skills/forge/references/pipeline-state-schema.json +34 -2
  141. package/adapters/copilot/skills/forge/references/process-overview.md +2 -2
  142. package/adapters/copilot/skills/forge/references/shared-conventions.md +44 -33
  143. package/adapters/copilot/skills/forge/references/stage-exit-protocol.md +64 -20
  144. package/adapters/copilot/skills/forge-0-epic/references/edit-mode.md +14 -10
  145. package/adapters/copilot/skills/forge-0-epic/references/epic-manifest-subcommands.md +8 -3
  146. package/adapters/copilot/skills/forge-0-epic/references/pipeline-state-schema.json +34 -2
  147. package/adapters/copilot/skills/forge-0-epic/references/shared-conventions.md +44 -33
  148. package/adapters/copilot/skills/forge-0-epic/references/stage-exit-protocol.md +64 -20
  149. package/adapters/copilot/skills/forge-1-prd/forge-1-prd.md +3 -1
  150. package/adapters/copilot/skills/forge-1-prd/references/shared-conventions.md +44 -33
  151. package/adapters/copilot/skills/forge-1-prd/references/stage-exit-protocol.md +64 -20
  152. package/adapters/copilot/skills/forge-2-tech/forge-2-tech.md +5 -1
  153. package/adapters/copilot/skills/forge-2-tech/references/shared-conventions.md +44 -33
  154. package/adapters/copilot/skills/forge-2-tech/references/stage-exit-protocol.md +64 -20
  155. package/adapters/copilot/skills/forge-3-specs/forge-3-specs.md +5 -1
  156. package/adapters/copilot/skills/forge-3-specs/references/shared-conventions.md +44 -33
  157. package/adapters/copilot/skills/forge-3-specs/references/stage-exit-protocol.md +64 -20
  158. package/adapters/copilot/skills/forge-4-backlog/forge-4-backlog.md +41 -3
  159. package/adapters/copilot/skills/forge-4-backlog/references/shared-conventions.md +44 -33
  160. package/adapters/copilot/skills/forge-4-backlog/references/stage-exit-protocol.md +64 -20
  161. package/adapters/copilot/skills/forge-5-loop/forge-5-loop.md +36 -36
  162. package/adapters/copilot/skills/forge-5-loop/references/agent-selection.md +17 -1
  163. package/adapters/copilot/skills/forge-5-loop/references/ralph-loop-contract.md +6 -3
  164. package/adapters/copilot/skills/forge-5-loop/references/recovery-procedure.md +349 -0
  165. package/adapters/copilot/skills/forge-5-loop/references/result-reporting.md +40 -11
  166. package/adapters/copilot/skills/forge-5-loop/references/runner-contract.md +26 -8
  167. package/adapters/copilot/skills/forge-5-loop/references/shared-conventions.md +44 -33
  168. package/adapters/copilot/skills/forge-5-loop/references/stage-exit-protocol.md +64 -20
  169. package/adapters/copilot/skills/forge-6-docs/forge-6-docs.md +29 -6
  170. package/adapters/copilot/skills/forge-6-docs/references/shared-conventions.md +44 -33
  171. package/adapters/copilot/skills/forge-6-docs/references/stage-exit-protocol.md +64 -20
  172. package/adapters/copilot/skills/forge-fix/forge-fix.md +34 -0
  173. package/adapters/copilot/skills/forge-fix/references/shared-conventions.md +44 -33
  174. package/adapters/copilot/skills/forge-fix/references/stage-exit-protocol.md +64 -20
  175. package/adapters/copilot/skills/forge-guide/forge-guide.md +1 -1
  176. package/adapters/copilot/skills/forge-guide/references/forge-config-schema.json +18 -0
  177. package/adapters/copilot/skills/forge-guide/references/process-overview.md +2 -2
  178. package/adapters/copilot/skills/forge-guide/references/ralph-loop-contract.md +6 -3
  179. package/adapters/copilot/skills/forge-guide/references/shared-conventions.md +44 -33
  180. package/adapters/copilot/skills/forge-init/forge-init.md +1 -1
  181. package/adapters/copilot/skills/forge-verify/forge-verify.md +11 -12
  182. package/adapters/copilot/skills/forge-verify/references/decisions/single-writer-threat-model.md +53 -0
  183. package/adapters/copilot/skills/forge-verify/references/findings-template.md +32 -2
  184. package/adapters/copilot/skills/forge-verify/references/shared-conventions.md +44 -33
  185. package/adapters/copilot/skills/forge-verify/references/stage-exit-protocol.md +64 -20
  186. package/adapters/copilot/skills/forge-verify/references/verification-checklists/backlog.md +73 -0
  187. package/adapters/copilot/skills/forge-verify/references/verification-checklists/epic.md +1 -1
  188. package/adapters/copilot/skills/forge-verify/references/verification-checklists/impl.md +85 -0
  189. package/adapters/copilot/skills/forge-verify/references/verification-checklists/specs.md +43 -1
  190. package/adapters/cursor/.feature-forge-bundle.json +1 -1
  191. package/adapters/cursor/agents/forge-verifier.mdc +3 -1
  192. package/adapters/cursor/references/decisions/single-writer-threat-model.md +53 -0
  193. package/adapters/cursor/references/epic-state-schema.json +50 -0
  194. package/adapters/cursor/references/forge-config-schema.json +18 -0
  195. package/adapters/cursor/references/forge-decisions-schema.json +33 -0
  196. package/adapters/cursor/references/pipeline-state-schema.json +34 -2
  197. package/adapters/cursor/references/process-overview.md +2 -2
  198. package/adapters/cursor/references/ralph-loop-contract.md +6 -3
  199. package/adapters/cursor/references/shared-conventions.md +44 -33
  200. package/adapters/cursor/references/stage-exit-protocol.md +64 -20
  201. package/adapters/cursor/scripts/epic-manifest.py +82 -4
  202. package/adapters/cursor/scripts/fix-sweep.py +1180 -0
  203. package/adapters/cursor/scripts/forge-session.py +1124 -26
  204. package/adapters/cursor/skills/forge/forge.mdc +8 -8
  205. package/adapters/cursor/skills/forge/references/pipeline-state-schema.json +34 -2
  206. package/adapters/cursor/skills/forge/references/process-overview.md +2 -2
  207. package/adapters/cursor/skills/forge/references/shared-conventions.md +44 -33
  208. package/adapters/cursor/skills/forge/references/stage-exit-protocol.md +64 -20
  209. package/adapters/cursor/skills/forge-0-epic/references/edit-mode.md +14 -10
  210. package/adapters/cursor/skills/forge-0-epic/references/epic-manifest-subcommands.md +8 -3
  211. package/adapters/cursor/skills/forge-0-epic/references/pipeline-state-schema.json +34 -2
  212. package/adapters/cursor/skills/forge-0-epic/references/shared-conventions.md +44 -33
  213. package/adapters/cursor/skills/forge-0-epic/references/stage-exit-protocol.md +64 -20
  214. package/adapters/cursor/skills/forge-1-prd/forge-1-prd.mdc +3 -1
  215. package/adapters/cursor/skills/forge-1-prd/references/shared-conventions.md +44 -33
  216. package/adapters/cursor/skills/forge-1-prd/references/stage-exit-protocol.md +64 -20
  217. package/adapters/cursor/skills/forge-2-tech/forge-2-tech.mdc +5 -1
  218. package/adapters/cursor/skills/forge-2-tech/references/shared-conventions.md +44 -33
  219. package/adapters/cursor/skills/forge-2-tech/references/stage-exit-protocol.md +64 -20
  220. package/adapters/cursor/skills/forge-3-specs/forge-3-specs.mdc +5 -1
  221. package/adapters/cursor/skills/forge-3-specs/references/shared-conventions.md +44 -33
  222. package/adapters/cursor/skills/forge-3-specs/references/stage-exit-protocol.md +64 -20
  223. package/adapters/cursor/skills/forge-4-backlog/forge-4-backlog.mdc +41 -3
  224. package/adapters/cursor/skills/forge-4-backlog/references/shared-conventions.md +44 -33
  225. package/adapters/cursor/skills/forge-4-backlog/references/stage-exit-protocol.md +64 -20
  226. package/adapters/cursor/skills/forge-5-loop/forge-5-loop.mdc +36 -36
  227. package/adapters/cursor/skills/forge-5-loop/references/agent-selection.md +17 -1
  228. package/adapters/cursor/skills/forge-5-loop/references/ralph-loop-contract.md +6 -3
  229. package/adapters/cursor/skills/forge-5-loop/references/recovery-procedure.md +349 -0
  230. package/adapters/cursor/skills/forge-5-loop/references/result-reporting.md +40 -11
  231. package/adapters/cursor/skills/forge-5-loop/references/runner-contract.md +26 -8
  232. package/adapters/cursor/skills/forge-5-loop/references/shared-conventions.md +44 -33
  233. package/adapters/cursor/skills/forge-5-loop/references/stage-exit-protocol.md +64 -20
  234. package/adapters/cursor/skills/forge-6-docs/forge-6-docs.mdc +29 -6
  235. package/adapters/cursor/skills/forge-6-docs/references/shared-conventions.md +44 -33
  236. package/adapters/cursor/skills/forge-6-docs/references/stage-exit-protocol.md +64 -20
  237. package/adapters/cursor/skills/forge-fix/forge-fix.mdc +34 -0
  238. package/adapters/cursor/skills/forge-fix/references/shared-conventions.md +44 -33
  239. package/adapters/cursor/skills/forge-fix/references/stage-exit-protocol.md +64 -20
  240. package/adapters/cursor/skills/forge-guide/forge-guide.mdc +1 -1
  241. package/adapters/cursor/skills/forge-guide/references/forge-config-schema.json +18 -0
  242. package/adapters/cursor/skills/forge-guide/references/process-overview.md +2 -2
  243. package/adapters/cursor/skills/forge-guide/references/ralph-loop-contract.md +6 -3
  244. package/adapters/cursor/skills/forge-guide/references/shared-conventions.md +44 -33
  245. package/adapters/cursor/skills/forge-init/forge-init.mdc +1 -1
  246. package/adapters/cursor/skills/forge-verify/forge-verify.mdc +11 -12
  247. package/adapters/cursor/skills/forge-verify/references/decisions/single-writer-threat-model.md +53 -0
  248. package/adapters/cursor/skills/forge-verify/references/findings-template.md +32 -2
  249. package/adapters/cursor/skills/forge-verify/references/shared-conventions.md +44 -33
  250. package/adapters/cursor/skills/forge-verify/references/stage-exit-protocol.md +64 -20
  251. package/adapters/cursor/skills/forge-verify/references/verification-checklists/backlog.md +73 -0
  252. package/adapters/cursor/skills/forge-verify/references/verification-checklists/epic.md +1 -1
  253. package/adapters/cursor/skills/forge-verify/references/verification-checklists/impl.md +85 -0
  254. package/adapters/cursor/skills/forge-verify/references/verification-checklists/specs.md +43 -1
  255. package/adapters/gemini/.feature-forge-bundle.json +1 -1
  256. package/adapters/gemini/agents/forge-verifier.md +3 -1
  257. package/adapters/gemini/gemini-extension.json +1 -1
  258. package/adapters/gemini/references/decisions/single-writer-threat-model.md +53 -0
  259. package/adapters/gemini/references/epic-state-schema.json +50 -0
  260. package/adapters/gemini/references/forge-config-schema.json +18 -0
  261. package/adapters/gemini/references/forge-decisions-schema.json +33 -0
  262. package/adapters/gemini/references/pipeline-state-schema.json +34 -2
  263. package/adapters/gemini/references/process-overview.md +2 -2
  264. package/adapters/gemini/references/ralph-loop-contract.md +6 -3
  265. package/adapters/gemini/references/shared-conventions.md +44 -33
  266. package/adapters/gemini/references/stage-exit-protocol.md +64 -20
  267. package/adapters/gemini/scripts/epic-manifest.py +82 -4
  268. package/adapters/gemini/scripts/fix-sweep.py +1180 -0
  269. package/adapters/gemini/scripts/forge-session.py +1124 -26
  270. package/adapters/gemini/skills/forge/forge.md +8 -8
  271. package/adapters/gemini/skills/forge/references/pipeline-state-schema.json +34 -2
  272. package/adapters/gemini/skills/forge/references/process-overview.md +2 -2
  273. package/adapters/gemini/skills/forge/references/shared-conventions.md +44 -33
  274. package/adapters/gemini/skills/forge/references/stage-exit-protocol.md +64 -20
  275. package/adapters/gemini/skills/forge-0-epic/references/edit-mode.md +14 -10
  276. package/adapters/gemini/skills/forge-0-epic/references/epic-manifest-subcommands.md +8 -3
  277. package/adapters/gemini/skills/forge-0-epic/references/pipeline-state-schema.json +34 -2
  278. package/adapters/gemini/skills/forge-0-epic/references/shared-conventions.md +44 -33
  279. package/adapters/gemini/skills/forge-0-epic/references/stage-exit-protocol.md +64 -20
  280. package/adapters/gemini/skills/forge-1-prd/forge-1-prd.md +3 -1
  281. package/adapters/gemini/skills/forge-1-prd/references/shared-conventions.md +44 -33
  282. package/adapters/gemini/skills/forge-1-prd/references/stage-exit-protocol.md +64 -20
  283. package/adapters/gemini/skills/forge-2-tech/forge-2-tech.md +5 -1
  284. package/adapters/gemini/skills/forge-2-tech/references/shared-conventions.md +44 -33
  285. package/adapters/gemini/skills/forge-2-tech/references/stage-exit-protocol.md +64 -20
  286. package/adapters/gemini/skills/forge-3-specs/forge-3-specs.md +5 -1
  287. package/adapters/gemini/skills/forge-3-specs/references/shared-conventions.md +44 -33
  288. package/adapters/gemini/skills/forge-3-specs/references/stage-exit-protocol.md +64 -20
  289. package/adapters/gemini/skills/forge-4-backlog/forge-4-backlog.md +41 -3
  290. package/adapters/gemini/skills/forge-4-backlog/references/shared-conventions.md +44 -33
  291. package/adapters/gemini/skills/forge-4-backlog/references/stage-exit-protocol.md +64 -20
  292. package/adapters/gemini/skills/forge-5-loop/forge-5-loop.md +36 -36
  293. package/adapters/gemini/skills/forge-5-loop/references/agent-selection.md +17 -1
  294. package/adapters/gemini/skills/forge-5-loop/references/ralph-loop-contract.md +6 -3
  295. package/adapters/gemini/skills/forge-5-loop/references/recovery-procedure.md +349 -0
  296. package/adapters/gemini/skills/forge-5-loop/references/result-reporting.md +40 -11
  297. package/adapters/gemini/skills/forge-5-loop/references/runner-contract.md +26 -8
  298. package/adapters/gemini/skills/forge-5-loop/references/shared-conventions.md +44 -33
  299. package/adapters/gemini/skills/forge-5-loop/references/stage-exit-protocol.md +64 -20
  300. package/adapters/gemini/skills/forge-6-docs/forge-6-docs.md +29 -6
  301. package/adapters/gemini/skills/forge-6-docs/references/shared-conventions.md +44 -33
  302. package/adapters/gemini/skills/forge-6-docs/references/stage-exit-protocol.md +64 -20
  303. package/adapters/gemini/skills/forge-fix/forge-fix.md +34 -0
  304. package/adapters/gemini/skills/forge-fix/references/shared-conventions.md +44 -33
  305. package/adapters/gemini/skills/forge-fix/references/stage-exit-protocol.md +64 -20
  306. package/adapters/gemini/skills/forge-guide/forge-guide.md +1 -1
  307. package/adapters/gemini/skills/forge-guide/references/forge-config-schema.json +18 -0
  308. package/adapters/gemini/skills/forge-guide/references/process-overview.md +2 -2
  309. package/adapters/gemini/skills/forge-guide/references/ralph-loop-contract.md +6 -3
  310. package/adapters/gemini/skills/forge-guide/references/shared-conventions.md +44 -33
  311. package/adapters/gemini/skills/forge-init/forge-init.md +1 -1
  312. package/adapters/gemini/skills/forge-verify/forge-verify.md +11 -12
  313. package/adapters/gemini/skills/forge-verify/references/decisions/single-writer-threat-model.md +53 -0
  314. package/adapters/gemini/skills/forge-verify/references/findings-template.md +32 -2
  315. package/adapters/gemini/skills/forge-verify/references/shared-conventions.md +44 -33
  316. package/adapters/gemini/skills/forge-verify/references/stage-exit-protocol.md +64 -20
  317. package/adapters/gemini/skills/forge-verify/references/verification-checklists/backlog.md +73 -0
  318. package/adapters/gemini/skills/forge-verify/references/verification-checklists/epic.md +1 -1
  319. package/adapters/gemini/skills/forge-verify/references/verification-checklists/impl.md +85 -0
  320. package/adapters/gemini/skills/forge-verify/references/verification-checklists/specs.md +43 -1
  321. package/adapters/pi/.feature-forge-bundle.json +1 -1
  322. package/adapters/pi/agents/forge-verifier.md +3 -1
  323. package/adapters/pi/references/decisions/single-writer-threat-model.md +53 -0
  324. package/adapters/pi/references/epic-state-schema.json +50 -0
  325. package/adapters/pi/references/forge-config-schema.json +18 -0
  326. package/adapters/pi/references/forge-decisions-schema.json +33 -0
  327. package/adapters/pi/references/pipeline-state-schema.json +34 -2
  328. package/adapters/pi/references/process-overview.md +2 -2
  329. package/adapters/pi/references/ralph-loop-contract.md +6 -3
  330. package/adapters/pi/references/shared-conventions.md +33 -22
  331. package/adapters/pi/references/stage-exit-protocol.md +63 -19
  332. package/adapters/pi/scripts/epic-manifest.py +82 -4
  333. package/adapters/pi/scripts/fix-sweep.py +1180 -0
  334. package/adapters/pi/scripts/forge-session.py +1124 -26
  335. package/adapters/pi/skills/forge/SKILL.md +5 -5
  336. package/adapters/pi/skills/forge/references/pipeline-state-schema.json +34 -2
  337. package/adapters/pi/skills/forge/references/process-overview.md +2 -2
  338. package/adapters/pi/skills/forge/references/shared-conventions.md +33 -22
  339. package/adapters/pi/skills/forge/references/stage-exit-protocol.md +63 -19
  340. package/adapters/pi/skills/forge-0-epic/references/edit-mode.md +8 -4
  341. package/adapters/pi/skills/forge-0-epic/references/epic-manifest-subcommands.md +6 -1
  342. package/adapters/pi/skills/forge-0-epic/references/pipeline-state-schema.json +34 -2
  343. package/adapters/pi/skills/forge-0-epic/references/shared-conventions.md +33 -22
  344. package/adapters/pi/skills/forge-0-epic/references/stage-exit-protocol.md +63 -19
  345. package/adapters/pi/skills/forge-1-prd/SKILL.md +3 -1
  346. package/adapters/pi/skills/forge-1-prd/references/shared-conventions.md +33 -22
  347. package/adapters/pi/skills/forge-1-prd/references/stage-exit-protocol.md +63 -19
  348. package/adapters/pi/skills/forge-2-tech/SKILL.md +5 -1
  349. package/adapters/pi/skills/forge-2-tech/references/shared-conventions.md +33 -22
  350. package/adapters/pi/skills/forge-2-tech/references/stage-exit-protocol.md +63 -19
  351. package/adapters/pi/skills/forge-3-specs/SKILL.md +5 -1
  352. package/adapters/pi/skills/forge-3-specs/references/shared-conventions.md +33 -22
  353. package/adapters/pi/skills/forge-3-specs/references/stage-exit-protocol.md +63 -19
  354. package/adapters/pi/skills/forge-4-backlog/SKILL.md +41 -3
  355. package/adapters/pi/skills/forge-4-backlog/references/shared-conventions.md +33 -22
  356. package/adapters/pi/skills/forge-4-backlog/references/stage-exit-protocol.md +63 -19
  357. package/adapters/pi/skills/forge-5-loop/SKILL.md +36 -36
  358. package/adapters/pi/skills/forge-5-loop/references/agent-selection.md +16 -0
  359. package/adapters/pi/skills/forge-5-loop/references/ralph-loop-contract.md +6 -3
  360. package/adapters/pi/skills/forge-5-loop/references/recovery-procedure.md +349 -0
  361. package/adapters/pi/skills/forge-5-loop/references/result-reporting.md +40 -11
  362. package/adapters/pi/skills/forge-5-loop/references/runner-contract.md +25 -7
  363. package/adapters/pi/skills/forge-5-loop/references/shared-conventions.md +33 -22
  364. package/adapters/pi/skills/forge-5-loop/references/stage-exit-protocol.md +63 -19
  365. package/adapters/pi/skills/forge-6-docs/SKILL.md +29 -6
  366. package/adapters/pi/skills/forge-6-docs/references/shared-conventions.md +33 -22
  367. package/adapters/pi/skills/forge-6-docs/references/stage-exit-protocol.md +63 -19
  368. package/adapters/pi/skills/forge-fix/SKILL.md +34 -0
  369. package/adapters/pi/skills/forge-fix/references/shared-conventions.md +33 -22
  370. package/adapters/pi/skills/forge-fix/references/stage-exit-protocol.md +63 -19
  371. package/adapters/pi/skills/forge-guide/references/forge-config-schema.json +18 -0
  372. package/adapters/pi/skills/forge-guide/references/process-overview.md +2 -2
  373. package/adapters/pi/skills/forge-guide/references/ralph-loop-contract.md +6 -3
  374. package/adapters/pi/skills/forge-guide/references/shared-conventions.md +33 -22
  375. package/adapters/pi/skills/forge-verify/SKILL.md +10 -11
  376. package/adapters/pi/skills/forge-verify/references/decisions/single-writer-threat-model.md +53 -0
  377. package/adapters/pi/skills/forge-verify/references/findings-template.md +31 -1
  378. package/adapters/pi/skills/forge-verify/references/shared-conventions.md +33 -22
  379. package/adapters/pi/skills/forge-verify/references/stage-exit-protocol.md +63 -19
  380. package/adapters/pi/skills/forge-verify/references/verification-checklists/backlog.md +73 -0
  381. package/adapters/pi/skills/forge-verify/references/verification-checklists/epic.md +1 -1
  382. package/adapters/pi/skills/forge-verify/references/verification-checklists/impl.md +85 -0
  383. package/adapters/pi/skills/forge-verify/references/verification-checklists/specs.md +43 -1
  384. package/package.json +1 -1
@@ -8,7 +8,7 @@ description: Execute the autonomous coding loop (rauf by default) against a forg
8
8
 
9
9
  Execute the autonomous coding loop against a forge feature's backlog. The loop spawns a fresh agent session per backlog item, implementing each task with full verification.
10
10
 
11
- The loop **runner** is configured, not hardcoded. feature-forge talks to it through the `loopRunner` block in `forge.config.json`; rauf is the default and reference implementation (see `references/ralph-loop-contract.md`). Every command below is rendered from `loopRunner` with token substitution — there are no hardcoded `rauf …` commands in this skill, and even the human log filename is tokenized as `{loopRunner.logFile}`.
11
+ The loop **runner** is configured, not hardcoded. feature-forge talks to it through the `loopRunner` block in `forge.config.json`; rauf is the default and reference implementation (see `references/ralph-loop-contract.md`). Every command below is rendered from `loopRunner` with token substitution — no hardcoded `rauf …` commands; even the human log filename is tokenized (`{loopRunner.logFile}`).
12
12
 
13
13
  ## Resolve the loop runner
14
14
 
@@ -41,13 +41,15 @@ Read and follow `references/shared-conventions.md` for feature name validation,
41
41
 
42
42
  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."
43
43
 
44
+ If the state's `notes` is non-empty, surface it before proceeding and treat it as run input — often backlog-time constraints; it never overrides specs or config (raise any conflict).
45
+
44
46
  ### 1b. Verification Check
45
47
 
46
48
  Read `stages.forge-verify-backlog` and branch on its status — **four** cases, in this order (the pending case must be tested *before* the generic one, or owed-and-dropped debt gets reported as never-scheduled):
47
49
 
48
50
  1. **`passed`** — proceed with no prompt.
49
- 2. **`findings-applied`** — fixes were applied but nothing re-verified them: this status deliberately clears freshness, so the backlog's verification is still outstanding, not silently satisfied. Use `AskUserQuestion` to offer: **Re-verify first (recommended)** (`/skill:forge-verify {feature} backlog`) · **Continue without re-verifying** — an explicit deferral, persisted via `state-verify --status skipped` (never by hand; add `--epic "{epic}"` for members) before the loop starts, so it is a recorded decision and not a walked-past gate.
50
- 3. **`auto-verify-pending`** — automatic verification *was* scheduled for the backlog stage and the debt *was* durably recorded; it simply has not run. Say exactly that, naming the served stage and the retry command: *"{feature}: automatic verification is still pending for forge-4-backlog; run `/skill:forge-verify {feature} backlog` to resolve it."* Then use `AskUserQuestion` to offer the same two choices as case 4. Never report this as "hasn't been verified yet" — "nobody ever asked for this" and "this was owed and dropped" are different facts and the operator acts on them differently.
51
+ 2. **`findings-applied`** — fixes were applied but nothing re-verified them: this status deliberately clears freshness, so the backlog's verification is still outstanding, not silently satisfied. Use `AskUserQuestion` to offer: **Re-verify first (recommended)** (`/skill:forge-verify {feature} backlog`) · **Continue without re-verifying**. On continue, write **nothing**: `state-verify` refuses demoting `findings-applied` to `skipped` (#203); the recorded status already says re-verification is outstanding.
52
+ 3. **`auto-verify-pending`** — automatic verification *was* scheduled for the backlog stage and the debt *was* durably recorded; it simply has not run. Say exactly that, naming the served stage and the retry command: *"{feature}: automatic verification is still pending for forge-4-backlog; run `/skill:forge-verify {feature} backlog` to resolve it."* Then use `AskUserQuestion` to offer the same two choices as case 4. Never report this as "hasn't been verified yet" — "nobody ever asked for this" and "this was owed and dropped" are different facts.
51
53
  4. **Anything else** (absent, `pending`, `skipped`, `findings-reported`) — use `AskUserQuestion` to warn with the cost of skipping: "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?"
52
54
 
53
55
  Cases 3 and 4 offer the same choices: **Verify first (recommended)** · **Continue without verifying**. The proceed-anyway path is unchanged.
@@ -114,13 +116,17 @@ Verify the file exists on disk. If not, STOP and tell the user: "No backlog.json
114
116
 
115
117
  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.
116
118
 
119
+ ### 1g. Stranded-Work Pre-flight (if using git)
120
+
121
+ Run `git status --porcelain`. If it reports changes **and** `{backlogDir}/{loopRunner.stateDir}/state.json` exists from a previous run, **STOP**: name that run (its `startedAt`, `currentItem`, and `blockedItems` from `state.json`) and point the user at the **Post-Run Tree Reconciliation** section of `references/recovery-procedure.md` to commit / stash / discard the stranded work before relaunch — never auto-pass `--force`. If the tree is dirty with **no** prior-run `state.json`, keep today's behavior (surface it; let the user commit/stash or pass `--force`). A clean tree is silent. rauf's own launch refusal remains the backstop.
122
+
117
123
  ## Step 2: Construct the Loop Command
118
124
 
119
125
  ### 2a. Analyze Backlog
120
126
 
121
- Run the **list command** (`loopRunner.listCommand`, default `rauf backlog list . --backlog {backlogDir} --json`) and count items by status: `pending`, `in_progress`, `done`, `blocked`.
127
+ Run the **list command** (`loopRunner.listCommand`, default `rauf backlog list . --backlog {backlogDir} --json`) and count items by status: `pending`, `in_progress`, `done`, `blocked`. Pipe that same list-command JSON into `backlog-topology --items-stdin --json` (a `forge-session.py` verb — invoke it via Step 3a's `$R` fence) and read `maxChainDepth` to report alongside the iteration count — advisory only: no prompt, no operator decision.
122
128
 
123
- 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.
129
+ Calculate the iteration count: `ceil((pending + in_progress) * loopIterationMultiplier)` where `loopIterationMultiplier` comes from `forge.config.json` (default: 1.5, headroom for retries).
124
130
 
125
131
  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."
126
132
 
@@ -133,8 +139,6 @@ If there are `blocked` items, note them — the user may want `--retry-blocked`.
133
139
  - If `backlogDir` is set in config: use the per-feature subpath `{backlogDir}/{feature}` (matching the 1e composition rule and forge-4-backlog §6.2).
134
140
  - Otherwise: use `{resolvedFeatureDir}` (the directory containing `backlog.json`).
135
141
 
136
- **Example:** If `specsDir` is `./specs` and feature is `auth`, `{backlogDir}` is `specs/auth`.
137
-
138
142
  ### 2c. Build Command
139
143
 
140
144
  Render the **run command** (`loopRunner.runCommand`) with token substitution, e.g. the rauf default becomes:
@@ -158,19 +162,20 @@ Backlog summary:
158
162
  - Done: {done}
159
163
  - Blocked: {blocked}
160
164
  - Iterations: {iterationCount} ({activeItems} items x {loopIterationMultiplier} multiplier)
165
+ - Max chain depth: {maxChainDepth} — depth bounds achievable progress regardless of iteration budget
161
166
 
162
167
  For the model-selection precedence (item.model > --model/options > project default >
163
168
  provider default), read references/runner-contract.md.
164
169
  ```
165
170
 
166
- **Run mode and full loop-runner contract:** follow `## Run mode (Step 2d, rauf)` and the remaining sections in `references/runner-contract.md` verbatim.
171
+ **Run mode and full loop-runner contract:** follow `## Run mode (Step 2d, rauf)` and the remaining sections in `references/runner-contract.md` verbatim; `loopRunner.reviewMode` (`"always"`/`"never"`) suppresses the Run-mode question — semantics live there.
167
172
 
168
173
  #### Agent selection (gated on `loopRunner.agentArgument`)
169
174
 
170
175
  **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/agent-selection.md`; read it. When the gate is on, augment Step 2d in order:
171
176
 
172
177
  - **(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).
173
- - **(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).
178
+ - **(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). Under `loopRunner.agentMode: "auto"`, skip this question (`run_selection = None`); (a)/(c)/(d)/(d-model) still run — see `references/agent-selection.md`.
174
179
  - **(c) Availability listing.** From the **same** parsed `agents[]` (no second probe), list `id` / `displayName` / available (`yes`/`no`, `detail` on unavailable rows).
175
180
  - **(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.
176
181
  - **(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/agent-selection.md`.
@@ -193,7 +198,7 @@ Then commit this state write before launching (mandatory). The runner refuses to
193
198
 
194
199
  ### 3b. Launch Background Process
195
200
 
196
- 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`.
201
+ 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). 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`.
197
202
 
198
203
  ### 3c. Inform User
199
204
 
@@ -202,21 +207,12 @@ Follow the **Inform-user output template (Step 3c)** section of `references/runn
202
207
  ### 3d. Arm a Monitor on the event stream, and react to events
203
208
 
204
209
  Arm the **host's monitoring mechanism** on the structured event stream (the NDJSON file, or the
205
- human log as fallback) so events flow back into this session as they happen. Use
206
- **`persistent: true`** — runs can exceed the host's monitoring mechanism's maximum `timeout_ms` (1 hour),
207
- and a bounded timeout would silently stop watching a still-running loop. The filter
208
- MUST match every terminal and exception state, not just the happy path (silence is
209
- not success). Monitor the **structured** surface, never raw `RAUF_*` tokens.
210
-
211
- Each Monitor event arrives as a message; react per type — surface `needs_human` /
212
- `loop_error` immediately with a `PushNotification`, coalesce `item_completed` into
213
- milestones, and treat `llm_stuck_warning` as a hang warning. A `needs_human` /
214
- `blocked` signal does **not** pause the loop — the runner sets the item aside and
215
- keeps going.
216
-
217
- For the exact Monitor commands (NDJSON `jq` filter and the log-fallback `grep`
218
- prefixes), the coverage-complete filter event list, and the full per-event reaction
219
- rules, read `references/runner-contract.md`.
210
+ human log as fallback) with **`persistent: true`**, a coverage-complete filter
211
+ matching every terminal and exception state (silence is not success), and react to
212
+ each event as it arrives. The exact Monitor commands, the filter event list, and the
213
+ full per-event reaction rules (`needs_human` / `loop_error` surfaced immediately with
214
+ a `PushNotification`, `item_completed` coalesced into milestones, `llm_stuck_warning`
215
+ as a hang warning) are in `references/runner-contract.md` — follow them verbatim.
220
216
 
221
217
  ### 3f. Reach completion
222
218
 
@@ -232,12 +228,16 @@ Run the **status-json command** (`loopRunner.statusJsonCommand`) and read
232
228
  `backlogSummary` for the authoritative counts — it separates the three non-done
233
229
  outcomes: genuine `blocked`, `needsHuman`, and runner-`deferred` ("false blocks").
234
230
  Fall back to the **list command** (`loopRunner.listCommand`) if `statusJsonCommand`
235
- 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`.
231
+ is not configured. 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`.
236
232
 
237
233
  ### 4b. Report Results
238
234
 
239
235
  Present a summary to the user. Pick **every** branch that applies (a run can be both
240
- blocked and needs-human) and render its report. The five verbatim result-report output templates — **all-done**, **needs-human**, **blocked**, **deferred**, and **pending** (iteration limit reached) — are in `references/result-reporting.md`, together with the Step 7 `LoopOutcome` ladder these same counts feed. The reports are descriptive only: they carry no next command, and the run does not end here. If the authoritative counts cannot be obtained at all (4a failed or its output does not parse), follow **Operational failure before the counts are known** in that same file: surface the failure and its recovery, and close nothing — no outcome, no stage exit, no terminal block.
236
+ blocked and needs-human) and render its report. The five verbatim result-report output templates — **all-done**, **needs-human**, **blocked**, **deferred**, and **pending** (with a conditional cause) — are in `references/result-reporting.md`, together with the Step 7 `LoopOutcome` ladder these same counts feed. The reports are descriptive only: they carry no next command, and the run does not end here. If the authoritative counts cannot be obtained at all (4a failed or its output does not parse), follow **Operational failure before the counts are known** in that same file: surface the failure and its recovery, and close nothing — no outcome, no stage exit, no terminal block.
237
+
238
+ ### 4c. Post-Run Recovery Pass (unconditional)
239
+
240
+ Run the **Post-Run Recovery Procedure** (`references/recovery-procedure.md`) now — on **every** run close, before Step 5 writes state, so the tree it inspects is exactly what the run left. The live `needs_human` handler (3d) collects answers early but is **not** the entry condition: a run that emitted no event still enters here — this is what makes the plain-blocked unblock reachable on blocked-only runs. With nothing to decide, its step 1 skips straight to the §4 tree reconciliation — silent on a clean tree. A run can strand uncommitted work with no signal (items failing a shared final acceptance criterion are never committed); this pass reconciles it — 1g's pre-flight is only the next-launch backstop. Its step-7 gate feeds Step 7's `resolved` rung; the stage still closes exactly once, in Step 7.
241
241
 
242
242
  ## Step 5: Update Pipeline State
243
243
 
@@ -253,9 +253,9 @@ python3 "$R/scripts/forge-session.py" state-complete --feature "{feature}" --sta
253
253
 
254
254
  ## Step 5b: Offer Impl-Verify (standalone path)
255
255
 
256
- **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** straight to Step 7 — a non-complete run has nothing to verify yet, 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**, invoke `feature-forge:forge-verify {feature} impl` with the literal `owner: nested` token in the dispatching prompt — this dispatch happens inside the loop stage, so **you** remain the sole terminal owner and the branch skill returns its structured result and prints no terminal block of its own (see "Branch ownership: the `owner:` token" in `references/stage-exit-protocol.md`). On **skip**, persist the skip through `state-verify` using the fence below (mirrors `forge-4-backlog`'s skip handling) — the forge-6-docs backstop re-surfaces the skip. Either way, do **not** name a next command here: Step 7 routes, and it routes differently depending on what this step recorded.
256
+ **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** straight to Step 7 — a non-complete run has nothing to verify yet, 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. 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**, invoke `feature-forge:forge-verify {feature} impl` with the literal `owner: nested` token in the dispatching prompt — this dispatch happens inside the loop stage, so **you** remain the sole terminal owner and the branch skill returns its structured result and prints no terminal block of its own (see "Branch ownership" and "Caller-side resumption" in `references/stage-exit-protocol.md`; a delegate-and-resume site — on return, control resumes here). A verifier return without its report structure is a dropped digest, not a result (issue #183) — apply "Truncated Verifier Returns" in forge-verify's `findings-template.md` reference (resume or re-dispatch) before recording anything. On **skip**, persist the skip through `state-verify` using the fence below (mirrors `forge-4-backlog`'s skip handling) — the forge-6-docs backstop re-surfaces the skip. Either way, do **not** name a next command here: Step 7 routes, and it routes differently depending on what this step recorded.
257
257
 
258
- **The skip is written by `state-verify`, never by hand.** Add `--epic "{epic}"` when this feature is an epic member — required, per the Pipeline State Protocol in `references/shared-conventions.md`. On exit 2, surface the plain `Error:` line verbatim and stop: the skip is not persisted, so Step 7 would route on state that is not on disk.
258
+ **The skip is written by `state-verify`, never by hand — and never over a resolved entry** (`passed`/`findings-applied`: the verb refuses that demotion, #203 — skip the fence and continue). Add `--epic "{epic}"` when this feature is an epic member — required, per the Pipeline State Protocol in `references/shared-conventions.md`. On exit 2, surface the plain `Error:` line verbatim and stop: the skip is not persisted, so Step 7 would route on state that is not on disk.
259
259
 
260
260
  ```bash
261
261
  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')"
@@ -267,7 +267,7 @@ python3 "$R/scripts/forge-session.py" state-verify --feature "{feature}" --stage
267
267
 
268
268
  **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** straight to Step 7 — standalone completed features are handled by Step 5b, and a non-complete run has no handoff to make (REQ-COMPAT-01).
269
269
 
270
- 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?"* On **run**, invoke `feature-forge:forge-verify {feature} impl` with the literal `owner: nested` token in the dispatching prompt — this dispatch happens inside the loop stage, so **you** remain the sole terminal owner and the branch skill returns its structured result and prints no terminal block of its own (see "Branch ownership: the `owner:` token" in `references/stage-exit-protocol.md`). On **skip**, persist the skip through `state-verify --status skipped` using Step 5b's fence (add `--epic "{epic}"` — required for members) so the Step 7 exit reads a recorded decision instead of re-asking the question the user just answered; completion is then judged on the §7 rule with impl-verify explicitly skipped.
270
+ 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?"* On **run**, dispatch exactly as Step 5b does — the same literal `owner: nested` token in the dispatching prompt (you remain the sole terminal owner), the same truncated-return guard before recording anything, and the same declared resume (control returns here; the handoff continues at 2). On **skip**, persist the skip through `state-verify --status skipped` using Step 5b's fence (add `--epic "{epic}"` — required for members) so the Step 7 exit reads a recorded decision instead of re-asking the question the user just answered; completion is then judged on the §7 rule with impl-verify explicitly skipped.
271
271
  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.
272
272
  3. **Announce what is actionable — do not route.** Read `render-status`'s `actionable` set (every dependency now complete, not itself complete) and say plainly which members can start now, or which are still blocked and on which dependencies. This is context for the user, not a handoff: the Step 7 exit consumes the same live payload and fences the one authoritative next command itself, so do **not** present a next-feature picker, offer to author a member's PRD, or repeat a member's `nextCommand` here. Two competing actions is exactly the ambiguity the scripted exit removes.
273
273
  4. **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. Then fall through to Step 7 — the epic handoff closes there, once, like every other path.
@@ -276,7 +276,7 @@ python3 "$R/scripts/forge-session.py" state-verify --feature "{feature}" --stage
276
276
 
277
277
  Every loop run ends here, and ends here **exactly once** — standalone or epic member, complete or not.
278
278
 
279
- First select the single `LoopOutcome` with the ladder in `references/result-reporting.md` (`needs-human` → `blocked` → `deferred` → `partial` → `complete`, first match wins), reading it from Step 4a's authoritative counts and never from the runner's process exit code. If those counts were never obtained, follow that file's operational-failure rule instead: report the failure and its recovery and run no exit at all.
279
+ First select the single `LoopOutcome` with the ladder in `references/result-reporting.md` (`resolved` → `needs-human` → `blocked` → `deferred` → `partial` → `complete`, first match wins), reading it from Step 4a's authoritative counts and never from the runner's process exit code. If those counts were never obtained, follow that file's operational-failure rule instead: report the failure and its recovery and run no exit at all.
280
280
 
281
281
  **Close this stage with the Scripted Stage Exit** (contract: `references/stage-exit-protocol.md`; do not improvise a "Next steps" list). Run:
282
282
 
@@ -292,13 +292,13 @@ Add `--epic "{epic}"` when this feature is an epic member — required, per the
292
292
 
293
293
  ## Gotchas
294
294
 
295
- - **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`.
295
+ - **Plugin-root discovery (1b-epic helper) covers installed paths, not workspace-dev checkouts.** The `forge-root.sh` search probes the locations of an **installed** plugin only, so a feature-forge **source checkout** (e.g. `~/workspace/feature-forge`) exits "cannot locate plugin root." Expected in a dev environment; 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`.
296
296
  - `{backlogDir}` is a **directory path**, not a file path. Pass `specs/auth`, not `specs/auth/backlog.json`.
297
- - 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.
298
- - 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).
299
- - 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.
297
+ - rauf resolves `RAUF.md` with fallback (`{backlogDir}/.rauf/RAUF.md` first, then the project's `.rauf/RAUF.md`). State files (state.json, {loopRunner.logFile}, etc.) land at `{backlogDir}/{loopRunner.stateDir}/`, isolated per backlog dir, so concurrent features don't collide.
298
+ - If the session disconnects mid-loop, the runner process continues independently — check results later with the status / list commands. A stale lock from a previous run may need `--force` to clear.
299
+ - 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) — `persistent: true`, the **structured** surface (`events.ndjson`), never raw `RAUF_*` tokens (they false-match in agent prose). 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.
300
300
  - The version gate (1c) uses the `--json` form on purpose; never parse `rauf version`'s human output.
301
- - **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.
301
+ - **Implementation artifacts must not cite specs.** The loop should **read** specs and `backlog.json` freely — they are the source of truth, and the backlog rightly cites specs for provenance. But artifacts the loop **writes into the target repo** (source code, generated `SKILL.md`/agent files, configs, code comments) must be **self-contained**: no references to feature-forge spec files (no `See specs/{feature}/NN-*.md`, no "source spec" provenance notes) — specs are pre-implementation inputs that may be archived or deleted once the feature ships. This applies only to shipped implementation output, never to the backlog or spec documents, which keep citing specs.
302
302
 
303
303
  ---
304
304
 
@@ -21,6 +21,22 @@ Step 3c are byte-identical to today (capability gate;
21
21
  item.provider > --agent (run selection) > loopRunner.defaultAgent (project) > runner default (claude-cli)
22
22
  ```
23
23
 
24
+ **`loopRunner.agentMode` gate (`"prompt"` default | `"auto"`).** `"prompt"`
25
+ presents the Step 2d agent question (SKILL sub-step b) — byte-identical to today.
26
+ `"auto"` suppresses **only the interactive pick**: skip the agent question and
27
+ resolve as if the user made no per-run selection (`run_selection = None`, so
28
+ `defaultAgent` — or the runner default when unset — applies). Everything else on
29
+ this surface **still runs under `"auto"`**: the single probe, the availability
30
+ listing, the verdict classification below (UNKNOWN hard-reject before any loop
31
+ side-effect, UNAVAILABLE with its proceed-anyway/choose-another question,
32
+ probe-failure handling), and the Claude-only model-alias guard — those questions
33
+ are safety surfaces, not the pick, and are never suppressed. The resolved
34
+ `Agent: {id} (source: …)` line still shows in the confirmation and the Step 3c
35
+ template, so the choice is never hidden. Meaningless when
36
+ `loopRunner.agentArgument` is absent — the capability gate above already removes
37
+ the entire surface, and `agentMode` adds no second gate. An unrecognized value
38
+ behaves as `"prompt"`.
39
+
24
40
  **Run-layer mapping — why forge never re-implements rauf's resolver.** forge owns
25
41
  **only** its run and project layers and collapses them into **one** value
26
42
  (`resolve()`: `run_selection or defaultAgent or none`), which it emits as a single
@@ -58,9 +58,12 @@ defined authoritatively in rauf's
58
58
  > item aside and **keeps working other runnable items to completion** (rauf:
59
59
  > `runner.ts` needs_human handler). So a supervising session can surface those
60
60
  > events live (visibility) and cancel early, but it cannot inject an answer and
61
- > resume the set-aside item mid-run — resolution is a follow-up retry pass. A
62
- > first-class pause/resume-with-answer capability is a desirable runner
63
- > enhancement (see `plans/rauf-enhancement-recommendations.md`).
61
+ > resume the set-aside item mid-run — resolution is the **Post-Run Recovery
62
+ > Procedure** (`skills/forge-5-loop/references/recovery-procedure.md`): record the
63
+ > answer via `decision-record` at the moment of collection, then drive recovery
64
+ > from the record after the run ends. A first-class pause/resume-with-answer
65
+ > capability is a desirable runner enhancement (see
66
+ > `plans/rauf-enhancement-recommendations.md`).
64
67
 
65
68
  ## rauf is the default and reference implementation
66
69
 
@@ -0,0 +1,349 @@
1
+ # forge-5-loop — Post-Run Recovery Procedure
2
+
3
+ The named procedure that turns a needs-human / blocked loop stop into a resumable backlog
4
+ without losing the operator's decision. It runs **after** a loop run ends — entered
5
+ **unconditionally** from SKILL Step 4c on every run close, whatever the counts say (the
6
+ `needs_human` / `item_blocked` live-event handling in `runner-contract.md` collects
7
+ answers early for it, but is **not** the entry condition) — and it runs again as the
8
+ **re-entry point** on a fresh session (§3).
9
+ Its seven ordered steps: **enumerate → cluster → consolidated prompts →
10
+ record-at-collection → apply → prove → gate & exit**.
11
+
12
+ Notation: `{backlogDir}` is the resolved backlog directory (SKILL Step 2b);
13
+ `{stateDir}` is the effective-config `loopRunner.stateDir` (default `.rauf`); `$R` is the
14
+ plugin root the SKILL's bootstrap prelude resolves; runner commands are the substituted
15
+ `loopRunner.*Command` forms with the SKILL's token substitution (`{bin}` etc.).
16
+
17
+ ## 1. Scope and the failure rule
18
+
19
+ The procedure orchestrates scripted substrate; it never improvises state. Decisions live
20
+ in `{backlogDir}/{stateDir}/forge-decisions.json` — append-only, written **only** by the
21
+ `decision-record` / `decision-list` / `decision-apply` verbs of
22
+ `scripts/forge-session.py` (schema: `references/forge-decisions-schema.json`), never by
23
+ hand. Being under the git-ignored state dir, the record survives session end and context
24
+ clear but never dirties the working tree that §4 inspects.
25
+
26
+ **The failure rule (applies to every step).** Any scripted step that exits non-zero, and
27
+ any runner invocation that errors or returns unparseable output, is surfaced **verbatim**
28
+ and **STOPS** the procedure with a **failed recovery** report — never reported as
29
+ recorded/succeeded. A failed *apply* (step 5) is distinguishable from a
30
+ ran-but-nothing-moved *proof* failure (step 6) because the former never reaches step 6
31
+ (§6).
32
+
33
+ ## 2. The seven steps
34
+
35
+ ### Step 1 — Enumerate
36
+
37
+ - **Input:** `{backlogDir}`; the runner's authoritative item list.
38
+ - **CLI:**
39
+ ```
40
+ python3 "$R/scripts/forge-session.py" decision-list --backlog-dir {backlogDir} --unapplied --json
41
+ {bin} backlog list . --backlog {backlogDir} --json # the substituted listCommand
42
+ ```
43
+ The unapplied set is the **latest entry per `itemId` with `appliedAt == null`** —
44
+ deferrals included, applied items excluded.
45
+ - **Decision point:** if the unapplied set is **empty** and no item is
46
+ `blocked`/`needsHuman`, there is nothing to decide or apply: **skip steps 2–6 and go
47
+ straight to step 7 — never exit around it.** Step 7's §4 tree reconciliation still
48
+ runs (it is what catches work stranded without any signal), and its `resolved` gate
49
+ does not apply — **an empty affected set never selects `resolved`**; the SKILL Step 7
50
+ ladder falls through to its count-based rungs. Combined with the clean-tree silence
51
+ of §4.1, this keeps a happy-path run free of any new prompt; the only new happy-path
52
+ output is the Step 2a depth line.
53
+ - **Output:** the unapplied-decision set (each entry's `itemId`, `question`,
54
+ `answer|null`, `deferred`, `clusterId?`), and the live blocked/needs-human item set.
55
+ - **Error:** a `decision-list` exit 2 (unknown dir, unparseable record) stops the
56
+ procedure. A failed `listCommand` read stops it as a failed recovery.
57
+
58
+ ### Step 2 — Cluster
59
+
60
+ - **Input:** the blocked/needs-human items from step 1, each carrying its
61
+ `blockedReason` (where the runner lands the `RAUF_NEEDS_HUMAN:<reason>` text).
62
+ - **CLI:**
63
+ ```
64
+ python3 "$R/scripts/forge-session.py" backlog-topology --items-stdin --cluster --json < items.json
65
+ ```
66
+ fed the **same** `listCommand` JSON already obtained (single data source — never a
67
+ `backlog.json` path). Returns `clusters[]`: each with `memberIds`, `memberReasons`,
68
+ `sharedTokens`, and the **union** of members' gated subtrees (`gatedIds` +
69
+ `gatedCount`).
70
+ - **Decision point:** you **may merge or refine** candidate clusters by judgment —
71
+ under-clustering is the deliberately-chosen failure direction of the scripted helper,
72
+ so its clusters are a floor, not a ceiling. You have no scripted *split* authority.
73
+ - **Output:** the final cluster set (scripted candidates ± your merges), each with its
74
+ member ids and blast-radius numbers.
75
+ - **Error:** a `backlog-topology` exit 2 stops the procedure.
76
+
77
+ ### Step 3 — Consolidated prompts
78
+
79
+ - **Input:** the final cluster set from step 2.
80
+ - **Mechanism:** `AskUserQuestion` (never inline prose).
81
+ - For any cluster of **two or more** items: emit **exactly one** consolidated question
82
+ that **names every affected item id** and states the **full gated subtree** the
83
+ cluster gates. Frame it by **blast radius** — e.g. *"This one decision gates 13 of
84
+ 16 backlog items (items 2, 3, …). Answer it once."* — never one prompt per member.
85
+ - Singleton clusters prompt per item (today's per-item shape).
86
+ - **Security:** prompts **MUST NOT solicit secrets**. Ask for the *decision* (which
87
+ path, which policy), never a credential/token/key value. The decision record has no
88
+ credential-shaped field and is treated as repo-visible content.
89
+ - **Decision point:** the operator may **answer**, **defer** the decision, or request
90
+ **cancel the run early** — all three branches proceed to step 4 (nothing is acted on
91
+ before it is recorded).
92
+ - **Output:** per cluster/item, one of {answer text, deferral, cancel-early}.
93
+ - **Citation:** the blast-radius framing is derived from `backlog-topology --cluster`
94
+ gated-subtree output (member ids + counts) — the prompt cites that source; a
95
+ "gates N/M" claim the topology output contradicts is a defect.
96
+
97
+ ### Step 4 — Record at collection
98
+
99
+ - **Input:** every branch outcome from step 3.
100
+ - **CLI (one call per decision, BEFORE anything is applied):**
101
+ ```
102
+ # answered singleton
103
+ python3 "$R/scripts/forge-session.py" decision-record --backlog-dir {backlogDir} \
104
+ --item ID --question "Q" --answer "A"
105
+ # deferred, or cancel-early (both record a deferral: no --answer)
106
+ python3 "$R/scripts/forge-session.py" decision-record --backlog-dir {backlogDir} \
107
+ --item ID --question "Q" --deferred
108
+ # consolidated answer: one entry per affected item, shared clusterId
109
+ python3 "$R/scripts/forge-session.py" decision-record --backlog-dir {backlogDir} \
110
+ --item ID1 --item ID2 --item ID3 --question "Q" --answer "A" --cluster c1
111
+ ```
112
+ (`--actor` defaults to `forge-5-loop@<host>` — a machine label, never user identity.)
113
+ - **Decision point:** a decision is recorded on **every** branch — answered, deferred,
114
+ **and** cancel-early — and it is recorded **before** step 5 acts on anything. A
115
+ cancel-early is recorded as a **deferral** (`answer: null`, `deferred: true`,
116
+ `question` carrying the original needs-human text) — there is no third entry form. A
117
+ recorded-but-unapplied entry (`appliedAt == null`) is exactly what step 1 re-surfaces
118
+ on the next launch (§3).
119
+ - **Consolidated:** one entry per affected item, all sharing one `clusterId` (minted
120
+ `c` + lowest member id). Items stay **independently re-decidable**: a later per-item
121
+ entry supersedes the cluster entry for that item only.
122
+ - **Output:** durable append-only entries in `forge-decisions.json`; the write is
123
+ atomic.
124
+ - **Error:** any `decision-record` exit 2 (both/neither of `--answer`/`--deferred`,
125
+ unknown dir, failed atomic write) is surfaced verbatim and stops the procedure. The
126
+ answer is **not** applied if it was not recorded.
127
+
128
+ ### Step 5 — Apply
129
+
130
+ - **Version probe (once, at the start of this step):** run the substituted
131
+ `loopRunner.versionCommand` (default `{bin} version --json`), parse
132
+ `{ "version": "<semver>" }`, and numerically semver-compare it against
133
+ `RECOVERY_MIN_RUNNER_VERSION` (a `scripts/forge-session.py` module constant, `0.14.0`
134
+ — the capability threshold for `{bin} backlog answer`; **not**
135
+ `loopRunner.minRunnerVersion`, which stays the launch floor). A probe miss
136
+ (missing/old/unparseable version) is **never** a hard failure — it selects the
137
+ degraded path and is reported with `loopRunner.installHint`.
138
+ - **Apply per item** (full dispatch table in §5):
139
+ - needs-human item, runner **≥** threshold →
140
+ `{bin} backlog answer . {id} "{answer}" --backlog {backlogDir} --json`
141
+ (the answer text is threaded into the next iteration's prompt).
142
+ - needs-human item, runner **<** threshold → **degraded path:**
143
+ `{bin} backlog unblock . {id} --backlog {backlogDir} --json` — the item is genuinely
144
+ unblocked and the answer stays durable in `forge-decisions.json`, but the recovery
145
+ report **must state explicitly** that the answer was **not** injected into the next
146
+ iteration's prompt, with the `installHint` upgrade hint attached.
147
+ - plain (non-needs-human) blocked item → `{bin} backlog unblock` at **every** runner
148
+ version.
149
+ - **Stamp:** after each runner apply **succeeds**, run
150
+ ```
151
+ python3 "$R/scripts/forge-session.py" decision-apply --backlog-dir {backlogDir} --item ID
152
+ ```
153
+ which stamps `appliedAt`/`appliedBy` on the item's latest entry. `decision-apply` is
154
+ called **only after** the runner apply returned success — a stamped record means the
155
+ runner actually accepted the change.
156
+ - **Error:** a runner apply that **errors** (non-zero exit — item missing, not
157
+ `blocked`, or any failure) is a **failed apply**: surface it verbatim, do **not** call
158
+ `decision-apply`, stop the procedure, report failed recovery. This is distinct from a
159
+ version-probe miss (which routes to the degraded path, not a failure) and from step
160
+ 6's ran-but-nothing-moved failure (§6).
161
+
162
+ ### Step 6 — Prove
163
+
164
+ - **Input:** the affected item set that step 5 applied.
165
+ - **CLI:** re-read per-item state via the substituted `loopRunner.listCommand`
166
+ (`{bin} backlog list . --backlog {backlogDir} --json`) and test **each** affected
167
+ item: `status != "blocked"` — which, per the runner's derivation
168
+ (needs-human ⇔ `status=="blocked" && needsHuman==true`), also removes it from the
169
+ needs-human count, so the single test covers both flags. Aggregate `backlogSummary`
170
+ counts are **never** the test. An affected item **missing** from the re-read counts
171
+ as a non-mover.
172
+ - **Decision point:** **all** affected items moved → proceed to step 7. **Any**
173
+ non-mover — including a partial move where some items moved and others did not — is a
174
+ **failed recovery**: report it, **naming the movers and the non-movers** from their
175
+ item `status` fields.
176
+ - **Output:** either "all moved → continue" or a failed-recovery report.
177
+ - **Citation:** the movers/non-movers are named from the per-item `listCommand` re-read
178
+ (`status` fields), never from aggregate counts — a report that contradicts the
179
+ per-item read is a defect.
180
+
181
+ ### Step 7 — Gate & exit
182
+
183
+ - **Tree reconciliation first.** Before any outcome is selected, run the **Post-Run
184
+ Tree Reconciliation** section (§4). It runs on every recovery pass — including passes
185
+ with no needs-human items — and is silent on a clean tree.
186
+ - **Evaluate the `resolved` gate — all three must hold:**
187
+ 1. `decision-list --unapplied` is **empty for the affected items**. The verb returns
188
+ the **global** latest-unapplied-per-item set, so **intersect** that payload's
189
+ entries (each carries `itemId`) with this session's affected-item set and test
190
+ only that intersection for emptiness — an unrelated item's stray deferral must not
191
+ suppress a legitimate `resolved`.
192
+ 2. `git status --porcelain` is **clean** (git-ignored `{stateDir}` artifacts are
193
+ invisible to porcelain — the exclusion holds by construction).
194
+ 3. the per-item re-read (step 6) shows **every** affected item left
195
+ `blocked`/`needsHuman`.
196
+ - **Select the outcome:** on all-three-pass, select `resolved` — the first rung of the
197
+ ladder in `result-reporting.md`, so a resolved stop never re-triggers the needs-human
198
+ branch its own recovery just cleared. **Any one gate failing falls the ladder
199
+ through** to `needs-human` / `blocked` / `deferred` / `partial` / `complete` exactly
200
+ as today. `resolved` routes **resume** — its NEXT-STEPS block fences
201
+ `/skill:forge-5-loop {feature}`, never the navigator.
202
+ - **CLI:** the close runs through the Scripted Stage Exit (SKILL Step 7):
203
+ `stage-exit … --outcome resolved …`. `stage-exit` does **not** re-verify the gate
204
+ server-side (it has no runner access) — enforcement is procedural: this step.
205
+ - **Citation:** the `resolved` outcome text cites the three gate evaluations
206
+ (`decision-list --unapplied` empty, porcelain empty, per-item re-read all-moved).
207
+ Claiming `resolved` without those preconditions is a reportable defect.
208
+
209
+ ## 3. Fresh-session re-entry
210
+
211
+ The procedure is the **re-entry point** on a fresh session / next launch — this is what
212
+ makes a decision survive session end and context clear.
213
+
214
+ On a new session, **step 1** enumerates every entry with `appliedAt == null` from a
215
+ *previous* session — answered-but-not-yet-applied decisions, deferrals, and cancel-early
216
+ deferrals alike. Those entries are re-surfaced:
217
+
218
+ - An entry that already carries an **answer** (`answer != null`, `deferred == false`,
219
+ `appliedAt == null`) **skips step 3's prompt** for that item — the operator already
220
+ decided; the procedure proceeds straight to step 5 (apply) and step 6 (prove). The
221
+ answer collected last session is applied this session without re-asking.
222
+ - A **deferral** (`deferred == true`) re-surfaces through step 3 as an open decision —
223
+ the operator is asked again, and their new answer appends a **new** entry
224
+ (append-only); the deferral's audit fields are never destroyed.
225
+
226
+ Because entries are durable and untracked, a session boundary, crash, or context clear
227
+ between "operator answered" and "answer applied" never costs the decision — step 1 of
228
+ the next launch finds it.
229
+
230
+ ## 4. Post-Run Tree Reconciliation
231
+
232
+ Invoked from step 7 after the run ends and **before** any outcome is selected. It runs
233
+ on **every** recovery pass — including passes with no needs-human items, which step 1
234
+ routes here directly, and SKILL Step 4c enters the procedure on every run close —
235
+ because it is the "tree" half of recovery. Four sub-steps.
236
+
237
+ ### 4.1 Detect
238
+
239
+ - **CLI:** `git status --porcelain`.
240
+ - **Clean tree → SILENT.** Empty output ⇒ no prompt, no output, no operator decision.
241
+ The decision record and all runner state under `{stateDir}` are git-ignored and
242
+ therefore never appear in porcelain output — decision writes never dirty the tree
243
+ this step inspects.
244
+ - **Dirty tree → proceed to 4.2.**
245
+ - **Error:** a `git status` failure (not a git repo, git error) is surfaced verbatim;
246
+ reconciliation is skipped (there is nothing git-native to reconcile), the rest of the
247
+ procedure continues.
248
+
249
+ ### 4.2 Attribute (best-effort, runner-native)
250
+
251
+ Best-effort attribution of dirty paths to the backlog item(s) that produced them, from
252
+ runner-native evidence — reliable per-item provenance is **not** a prerequisite.
253
+
254
+ - **Read `{backlogDir}/{stateDir}/state.json`** (the runner's loop state):
255
+ `baseCommitHash` (the HEAD captured at run start — the baseline for
256
+ `git log {baseCommitHash}..HEAD`), `completedItems` / `blockedItems` (item ids that
257
+ finished / blocked), `currentItem` (the item in flight when the run stopped — a
258
+ strong candidate for uncommitted changes), `startedAt` and
259
+ `iteration`/`maxIterations` (run identity + budget).
260
+ - **Read `{backlogDir}/{stateDir}/events.ndjson`** — one JSON object per line; parse
261
+ line-by-line (there is **no** runner CLI for events; the file is read directly). The
262
+ per-iteration `item_selected`, `llm_spawned`, and `llm_exited` records — each
263
+ carrying an `itemId` and a `timestamp` — name which items ran during the window and
264
+ in what order.
265
+ - **Map dirty paths → candidate items:** the `currentItem` and the most recent
266
+ `item_selected`/`llm_spawned` without a matching clean `llm_exited` are the items "in
267
+ flight when the run died"; `git log {baseCommitHash}..HEAD` names what was already
268
+ committed for which item (the runner commits `[rauf] <id>: <title>`). Present the
269
+ mapping as **CANDIDATES, never asserted**.
270
+ - **Degradation (detection never aborts):** if `state.json` or `events.ndjson` is
271
+ missing, unreadable, or unparseable, **degrade** to the fully-unattributed path —
272
+ everything goes into 4.3's single consolidated decision. Detection (4.1) is never
273
+ aborted by an evidence-parse failure.
274
+ - **Citation:** the presentation cites `git status --porcelain` paths +
275
+ `{stateDir}/state.json` / `events.ndjson` run evidence, with every attribution
276
+ explicitly labelled a **candidate**.
277
+
278
+ ### 4.3 Decide
279
+
280
+ - **Mechanism:** `AskUserQuestion` (never inline prose).
281
+ - **One question per attributed item-group:** for each candidate item-group from 4.2,
282
+ offer **commit-for-that-item** / **stash** / **discard**.
283
+ - **Unattributable changes → ONE consolidated decision:** everything that could not
284
+ be attributed is presented as a single grouped question, not dropped.
285
+ - **Discard guard:** **discard is NEVER the default** and requires its **own explicit
286
+ confirmation** — a second, dedicated question via `AskUserQuestion` confirming the specific paths
287
+ to be discarded before any `git checkout`/`git restore`/`git clean` runs. No path is
288
+ discarded on a single click.
289
+ - **Output:** per group, an executed reconciliation (commit / stash / confirmed
290
+ discard) or a deferral the operator can revisit.
291
+
292
+ ### 4.4 Launch blocker
293
+
294
+ The next launch's `### 1g. Stranded-Work Pre-flight` (SKILL Step 1) STOPS on a dirty
295
+ tree when a prior run's `{backlogDir}/{stateDir}/state.json` exists, names that run
296
+ (its `startedAt`, `currentItem`, `blockedItems`), and points at this section to
297
+ commit / stash / discard the stranded work before relaunch. The runner's own
298
+ uncommitted-changes launch refusal remains the backstop for a dirty tree with no
299
+ prior-run state.
300
+
301
+ ## 5. Apply-mechanism dispatch (version gate & the degraded path)
302
+
303
+ | Runner version | Item kind | Apply mechanism | What the report says |
304
+ |---|---|---|---|
305
+ | `≥ RECOVERY_MIN_RUNNER_VERSION` | needs-human (has an answer) | `{bin} backlog answer . {id} "{answer}" --backlog {backlogDir} --json` | Answer applied and threaded into the next iteration's prompt. |
306
+ | `≥ RECOVERY_MIN_RUNNER_VERSION` | plain blocked | `{bin} backlog unblock . {id} --backlog {backlogDir} --json` | Item unblocked. |
307
+ | `< RECOVERY_MIN_RUNNER_VERSION` (or probe miss) | needs-human | **DEGRADE:** `{bin} backlog unblock . {id} --backlog {backlogDir} --json` | Item unblocked; **answer was NOT injected into the next prompt** (durable in `forge-decisions.json`); `{installHint}` — upgrade to a runner that ships `backlog answer` to thread it. |
308
+ | any version (incl. probe miss) | plain blocked | `{bin} backlog unblock . {id} --backlog {backlogDir} --json` | Item unblocked. |
309
+
310
+ Key properties:
311
+
312
+ - **Plain blocked items always use `unblock`, at every version** — they carry no answer
313
+ to thread. The version gate only ever changes the needs-human path.
314
+ - **The degraded needs-human path genuinely unblocks** (the runner clears
315
+ `status`/`blockedReason`/`needsHuman`/`deferred`), so recovery works across the whole
316
+ supported runner floor. The only capability lost below the threshold is
317
+ prompt-threading — the answer remains durable in the decision record and re-surfaces
318
+ via `decision-list --unapplied` if re-decided.
319
+ - **The report is honest either way:** the degraded path states explicitly that the
320
+ answer was not threaded, with the upgrade hint.
321
+
322
+ ## 6. Failure taxonomy
323
+
324
+ | Failure | When it occurs | Reaches the step-6 per-item test? | Report |
325
+ |---|---|---|---|
326
+ | **Failed apply** | `{bin} backlog answer` / `unblock` exits non-zero (corrupt backlog, I/O error, not-blocked/not-found refusal); or the post-apply re-read is unparseable | **No** — stops *before* the test | Verbatim runner error + which item; **failed recovery**; procedure stops; never claimed succeeded |
327
+ | **Ran-but-nothing-moved** | Every apply exited 0, but the step-6 per-item test finds a non-mover | **Yes** — *is* the test failing | Movers/non-movers named from `status` fields; **failed recovery** |
328
+ | **Version-probe miss** | `versionCommand` missing/unparseable, or version `< RECOVERY_MIN_RUNNER_VERSION` | N/A — selects the degraded path (§5) | Degraded path proceeds; not-threaded caveat + `installHint`; **not** a failed recovery |
329
+
330
+ Rules: never report recorded/succeeded past a failed step; a failed apply stops before
331
+ the per-item test, so a runner that errored is never conflated with a runner that ran
332
+ cleanly but moved nothing; `decision-apply` is not called for a failed item — the record
333
+ stays unapplied and re-surfaces next launch; a probe miss degrades, it never fails
334
+ recovery.
335
+
336
+ ## 7. Report citations (REQ-OBS-01)
337
+
338
+ Every report surface this procedure produces names the authoritative source it derived
339
+ its claims from; a claim that source contradicts is a reportable defect. Each report
340
+ surface names the authoritative source it derives its claims from:
341
+
342
+ | Report surface | Authoritative citation basis |
343
+ |---|---|
344
+ | Pending / starvation template | `backlogSummary` counts + `backlog-topology` output over `listCommand` JSON; iteration counters from `state.json` (`iteration`/`maxIterations`) |
345
+ | Failed-recovery report (§2 step 6) | The per-item `listCommand` re-read — movers/non-movers named from item `status`, never aggregate counts |
346
+ | `resolved` outcome text | The three gate evaluations: `decision-list --unapplied` (empty), `git status --porcelain` (empty), per-item re-read (all affected left `blocked`) |
347
+ | Consolidated blast-radius prompt (§2 step 3) | `backlog-topology --cluster` gated-subtree output (member ids + counts) |
348
+ | Tree-reconciliation presentation (§4) | `git status --porcelain` paths + `state.json`/`events.ndjson` run evidence, attributions explicitly presented as **candidates** |
349
+ | Step 2a depth line | The same `backlog-topology` output (`maxChainDepth`) |