@garygentry/feature-forge 0.3.6 → 0.3.8

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 (753) hide show
  1. package/README.md +1 -1
  2. package/adapters/GENERATION-REPORT.md +20 -0
  3. package/adapters/claude/.claude-plugin/plugin.json +16 -0
  4. package/adapters/claude/.feature-forge-bundle.json +1 -1
  5. package/adapters/claude/agents/forge-verifier.md +16 -5
  6. package/adapters/claude/references/forge-config-schema.json +3 -3
  7. package/adapters/claude/references/portable-root.md +20 -16
  8. package/adapters/claude/references/preflight-and-self-heal.md +183 -0
  9. package/adapters/claude/references/process-overview.md +1 -1
  10. package/adapters/claude/references/select-outcome.md +138 -0
  11. package/adapters/claude/references/shared-conventions.md +175 -22
  12. package/adapters/claude/references/skill-frontmatter.schema.json +3 -1
  13. package/adapters/claude/references/stack-resolution.md +1 -1
  14. package/adapters/claude/references/stage-exit-protocol.md +19 -4
  15. package/adapters/claude/references/templates/root-hygiene/AGENTS.md +14 -0
  16. package/adapters/claude/references/templates/root-hygiene/CLAUDE.md +14 -0
  17. package/adapters/claude/references/templates/specs-hygiene/AGENTS.md +3 -1
  18. package/adapters/claude/references/templates/specs-hygiene/CLAUDE.md +2 -1
  19. package/adapters/claude/references/verifier-patterns/MEMORY.md +35 -0
  20. package/adapters/claude/references/verifier-patterns/pattern_absence_claims.md +36 -0
  21. package/adapters/claude/references/verifier-patterns/pattern_carveout_sibling_semantics.md +34 -0
  22. package/adapters/claude/references/verifier-patterns/pattern_deviation_judgment.md +36 -0
  23. package/adapters/claude/references/verifier-patterns/pattern_enum_vocabulary_ripple.md +43 -0
  24. package/adapters/claude/references/verifier-patterns/pattern_extend_the_existing.md +31 -0
  25. package/adapters/claude/references/verifier-patterns/pattern_guard_substitution_detection.md +34 -0
  26. package/adapters/claude/references/verifier-patterns/pattern_mechanical_rewrite_damage.md +30 -0
  27. package/adapters/claude/references/verifier-patterns/pattern_postfix_reverify.md +82 -0
  28. package/adapters/claude/references/verifier-patterns/pattern_proximity_window_guards.md +26 -0
  29. package/adapters/claude/references/verifier-patterns/pattern_scratch_root_probes.md +40 -0
  30. package/adapters/claude/references/verifier-patterns/pattern_self_referential_control.md +26 -0
  31. package/adapters/claude/references/verifier-patterns/pattern_sibling_docstring_sweep.md +28 -0
  32. package/adapters/claude/references/verifier-patterns/pattern_spec_literals_are_claims.md +45 -0
  33. package/adapters/claude/references/verifier-patterns/pattern_stale_counts_after_split.md +36 -0
  34. package/adapters/claude/references/verifier-patterns/pattern_test_count_units.md +20 -0
  35. package/adapters/claude/references/verifier-patterns/pattern_vacuous_self_reading_tests.md +32 -0
  36. package/adapters/claude/references/verify-state.md +98 -0
  37. package/adapters/claude/scripts/forge-bootstrap.py +23 -2
  38. package/adapters/claude/scripts/forge-root.sh +104 -21
  39. package/adapters/claude/scripts/forge-session.py +620 -6973
  40. package/adapters/claude/scripts/forge_session/__init__.py +11 -0
  41. package/adapters/claude/scripts/forge_session/_common.py +1566 -0
  42. package/adapters/claude/scripts/forge_session/_doctor_util.py +218 -0
  43. package/adapters/claude/scripts/forge_session/cli.py +1067 -0
  44. package/adapters/claude/scripts/forge_session/decisions.py +355 -0
  45. package/adapters/claude/scripts/forge_session/discover.py +472 -0
  46. package/adapters/claude/scripts/forge_session/doctor.py +2144 -0
  47. package/adapters/claude/scripts/forge_session/exit.py +1081 -0
  48. package/adapters/claude/scripts/forge_session/outcomes.py +574 -0
  49. package/adapters/claude/scripts/forge_session/routes.py +1162 -0
  50. package/adapters/claude/scripts/forge_session/state.py +1479 -0
  51. package/adapters/claude/scripts/forge_session/topology.py +443 -0
  52. package/adapters/claude/skills/forge/SKILL.md +7 -7
  53. package/adapters/claude/skills/forge/references/process-overview.md +1 -1
  54. package/adapters/claude/skills/forge/references/shared-conventions.md +175 -22
  55. package/adapters/claude/skills/forge/references/stage-exit-protocol.md +19 -4
  56. package/adapters/claude/skills/forge-0-epic/SKILL.md +7 -7
  57. package/adapters/claude/skills/forge-0-epic/references/edit-mode.md +9 -5
  58. package/adapters/claude/skills/forge-0-epic/references/epic-manifest-subcommands.md +6 -1
  59. package/adapters/claude/skills/forge-0-epic/references/portable-root.md +20 -16
  60. package/adapters/claude/skills/forge-0-epic/references/shared-conventions.md +175 -22
  61. package/adapters/claude/skills/forge-0-epic/references/stage-exit-protocol.md +19 -4
  62. package/adapters/claude/skills/forge-1-prd/SKILL.md +7 -7
  63. package/adapters/claude/skills/forge-1-prd/references/shared-conventions.md +175 -22
  64. package/adapters/claude/skills/forge-1-prd/references/stage-exit-protocol.md +19 -4
  65. package/adapters/claude/skills/forge-2-tech/SKILL.md +7 -7
  66. package/adapters/claude/skills/forge-2-tech/references/shared-conventions.md +175 -22
  67. package/adapters/claude/skills/forge-2-tech/references/stack-resolution.md +1 -1
  68. package/adapters/claude/skills/forge-2-tech/references/stage-exit-protocol.md +19 -4
  69. package/adapters/claude/skills/forge-3-specs/SKILL.md +5 -5
  70. package/adapters/claude/skills/forge-3-specs/references/shared-conventions.md +175 -22
  71. package/adapters/claude/skills/forge-3-specs/references/stage-exit-protocol.md +19 -4
  72. package/adapters/claude/skills/forge-4-backlog/SKILL.md +16 -8
  73. package/adapters/claude/skills/forge-4-backlog/references/shared-conventions.md +175 -22
  74. package/adapters/claude/skills/forge-4-backlog/references/stage-exit-protocol.md +19 -4
  75. package/adapters/claude/skills/forge-4-backlog/references/verify-state.md +98 -0
  76. package/adapters/claude/skills/forge-5-loop/SKILL.md +36 -41
  77. package/adapters/claude/skills/forge-5-loop/references/agent-selection.md +4 -0
  78. package/adapters/claude/skills/forge-5-loop/references/preflight-and-self-heal.md +183 -0
  79. package/adapters/claude/skills/forge-5-loop/references/recovery-procedure.md +10 -0
  80. package/adapters/claude/skills/forge-5-loop/references/runner-contract.md +15 -5
  81. package/adapters/claude/skills/forge-5-loop/references/shared-conventions.md +175 -22
  82. package/adapters/claude/skills/forge-5-loop/references/stage-exit-protocol.md +19 -4
  83. package/adapters/claude/skills/forge-5-loop/references/verify-state.md +98 -0
  84. package/adapters/claude/skills/forge-6-docs/SKILL.md +21 -14
  85. package/adapters/claude/skills/forge-6-docs/references/shared-conventions.md +175 -22
  86. package/adapters/claude/skills/forge-6-docs/references/stage-exit-protocol.md +19 -4
  87. package/adapters/claude/skills/forge-6-docs/references/verify-state.md +98 -0
  88. package/adapters/claude/skills/forge-bootstrap/SKILL.md +19 -10
  89. package/adapters/claude/skills/forge-bootstrap/references/shared-conventions.md +572 -0
  90. package/adapters/claude/skills/forge-fix/SKILL.md +15 -18
  91. package/adapters/claude/skills/forge-fix/references/select-outcome.md +138 -0
  92. package/adapters/claude/skills/forge-fix/references/shared-conventions.md +175 -22
  93. package/adapters/claude/skills/forge-fix/references/stage-exit-protocol.md +19 -4
  94. package/adapters/claude/skills/forge-guide/SKILL.md +87 -1
  95. package/adapters/claude/skills/forge-guide/references/forge-config-schema.json +3 -3
  96. package/adapters/claude/skills/forge-guide/references/preflight-and-self-heal.md +183 -0
  97. package/adapters/claude/skills/forge-guide/references/process-overview.md +1 -1
  98. package/adapters/claude/skills/forge-guide/references/shared-conventions.md +175 -22
  99. package/adapters/claude/skills/forge-guide/references/stack-resolution.md +1 -1
  100. package/adapters/claude/skills/forge-init/SKILL.md +58 -7
  101. package/adapters/claude/skills/forge-init/references/preflight-and-self-heal.md +183 -0
  102. package/adapters/claude/skills/forge-init/references/shared-conventions.md +572 -0
  103. package/adapters/claude/skills/forge-verify/SKILL.md +20 -23
  104. package/adapters/claude/skills/forge-verify/references/findings-template.md +2 -2
  105. package/adapters/claude/skills/forge-verify/references/select-outcome.md +138 -0
  106. package/adapters/claude/skills/forge-verify/references/shared-conventions.md +175 -22
  107. package/adapters/claude/skills/forge-verify/references/stage-exit-protocol.md +19 -4
  108. package/adapters/claude/skills/forge-verify/references/verification-checklists/epic.md +1 -1
  109. package/adapters/claude/skills/forge-verify/references/verifier-patterns/MEMORY.md +35 -0
  110. package/adapters/claude/skills/forge-verify/references/verifier-patterns/pattern_absence_claims.md +36 -0
  111. package/adapters/claude/skills/forge-verify/references/verifier-patterns/pattern_carveout_sibling_semantics.md +34 -0
  112. package/adapters/claude/skills/forge-verify/references/verifier-patterns/pattern_deviation_judgment.md +36 -0
  113. package/adapters/claude/skills/forge-verify/references/verifier-patterns/pattern_enum_vocabulary_ripple.md +43 -0
  114. package/adapters/claude/skills/forge-verify/references/verifier-patterns/pattern_extend_the_existing.md +31 -0
  115. package/adapters/claude/skills/forge-verify/references/verifier-patterns/pattern_guard_substitution_detection.md +34 -0
  116. package/adapters/claude/skills/forge-verify/references/verifier-patterns/pattern_mechanical_rewrite_damage.md +30 -0
  117. package/adapters/claude/skills/forge-verify/references/verifier-patterns/pattern_postfix_reverify.md +82 -0
  118. package/adapters/claude/skills/forge-verify/references/verifier-patterns/pattern_proximity_window_guards.md +26 -0
  119. package/adapters/claude/skills/forge-verify/references/verifier-patterns/pattern_scratch_root_probes.md +40 -0
  120. package/adapters/claude/skills/forge-verify/references/verifier-patterns/pattern_self_referential_control.md +26 -0
  121. package/adapters/claude/skills/forge-verify/references/verifier-patterns/pattern_sibling_docstring_sweep.md +28 -0
  122. package/adapters/claude/skills/forge-verify/references/verifier-patterns/pattern_spec_literals_are_claims.md +45 -0
  123. package/adapters/claude/skills/forge-verify/references/verifier-patterns/pattern_stale_counts_after_split.md +36 -0
  124. package/adapters/claude/skills/forge-verify/references/verifier-patterns/pattern_test_count_units.md +20 -0
  125. package/adapters/claude/skills/forge-verify/references/verifier-patterns/pattern_vacuous_self_reading_tests.md +32 -0
  126. package/adapters/codex/.feature-forge-bundle.json +1 -1
  127. package/adapters/codex/agents/forge-researcher.toml +1 -1
  128. package/adapters/codex/agents/forge-verifier.toml +17 -6
  129. package/adapters/codex/references/forge-config-schema.json +5 -5
  130. package/adapters/codex/references/portable-root.md +20 -16
  131. package/adapters/codex/references/preflight-and-self-heal.md +183 -0
  132. package/adapters/codex/references/process-overview.md +10 -10
  133. package/adapters/codex/references/select-outcome.md +138 -0
  134. package/adapters/codex/references/shared-conventions.md +188 -35
  135. package/adapters/codex/references/skill-frontmatter.schema.json +3 -1
  136. package/adapters/codex/references/stack-resolution.md +1 -1
  137. package/adapters/codex/references/stage-exit-protocol.md +22 -7
  138. package/adapters/codex/references/templates/root-hygiene/AGENTS.md +14 -0
  139. package/adapters/codex/references/templates/root-hygiene/CLAUDE.md +14 -0
  140. package/adapters/codex/references/templates/specs-hygiene/AGENTS.md +3 -1
  141. package/adapters/codex/references/templates/specs-hygiene/CLAUDE.md +2 -1
  142. package/adapters/codex/references/verifier-patterns/MEMORY.md +35 -0
  143. package/adapters/codex/references/verifier-patterns/pattern_absence_claims.md +36 -0
  144. package/adapters/codex/references/verifier-patterns/pattern_carveout_sibling_semantics.md +34 -0
  145. package/adapters/codex/references/verifier-patterns/pattern_deviation_judgment.md +36 -0
  146. package/adapters/codex/references/verifier-patterns/pattern_enum_vocabulary_ripple.md +43 -0
  147. package/adapters/codex/references/verifier-patterns/pattern_extend_the_existing.md +31 -0
  148. package/adapters/codex/references/verifier-patterns/pattern_guard_substitution_detection.md +34 -0
  149. package/adapters/codex/references/verifier-patterns/pattern_mechanical_rewrite_damage.md +30 -0
  150. package/adapters/codex/references/verifier-patterns/pattern_postfix_reverify.md +82 -0
  151. package/adapters/codex/references/verifier-patterns/pattern_proximity_window_guards.md +26 -0
  152. package/adapters/codex/references/verifier-patterns/pattern_scratch_root_probes.md +40 -0
  153. package/adapters/codex/references/verifier-patterns/pattern_self_referential_control.md +26 -0
  154. package/adapters/codex/references/verifier-patterns/pattern_sibling_docstring_sweep.md +28 -0
  155. package/adapters/codex/references/verifier-patterns/pattern_spec_literals_are_claims.md +45 -0
  156. package/adapters/codex/references/verifier-patterns/pattern_stale_counts_after_split.md +36 -0
  157. package/adapters/codex/references/verifier-patterns/pattern_test_count_units.md +20 -0
  158. package/adapters/codex/references/verifier-patterns/pattern_vacuous_self_reading_tests.md +32 -0
  159. package/adapters/codex/references/verify-state.md +98 -0
  160. package/adapters/codex/scripts/forge-bootstrap.py +23 -2
  161. package/adapters/codex/scripts/forge-root.sh +104 -21
  162. package/adapters/codex/scripts/forge-session.py +620 -6973
  163. package/adapters/codex/scripts/forge_session/__init__.py +11 -0
  164. package/adapters/codex/scripts/forge_session/_common.py +1566 -0
  165. package/adapters/codex/scripts/forge_session/_doctor_util.py +218 -0
  166. package/adapters/codex/scripts/forge_session/cli.py +1067 -0
  167. package/adapters/codex/scripts/forge_session/decisions.py +355 -0
  168. package/adapters/codex/scripts/forge_session/discover.py +472 -0
  169. package/adapters/codex/scripts/forge_session/doctor.py +2144 -0
  170. package/adapters/codex/scripts/forge_session/exit.py +1081 -0
  171. package/adapters/codex/scripts/forge_session/outcomes.py +574 -0
  172. package/adapters/codex/scripts/forge_session/routes.py +1162 -0
  173. package/adapters/codex/scripts/forge_session/state.py +1479 -0
  174. package/adapters/codex/scripts/forge_session/topology.py +443 -0
  175. package/adapters/codex/skills/forge/SKILL.md +40 -40
  176. package/adapters/codex/skills/forge/references/process-overview.md +10 -10
  177. package/adapters/codex/skills/forge/references/shared-conventions.md +188 -35
  178. package/adapters/codex/skills/forge/references/stage-exit-protocol.md +22 -7
  179. package/adapters/codex/skills/forge-0-epic/SKILL.md +15 -15
  180. package/adapters/codex/skills/forge-0-epic/references/edit-mode.md +14 -10
  181. package/adapters/codex/skills/forge-0-epic/references/epic-manifest-subcommands.md +8 -3
  182. package/adapters/codex/skills/forge-0-epic/references/portable-root.md +20 -16
  183. package/adapters/codex/skills/forge-0-epic/references/shared-conventions.md +188 -35
  184. package/adapters/codex/skills/forge-0-epic/references/stage-exit-protocol.md +22 -7
  185. package/adapters/codex/skills/forge-1-prd/SKILL.md +15 -15
  186. package/adapters/codex/skills/forge-1-prd/references/shared-conventions.md +188 -35
  187. package/adapters/codex/skills/forge-1-prd/references/stage-exit-protocol.md +22 -7
  188. package/adapters/codex/skills/forge-2-tech/SKILL.md +15 -15
  189. package/adapters/codex/skills/forge-2-tech/references/shared-conventions.md +188 -35
  190. package/adapters/codex/skills/forge-2-tech/references/stack-resolution.md +1 -1
  191. package/adapters/codex/skills/forge-2-tech/references/stage-exit-protocol.md +22 -7
  192. package/adapters/codex/skills/forge-3-specs/SKILL.md +10 -10
  193. package/adapters/codex/skills/forge-3-specs/references/shared-conventions.md +188 -35
  194. package/adapters/codex/skills/forge-3-specs/references/stage-exit-protocol.md +22 -7
  195. package/adapters/codex/skills/forge-4-backlog/SKILL.md +19 -11
  196. package/adapters/codex/skills/forge-4-backlog/references/shared-conventions.md +188 -35
  197. package/adapters/codex/skills/forge-4-backlog/references/stage-exit-protocol.md +22 -7
  198. package/adapters/codex/skills/forge-4-backlog/references/verify-state.md +98 -0
  199. package/adapters/codex/skills/forge-5-loop/SKILL.md +48 -53
  200. package/adapters/codex/skills/forge-5-loop/references/agent-selection.md +5 -1
  201. package/adapters/codex/skills/forge-5-loop/references/preflight-and-self-heal.md +183 -0
  202. package/adapters/codex/skills/forge-5-loop/references/recovery-procedure.md +14 -4
  203. package/adapters/codex/skills/forge-5-loop/references/runner-contract.md +18 -8
  204. package/adapters/codex/skills/forge-5-loop/references/shared-conventions.md +188 -35
  205. package/adapters/codex/skills/forge-5-loop/references/stage-exit-protocol.md +22 -7
  206. package/adapters/codex/skills/forge-5-loop/references/verify-state.md +98 -0
  207. package/adapters/codex/skills/forge-6-docs/SKILL.md +27 -20
  208. package/adapters/codex/skills/forge-6-docs/references/shared-conventions.md +188 -35
  209. package/adapters/codex/skills/forge-6-docs/references/stage-exit-protocol.md +22 -7
  210. package/adapters/codex/skills/forge-6-docs/references/verify-state.md +98 -0
  211. package/adapters/codex/skills/forge-bootstrap/SKILL.md +23 -15
  212. package/adapters/codex/skills/forge-bootstrap/references/shared-conventions.md +572 -0
  213. package/adapters/codex/skills/forge-bootstrap/references/templates/hygiene/CLAUDE.md +2 -2
  214. package/adapters/codex/skills/forge-fix/SKILL.md +27 -30
  215. package/adapters/codex/skills/forge-fix/references/select-outcome.md +138 -0
  216. package/adapters/codex/skills/forge-fix/references/shared-conventions.md +188 -35
  217. package/adapters/codex/skills/forge-fix/references/stage-exit-protocol.md +22 -7
  218. package/adapters/codex/skills/forge-guide/SKILL.md +84 -7
  219. package/adapters/codex/skills/forge-guide/references/forge-config-schema.json +5 -5
  220. package/adapters/codex/skills/forge-guide/references/preflight-and-self-heal.md +183 -0
  221. package/adapters/codex/skills/forge-guide/references/process-overview.md +10 -10
  222. package/adapters/codex/skills/forge-guide/references/shared-conventions.md +188 -35
  223. package/adapters/codex/skills/forge-guide/references/stack-resolution.md +1 -1
  224. package/adapters/codex/skills/forge-init/SKILL.md +62 -12
  225. package/adapters/codex/skills/forge-init/references/preflight-and-self-heal.md +183 -0
  226. package/adapters/codex/skills/forge-init/references/shared-conventions.md +572 -0
  227. package/adapters/codex/skills/forge-verify/SKILL.md +27 -30
  228. package/adapters/codex/skills/forge-verify/references/findings-template.md +2 -2
  229. package/adapters/codex/skills/forge-verify/references/select-outcome.md +138 -0
  230. package/adapters/codex/skills/forge-verify/references/shared-conventions.md +188 -35
  231. package/adapters/codex/skills/forge-verify/references/stage-exit-protocol.md +22 -7
  232. package/adapters/codex/skills/forge-verify/references/verification-checklists/epic.md +2 -2
  233. package/adapters/codex/skills/forge-verify/references/verifier-patterns/MEMORY.md +35 -0
  234. package/adapters/codex/skills/forge-verify/references/verifier-patterns/pattern_absence_claims.md +36 -0
  235. package/adapters/codex/skills/forge-verify/references/verifier-patterns/pattern_carveout_sibling_semantics.md +34 -0
  236. package/adapters/codex/skills/forge-verify/references/verifier-patterns/pattern_deviation_judgment.md +36 -0
  237. package/adapters/codex/skills/forge-verify/references/verifier-patterns/pattern_enum_vocabulary_ripple.md +43 -0
  238. package/adapters/codex/skills/forge-verify/references/verifier-patterns/pattern_extend_the_existing.md +31 -0
  239. package/adapters/codex/skills/forge-verify/references/verifier-patterns/pattern_guard_substitution_detection.md +34 -0
  240. package/adapters/codex/skills/forge-verify/references/verifier-patterns/pattern_mechanical_rewrite_damage.md +30 -0
  241. package/adapters/codex/skills/forge-verify/references/verifier-patterns/pattern_postfix_reverify.md +82 -0
  242. package/adapters/codex/skills/forge-verify/references/verifier-patterns/pattern_proximity_window_guards.md +26 -0
  243. package/adapters/codex/skills/forge-verify/references/verifier-patterns/pattern_scratch_root_probes.md +40 -0
  244. package/adapters/codex/skills/forge-verify/references/verifier-patterns/pattern_self_referential_control.md +26 -0
  245. package/adapters/codex/skills/forge-verify/references/verifier-patterns/pattern_sibling_docstring_sweep.md +28 -0
  246. package/adapters/codex/skills/forge-verify/references/verifier-patterns/pattern_spec_literals_are_claims.md +45 -0
  247. package/adapters/codex/skills/forge-verify/references/verifier-patterns/pattern_stale_counts_after_split.md +36 -0
  248. package/adapters/codex/skills/forge-verify/references/verifier-patterns/pattern_test_count_units.md +20 -0
  249. package/adapters/codex/skills/forge-verify/references/verifier-patterns/pattern_vacuous_self_reading_tests.md +32 -0
  250. package/adapters/copilot/.feature-forge-bundle.json +1 -1
  251. package/adapters/copilot/agents/forge-researcher.md +1 -1
  252. package/adapters/copilot/agents/forge-verifier.md +17 -6
  253. package/adapters/copilot/references/forge-config-schema.json +5 -5
  254. package/adapters/copilot/references/portable-root.md +20 -16
  255. package/adapters/copilot/references/preflight-and-self-heal.md +183 -0
  256. package/adapters/copilot/references/process-overview.md +10 -10
  257. package/adapters/copilot/references/select-outcome.md +138 -0
  258. package/adapters/copilot/references/shared-conventions.md +188 -35
  259. package/adapters/copilot/references/skill-frontmatter.schema.json +3 -1
  260. package/adapters/copilot/references/stack-resolution.md +1 -1
  261. package/adapters/copilot/references/stage-exit-protocol.md +22 -7
  262. package/adapters/copilot/references/templates/root-hygiene/AGENTS.md +14 -0
  263. package/adapters/copilot/references/templates/root-hygiene/CLAUDE.md +14 -0
  264. package/adapters/copilot/references/templates/specs-hygiene/AGENTS.md +3 -1
  265. package/adapters/copilot/references/templates/specs-hygiene/CLAUDE.md +2 -1
  266. package/adapters/copilot/references/verifier-patterns/MEMORY.md +35 -0
  267. package/adapters/copilot/references/verifier-patterns/pattern_absence_claims.md +36 -0
  268. package/adapters/copilot/references/verifier-patterns/pattern_carveout_sibling_semantics.md +34 -0
  269. package/adapters/copilot/references/verifier-patterns/pattern_deviation_judgment.md +36 -0
  270. package/adapters/copilot/references/verifier-patterns/pattern_enum_vocabulary_ripple.md +43 -0
  271. package/adapters/copilot/references/verifier-patterns/pattern_extend_the_existing.md +31 -0
  272. package/adapters/copilot/references/verifier-patterns/pattern_guard_substitution_detection.md +34 -0
  273. package/adapters/copilot/references/verifier-patterns/pattern_mechanical_rewrite_damage.md +30 -0
  274. package/adapters/copilot/references/verifier-patterns/pattern_postfix_reverify.md +82 -0
  275. package/adapters/copilot/references/verifier-patterns/pattern_proximity_window_guards.md +26 -0
  276. package/adapters/copilot/references/verifier-patterns/pattern_scratch_root_probes.md +40 -0
  277. package/adapters/copilot/references/verifier-patterns/pattern_self_referential_control.md +26 -0
  278. package/adapters/copilot/references/verifier-patterns/pattern_sibling_docstring_sweep.md +28 -0
  279. package/adapters/copilot/references/verifier-patterns/pattern_spec_literals_are_claims.md +45 -0
  280. package/adapters/copilot/references/verifier-patterns/pattern_stale_counts_after_split.md +36 -0
  281. package/adapters/copilot/references/verifier-patterns/pattern_test_count_units.md +20 -0
  282. package/adapters/copilot/references/verifier-patterns/pattern_vacuous_self_reading_tests.md +32 -0
  283. package/adapters/copilot/references/verify-state.md +98 -0
  284. package/adapters/copilot/scripts/forge-bootstrap.py +23 -2
  285. package/adapters/copilot/scripts/forge-root.sh +104 -21
  286. package/adapters/copilot/scripts/forge-session.py +620 -6973
  287. package/adapters/copilot/scripts/forge_session/__init__.py +11 -0
  288. package/adapters/copilot/scripts/forge_session/_common.py +1566 -0
  289. package/adapters/copilot/scripts/forge_session/_doctor_util.py +218 -0
  290. package/adapters/copilot/scripts/forge_session/cli.py +1067 -0
  291. package/adapters/copilot/scripts/forge_session/decisions.py +355 -0
  292. package/adapters/copilot/scripts/forge_session/discover.py +472 -0
  293. package/adapters/copilot/scripts/forge_session/doctor.py +2144 -0
  294. package/adapters/copilot/scripts/forge_session/exit.py +1081 -0
  295. package/adapters/copilot/scripts/forge_session/outcomes.py +574 -0
  296. package/adapters/copilot/scripts/forge_session/routes.py +1162 -0
  297. package/adapters/copilot/scripts/forge_session/state.py +1479 -0
  298. package/adapters/copilot/scripts/forge_session/topology.py +443 -0
  299. package/adapters/copilot/skills/forge/forge.md +40 -40
  300. package/adapters/copilot/skills/forge/references/process-overview.md +10 -10
  301. package/adapters/copilot/skills/forge/references/shared-conventions.md +188 -35
  302. package/adapters/copilot/skills/forge/references/stage-exit-protocol.md +22 -7
  303. package/adapters/copilot/skills/forge-0-epic/forge-0-epic.md +15 -15
  304. package/adapters/copilot/skills/forge-0-epic/references/edit-mode.md +14 -10
  305. package/adapters/copilot/skills/forge-0-epic/references/epic-manifest-subcommands.md +8 -3
  306. package/adapters/copilot/skills/forge-0-epic/references/portable-root.md +20 -16
  307. package/adapters/copilot/skills/forge-0-epic/references/shared-conventions.md +188 -35
  308. package/adapters/copilot/skills/forge-0-epic/references/stage-exit-protocol.md +22 -7
  309. package/adapters/copilot/skills/forge-1-prd/forge-1-prd.md +15 -15
  310. package/adapters/copilot/skills/forge-1-prd/references/shared-conventions.md +188 -35
  311. package/adapters/copilot/skills/forge-1-prd/references/stage-exit-protocol.md +22 -7
  312. package/adapters/copilot/skills/forge-2-tech/forge-2-tech.md +15 -15
  313. package/adapters/copilot/skills/forge-2-tech/references/shared-conventions.md +188 -35
  314. package/adapters/copilot/skills/forge-2-tech/references/stack-resolution.md +1 -1
  315. package/adapters/copilot/skills/forge-2-tech/references/stage-exit-protocol.md +22 -7
  316. package/adapters/copilot/skills/forge-3-specs/forge-3-specs.md +10 -10
  317. package/adapters/copilot/skills/forge-3-specs/references/shared-conventions.md +188 -35
  318. package/adapters/copilot/skills/forge-3-specs/references/stage-exit-protocol.md +22 -7
  319. package/adapters/copilot/skills/forge-4-backlog/forge-4-backlog.md +19 -11
  320. package/adapters/copilot/skills/forge-4-backlog/references/shared-conventions.md +188 -35
  321. package/adapters/copilot/skills/forge-4-backlog/references/stage-exit-protocol.md +22 -7
  322. package/adapters/copilot/skills/forge-4-backlog/references/verify-state.md +98 -0
  323. package/adapters/copilot/skills/forge-5-loop/forge-5-loop.md +48 -53
  324. package/adapters/copilot/skills/forge-5-loop/references/agent-selection.md +5 -1
  325. package/adapters/copilot/skills/forge-5-loop/references/preflight-and-self-heal.md +183 -0
  326. package/adapters/copilot/skills/forge-5-loop/references/recovery-procedure.md +14 -4
  327. package/adapters/copilot/skills/forge-5-loop/references/runner-contract.md +18 -8
  328. package/adapters/copilot/skills/forge-5-loop/references/shared-conventions.md +188 -35
  329. package/adapters/copilot/skills/forge-5-loop/references/stage-exit-protocol.md +22 -7
  330. package/adapters/copilot/skills/forge-5-loop/references/verify-state.md +98 -0
  331. package/adapters/copilot/skills/forge-6-docs/forge-6-docs.md +27 -20
  332. package/adapters/copilot/skills/forge-6-docs/references/shared-conventions.md +188 -35
  333. package/adapters/copilot/skills/forge-6-docs/references/stage-exit-protocol.md +22 -7
  334. package/adapters/copilot/skills/forge-6-docs/references/verify-state.md +98 -0
  335. package/adapters/copilot/skills/forge-bootstrap/forge-bootstrap.md +23 -15
  336. package/adapters/copilot/skills/forge-bootstrap/references/shared-conventions.md +572 -0
  337. package/adapters/copilot/skills/forge-bootstrap/references/templates/hygiene/CLAUDE.md +2 -2
  338. package/adapters/copilot/skills/forge-fix/forge-fix.md +27 -30
  339. package/adapters/copilot/skills/forge-fix/references/select-outcome.md +138 -0
  340. package/adapters/copilot/skills/forge-fix/references/shared-conventions.md +188 -35
  341. package/adapters/copilot/skills/forge-fix/references/stage-exit-protocol.md +22 -7
  342. package/adapters/copilot/skills/forge-guide/forge-guide.md +84 -7
  343. package/adapters/copilot/skills/forge-guide/references/forge-config-schema.json +5 -5
  344. package/adapters/copilot/skills/forge-guide/references/preflight-and-self-heal.md +183 -0
  345. package/adapters/copilot/skills/forge-guide/references/process-overview.md +10 -10
  346. package/adapters/copilot/skills/forge-guide/references/shared-conventions.md +188 -35
  347. package/adapters/copilot/skills/forge-guide/references/stack-resolution.md +1 -1
  348. package/adapters/copilot/skills/forge-init/forge-init.md +62 -12
  349. package/adapters/copilot/skills/forge-init/references/preflight-and-self-heal.md +183 -0
  350. package/adapters/copilot/skills/forge-init/references/shared-conventions.md +572 -0
  351. package/adapters/copilot/skills/forge-verify/forge-verify.md +27 -30
  352. package/adapters/copilot/skills/forge-verify/references/findings-template.md +2 -2
  353. package/adapters/copilot/skills/forge-verify/references/select-outcome.md +138 -0
  354. package/adapters/copilot/skills/forge-verify/references/shared-conventions.md +188 -35
  355. package/adapters/copilot/skills/forge-verify/references/stage-exit-protocol.md +22 -7
  356. package/adapters/copilot/skills/forge-verify/references/verification-checklists/epic.md +2 -2
  357. package/adapters/copilot/skills/forge-verify/references/verifier-patterns/MEMORY.md +35 -0
  358. package/adapters/copilot/skills/forge-verify/references/verifier-patterns/pattern_absence_claims.md +36 -0
  359. package/adapters/copilot/skills/forge-verify/references/verifier-patterns/pattern_carveout_sibling_semantics.md +34 -0
  360. package/adapters/copilot/skills/forge-verify/references/verifier-patterns/pattern_deviation_judgment.md +36 -0
  361. package/adapters/copilot/skills/forge-verify/references/verifier-patterns/pattern_enum_vocabulary_ripple.md +43 -0
  362. package/adapters/copilot/skills/forge-verify/references/verifier-patterns/pattern_extend_the_existing.md +31 -0
  363. package/adapters/copilot/skills/forge-verify/references/verifier-patterns/pattern_guard_substitution_detection.md +34 -0
  364. package/adapters/copilot/skills/forge-verify/references/verifier-patterns/pattern_mechanical_rewrite_damage.md +30 -0
  365. package/adapters/copilot/skills/forge-verify/references/verifier-patterns/pattern_postfix_reverify.md +82 -0
  366. package/adapters/copilot/skills/forge-verify/references/verifier-patterns/pattern_proximity_window_guards.md +26 -0
  367. package/adapters/copilot/skills/forge-verify/references/verifier-patterns/pattern_scratch_root_probes.md +40 -0
  368. package/adapters/copilot/skills/forge-verify/references/verifier-patterns/pattern_self_referential_control.md +26 -0
  369. package/adapters/copilot/skills/forge-verify/references/verifier-patterns/pattern_sibling_docstring_sweep.md +28 -0
  370. package/adapters/copilot/skills/forge-verify/references/verifier-patterns/pattern_spec_literals_are_claims.md +45 -0
  371. package/adapters/copilot/skills/forge-verify/references/verifier-patterns/pattern_stale_counts_after_split.md +36 -0
  372. package/adapters/copilot/skills/forge-verify/references/verifier-patterns/pattern_test_count_units.md +20 -0
  373. package/adapters/copilot/skills/forge-verify/references/verifier-patterns/pattern_vacuous_self_reading_tests.md +32 -0
  374. package/adapters/cursor/.feature-forge-bundle.json +1 -1
  375. package/adapters/cursor/agents/forge-researcher.mdc +1 -1
  376. package/adapters/cursor/agents/forge-verifier.mdc +17 -6
  377. package/adapters/cursor/references/forge-config-schema.json +5 -5
  378. package/adapters/cursor/references/portable-root.md +20 -16
  379. package/adapters/cursor/references/preflight-and-self-heal.md +183 -0
  380. package/adapters/cursor/references/process-overview.md +10 -10
  381. package/adapters/cursor/references/select-outcome.md +138 -0
  382. package/adapters/cursor/references/shared-conventions.md +188 -35
  383. package/adapters/cursor/references/skill-frontmatter.schema.json +3 -1
  384. package/adapters/cursor/references/stack-resolution.md +1 -1
  385. package/adapters/cursor/references/stage-exit-protocol.md +22 -7
  386. package/adapters/cursor/references/templates/root-hygiene/AGENTS.md +14 -0
  387. package/adapters/cursor/references/templates/root-hygiene/CLAUDE.md +14 -0
  388. package/adapters/cursor/references/templates/specs-hygiene/AGENTS.md +3 -1
  389. package/adapters/cursor/references/templates/specs-hygiene/CLAUDE.md +2 -1
  390. package/adapters/cursor/references/verifier-patterns/MEMORY.md +35 -0
  391. package/adapters/cursor/references/verifier-patterns/pattern_absence_claims.md +36 -0
  392. package/adapters/cursor/references/verifier-patterns/pattern_carveout_sibling_semantics.md +34 -0
  393. package/adapters/cursor/references/verifier-patterns/pattern_deviation_judgment.md +36 -0
  394. package/adapters/cursor/references/verifier-patterns/pattern_enum_vocabulary_ripple.md +43 -0
  395. package/adapters/cursor/references/verifier-patterns/pattern_extend_the_existing.md +31 -0
  396. package/adapters/cursor/references/verifier-patterns/pattern_guard_substitution_detection.md +34 -0
  397. package/adapters/cursor/references/verifier-patterns/pattern_mechanical_rewrite_damage.md +30 -0
  398. package/adapters/cursor/references/verifier-patterns/pattern_postfix_reverify.md +82 -0
  399. package/adapters/cursor/references/verifier-patterns/pattern_proximity_window_guards.md +26 -0
  400. package/adapters/cursor/references/verifier-patterns/pattern_scratch_root_probes.md +40 -0
  401. package/adapters/cursor/references/verifier-patterns/pattern_self_referential_control.md +26 -0
  402. package/adapters/cursor/references/verifier-patterns/pattern_sibling_docstring_sweep.md +28 -0
  403. package/adapters/cursor/references/verifier-patterns/pattern_spec_literals_are_claims.md +45 -0
  404. package/adapters/cursor/references/verifier-patterns/pattern_stale_counts_after_split.md +36 -0
  405. package/adapters/cursor/references/verifier-patterns/pattern_test_count_units.md +20 -0
  406. package/adapters/cursor/references/verifier-patterns/pattern_vacuous_self_reading_tests.md +32 -0
  407. package/adapters/cursor/references/verify-state.md +98 -0
  408. package/adapters/cursor/scripts/forge-bootstrap.py +23 -2
  409. package/adapters/cursor/scripts/forge-root.sh +104 -21
  410. package/adapters/cursor/scripts/forge-session.py +620 -6973
  411. package/adapters/cursor/scripts/forge_session/__init__.py +11 -0
  412. package/adapters/cursor/scripts/forge_session/_common.py +1566 -0
  413. package/adapters/cursor/scripts/forge_session/_doctor_util.py +218 -0
  414. package/adapters/cursor/scripts/forge_session/cli.py +1067 -0
  415. package/adapters/cursor/scripts/forge_session/decisions.py +355 -0
  416. package/adapters/cursor/scripts/forge_session/discover.py +472 -0
  417. package/adapters/cursor/scripts/forge_session/doctor.py +2144 -0
  418. package/adapters/cursor/scripts/forge_session/exit.py +1081 -0
  419. package/adapters/cursor/scripts/forge_session/outcomes.py +574 -0
  420. package/adapters/cursor/scripts/forge_session/routes.py +1162 -0
  421. package/adapters/cursor/scripts/forge_session/state.py +1479 -0
  422. package/adapters/cursor/scripts/forge_session/topology.py +443 -0
  423. package/adapters/cursor/skills/forge/forge.mdc +40 -40
  424. package/adapters/cursor/skills/forge/references/process-overview.md +10 -10
  425. package/adapters/cursor/skills/forge/references/shared-conventions.md +188 -35
  426. package/adapters/cursor/skills/forge/references/stage-exit-protocol.md +22 -7
  427. package/adapters/cursor/skills/forge-0-epic/forge-0-epic.mdc +15 -15
  428. package/adapters/cursor/skills/forge-0-epic/references/edit-mode.md +14 -10
  429. package/adapters/cursor/skills/forge-0-epic/references/epic-manifest-subcommands.md +8 -3
  430. package/adapters/cursor/skills/forge-0-epic/references/portable-root.md +20 -16
  431. package/adapters/cursor/skills/forge-0-epic/references/shared-conventions.md +188 -35
  432. package/adapters/cursor/skills/forge-0-epic/references/stage-exit-protocol.md +22 -7
  433. package/adapters/cursor/skills/forge-1-prd/forge-1-prd.mdc +15 -15
  434. package/adapters/cursor/skills/forge-1-prd/references/shared-conventions.md +188 -35
  435. package/adapters/cursor/skills/forge-1-prd/references/stage-exit-protocol.md +22 -7
  436. package/adapters/cursor/skills/forge-2-tech/forge-2-tech.mdc +15 -15
  437. package/adapters/cursor/skills/forge-2-tech/references/shared-conventions.md +188 -35
  438. package/adapters/cursor/skills/forge-2-tech/references/stack-resolution.md +1 -1
  439. package/adapters/cursor/skills/forge-2-tech/references/stage-exit-protocol.md +22 -7
  440. package/adapters/cursor/skills/forge-3-specs/forge-3-specs.mdc +10 -10
  441. package/adapters/cursor/skills/forge-3-specs/references/shared-conventions.md +188 -35
  442. package/adapters/cursor/skills/forge-3-specs/references/stage-exit-protocol.md +22 -7
  443. package/adapters/cursor/skills/forge-4-backlog/forge-4-backlog.mdc +19 -11
  444. package/adapters/cursor/skills/forge-4-backlog/references/shared-conventions.md +188 -35
  445. package/adapters/cursor/skills/forge-4-backlog/references/stage-exit-protocol.md +22 -7
  446. package/adapters/cursor/skills/forge-4-backlog/references/verify-state.md +98 -0
  447. package/adapters/cursor/skills/forge-5-loop/forge-5-loop.mdc +48 -53
  448. package/adapters/cursor/skills/forge-5-loop/references/agent-selection.md +5 -1
  449. package/adapters/cursor/skills/forge-5-loop/references/preflight-and-self-heal.md +183 -0
  450. package/adapters/cursor/skills/forge-5-loop/references/recovery-procedure.md +14 -4
  451. package/adapters/cursor/skills/forge-5-loop/references/runner-contract.md +18 -8
  452. package/adapters/cursor/skills/forge-5-loop/references/shared-conventions.md +188 -35
  453. package/adapters/cursor/skills/forge-5-loop/references/stage-exit-protocol.md +22 -7
  454. package/adapters/cursor/skills/forge-5-loop/references/verify-state.md +98 -0
  455. package/adapters/cursor/skills/forge-6-docs/forge-6-docs.mdc +27 -20
  456. package/adapters/cursor/skills/forge-6-docs/references/shared-conventions.md +188 -35
  457. package/adapters/cursor/skills/forge-6-docs/references/stage-exit-protocol.md +22 -7
  458. package/adapters/cursor/skills/forge-6-docs/references/verify-state.md +98 -0
  459. package/adapters/cursor/skills/forge-bootstrap/forge-bootstrap.mdc +23 -15
  460. package/adapters/cursor/skills/forge-bootstrap/references/shared-conventions.md +572 -0
  461. package/adapters/cursor/skills/forge-bootstrap/references/templates/hygiene/CLAUDE.md +2 -2
  462. package/adapters/cursor/skills/forge-fix/forge-fix.mdc +27 -30
  463. package/adapters/cursor/skills/forge-fix/references/select-outcome.md +138 -0
  464. package/adapters/cursor/skills/forge-fix/references/shared-conventions.md +188 -35
  465. package/adapters/cursor/skills/forge-fix/references/stage-exit-protocol.md +22 -7
  466. package/adapters/cursor/skills/forge-guide/forge-guide.mdc +84 -7
  467. package/adapters/cursor/skills/forge-guide/references/forge-config-schema.json +5 -5
  468. package/adapters/cursor/skills/forge-guide/references/preflight-and-self-heal.md +183 -0
  469. package/adapters/cursor/skills/forge-guide/references/process-overview.md +10 -10
  470. package/adapters/cursor/skills/forge-guide/references/shared-conventions.md +188 -35
  471. package/adapters/cursor/skills/forge-guide/references/stack-resolution.md +1 -1
  472. package/adapters/cursor/skills/forge-init/forge-init.mdc +62 -12
  473. package/adapters/cursor/skills/forge-init/references/preflight-and-self-heal.md +183 -0
  474. package/adapters/cursor/skills/forge-init/references/shared-conventions.md +572 -0
  475. package/adapters/cursor/skills/forge-verify/forge-verify.mdc +27 -30
  476. package/adapters/cursor/skills/forge-verify/references/findings-template.md +2 -2
  477. package/adapters/cursor/skills/forge-verify/references/select-outcome.md +138 -0
  478. package/adapters/cursor/skills/forge-verify/references/shared-conventions.md +188 -35
  479. package/adapters/cursor/skills/forge-verify/references/stage-exit-protocol.md +22 -7
  480. package/adapters/cursor/skills/forge-verify/references/verification-checklists/epic.md +2 -2
  481. package/adapters/cursor/skills/forge-verify/references/verifier-patterns/MEMORY.md +35 -0
  482. package/adapters/cursor/skills/forge-verify/references/verifier-patterns/pattern_absence_claims.md +36 -0
  483. package/adapters/cursor/skills/forge-verify/references/verifier-patterns/pattern_carveout_sibling_semantics.md +34 -0
  484. package/adapters/cursor/skills/forge-verify/references/verifier-patterns/pattern_deviation_judgment.md +36 -0
  485. package/adapters/cursor/skills/forge-verify/references/verifier-patterns/pattern_enum_vocabulary_ripple.md +43 -0
  486. package/adapters/cursor/skills/forge-verify/references/verifier-patterns/pattern_extend_the_existing.md +31 -0
  487. package/adapters/cursor/skills/forge-verify/references/verifier-patterns/pattern_guard_substitution_detection.md +34 -0
  488. package/adapters/cursor/skills/forge-verify/references/verifier-patterns/pattern_mechanical_rewrite_damage.md +30 -0
  489. package/adapters/cursor/skills/forge-verify/references/verifier-patterns/pattern_postfix_reverify.md +82 -0
  490. package/adapters/cursor/skills/forge-verify/references/verifier-patterns/pattern_proximity_window_guards.md +26 -0
  491. package/adapters/cursor/skills/forge-verify/references/verifier-patterns/pattern_scratch_root_probes.md +40 -0
  492. package/adapters/cursor/skills/forge-verify/references/verifier-patterns/pattern_self_referential_control.md +26 -0
  493. package/adapters/cursor/skills/forge-verify/references/verifier-patterns/pattern_sibling_docstring_sweep.md +28 -0
  494. package/adapters/cursor/skills/forge-verify/references/verifier-patterns/pattern_spec_literals_are_claims.md +45 -0
  495. package/adapters/cursor/skills/forge-verify/references/verifier-patterns/pattern_stale_counts_after_split.md +36 -0
  496. package/adapters/cursor/skills/forge-verify/references/verifier-patterns/pattern_test_count_units.md +20 -0
  497. package/adapters/cursor/skills/forge-verify/references/verifier-patterns/pattern_vacuous_self_reading_tests.md +32 -0
  498. package/adapters/gemini/.feature-forge-bundle.json +1 -1
  499. package/adapters/gemini/agents/forge-researcher.md +1 -1
  500. package/adapters/gemini/agents/forge-verifier.md +17 -6
  501. package/adapters/gemini/gemini-extension.json +13 -13
  502. package/adapters/gemini/references/forge-config-schema.json +5 -5
  503. package/adapters/gemini/references/portable-root.md +20 -16
  504. package/adapters/gemini/references/preflight-and-self-heal.md +183 -0
  505. package/adapters/gemini/references/process-overview.md +10 -10
  506. package/adapters/gemini/references/select-outcome.md +138 -0
  507. package/adapters/gemini/references/shared-conventions.md +188 -35
  508. package/adapters/gemini/references/skill-frontmatter.schema.json +3 -1
  509. package/adapters/gemini/references/stack-resolution.md +1 -1
  510. package/adapters/gemini/references/stage-exit-protocol.md +22 -7
  511. package/adapters/gemini/references/templates/root-hygiene/AGENTS.md +14 -0
  512. package/adapters/gemini/references/templates/root-hygiene/CLAUDE.md +14 -0
  513. package/adapters/gemini/references/templates/specs-hygiene/AGENTS.md +3 -1
  514. package/adapters/gemini/references/templates/specs-hygiene/CLAUDE.md +2 -1
  515. package/adapters/gemini/references/verifier-patterns/MEMORY.md +35 -0
  516. package/adapters/gemini/references/verifier-patterns/pattern_absence_claims.md +36 -0
  517. package/adapters/gemini/references/verifier-patterns/pattern_carveout_sibling_semantics.md +34 -0
  518. package/adapters/gemini/references/verifier-patterns/pattern_deviation_judgment.md +36 -0
  519. package/adapters/gemini/references/verifier-patterns/pattern_enum_vocabulary_ripple.md +43 -0
  520. package/adapters/gemini/references/verifier-patterns/pattern_extend_the_existing.md +31 -0
  521. package/adapters/gemini/references/verifier-patterns/pattern_guard_substitution_detection.md +34 -0
  522. package/adapters/gemini/references/verifier-patterns/pattern_mechanical_rewrite_damage.md +30 -0
  523. package/adapters/gemini/references/verifier-patterns/pattern_postfix_reverify.md +82 -0
  524. package/adapters/gemini/references/verifier-patterns/pattern_proximity_window_guards.md +26 -0
  525. package/adapters/gemini/references/verifier-patterns/pattern_scratch_root_probes.md +40 -0
  526. package/adapters/gemini/references/verifier-patterns/pattern_self_referential_control.md +26 -0
  527. package/adapters/gemini/references/verifier-patterns/pattern_sibling_docstring_sweep.md +28 -0
  528. package/adapters/gemini/references/verifier-patterns/pattern_spec_literals_are_claims.md +45 -0
  529. package/adapters/gemini/references/verifier-patterns/pattern_stale_counts_after_split.md +36 -0
  530. package/adapters/gemini/references/verifier-patterns/pattern_test_count_units.md +20 -0
  531. package/adapters/gemini/references/verifier-patterns/pattern_vacuous_self_reading_tests.md +32 -0
  532. package/adapters/gemini/references/verify-state.md +98 -0
  533. package/adapters/gemini/scripts/forge-bootstrap.py +23 -2
  534. package/adapters/gemini/scripts/forge-root.sh +104 -21
  535. package/adapters/gemini/scripts/forge-session.py +620 -6973
  536. package/adapters/gemini/scripts/forge_session/__init__.py +11 -0
  537. package/adapters/gemini/scripts/forge_session/_common.py +1566 -0
  538. package/adapters/gemini/scripts/forge_session/_doctor_util.py +218 -0
  539. package/adapters/gemini/scripts/forge_session/cli.py +1067 -0
  540. package/adapters/gemini/scripts/forge_session/decisions.py +355 -0
  541. package/adapters/gemini/scripts/forge_session/discover.py +472 -0
  542. package/adapters/gemini/scripts/forge_session/doctor.py +2144 -0
  543. package/adapters/gemini/scripts/forge_session/exit.py +1081 -0
  544. package/adapters/gemini/scripts/forge_session/outcomes.py +574 -0
  545. package/adapters/gemini/scripts/forge_session/routes.py +1162 -0
  546. package/adapters/gemini/scripts/forge_session/state.py +1479 -0
  547. package/adapters/gemini/scripts/forge_session/topology.py +443 -0
  548. package/adapters/gemini/skills/forge/forge.md +40 -40
  549. package/adapters/gemini/skills/forge/references/process-overview.md +10 -10
  550. package/adapters/gemini/skills/forge/references/shared-conventions.md +188 -35
  551. package/adapters/gemini/skills/forge/references/stage-exit-protocol.md +22 -7
  552. package/adapters/gemini/skills/forge-0-epic/forge-0-epic.md +15 -15
  553. package/adapters/gemini/skills/forge-0-epic/references/edit-mode.md +14 -10
  554. package/adapters/gemini/skills/forge-0-epic/references/epic-manifest-subcommands.md +8 -3
  555. package/adapters/gemini/skills/forge-0-epic/references/portable-root.md +20 -16
  556. package/adapters/gemini/skills/forge-0-epic/references/shared-conventions.md +188 -35
  557. package/adapters/gemini/skills/forge-0-epic/references/stage-exit-protocol.md +22 -7
  558. package/adapters/gemini/skills/forge-1-prd/forge-1-prd.md +15 -15
  559. package/adapters/gemini/skills/forge-1-prd/references/shared-conventions.md +188 -35
  560. package/adapters/gemini/skills/forge-1-prd/references/stage-exit-protocol.md +22 -7
  561. package/adapters/gemini/skills/forge-2-tech/forge-2-tech.md +15 -15
  562. package/adapters/gemini/skills/forge-2-tech/references/shared-conventions.md +188 -35
  563. package/adapters/gemini/skills/forge-2-tech/references/stack-resolution.md +1 -1
  564. package/adapters/gemini/skills/forge-2-tech/references/stage-exit-protocol.md +22 -7
  565. package/adapters/gemini/skills/forge-3-specs/forge-3-specs.md +10 -10
  566. package/adapters/gemini/skills/forge-3-specs/references/shared-conventions.md +188 -35
  567. package/adapters/gemini/skills/forge-3-specs/references/stage-exit-protocol.md +22 -7
  568. package/adapters/gemini/skills/forge-4-backlog/forge-4-backlog.md +19 -11
  569. package/adapters/gemini/skills/forge-4-backlog/references/shared-conventions.md +188 -35
  570. package/adapters/gemini/skills/forge-4-backlog/references/stage-exit-protocol.md +22 -7
  571. package/adapters/gemini/skills/forge-4-backlog/references/verify-state.md +98 -0
  572. package/adapters/gemini/skills/forge-5-loop/forge-5-loop.md +48 -53
  573. package/adapters/gemini/skills/forge-5-loop/references/agent-selection.md +5 -1
  574. package/adapters/gemini/skills/forge-5-loop/references/preflight-and-self-heal.md +183 -0
  575. package/adapters/gemini/skills/forge-5-loop/references/recovery-procedure.md +14 -4
  576. package/adapters/gemini/skills/forge-5-loop/references/runner-contract.md +18 -8
  577. package/adapters/gemini/skills/forge-5-loop/references/shared-conventions.md +188 -35
  578. package/adapters/gemini/skills/forge-5-loop/references/stage-exit-protocol.md +22 -7
  579. package/adapters/gemini/skills/forge-5-loop/references/verify-state.md +98 -0
  580. package/adapters/gemini/skills/forge-6-docs/forge-6-docs.md +27 -20
  581. package/adapters/gemini/skills/forge-6-docs/references/shared-conventions.md +188 -35
  582. package/adapters/gemini/skills/forge-6-docs/references/stage-exit-protocol.md +22 -7
  583. package/adapters/gemini/skills/forge-6-docs/references/verify-state.md +98 -0
  584. package/adapters/gemini/skills/forge-bootstrap/forge-bootstrap.md +23 -15
  585. package/adapters/gemini/skills/forge-bootstrap/references/shared-conventions.md +572 -0
  586. package/adapters/gemini/skills/forge-bootstrap/references/templates/hygiene/CLAUDE.md +2 -2
  587. package/adapters/gemini/skills/forge-fix/forge-fix.md +27 -30
  588. package/adapters/gemini/skills/forge-fix/references/select-outcome.md +138 -0
  589. package/adapters/gemini/skills/forge-fix/references/shared-conventions.md +188 -35
  590. package/adapters/gemini/skills/forge-fix/references/stage-exit-protocol.md +22 -7
  591. package/adapters/gemini/skills/forge-guide/forge-guide.md +84 -7
  592. package/adapters/gemini/skills/forge-guide/references/forge-config-schema.json +5 -5
  593. package/adapters/gemini/skills/forge-guide/references/preflight-and-self-heal.md +183 -0
  594. package/adapters/gemini/skills/forge-guide/references/process-overview.md +10 -10
  595. package/adapters/gemini/skills/forge-guide/references/shared-conventions.md +188 -35
  596. package/adapters/gemini/skills/forge-guide/references/stack-resolution.md +1 -1
  597. package/adapters/gemini/skills/forge-init/forge-init.md +62 -12
  598. package/adapters/gemini/skills/forge-init/references/preflight-and-self-heal.md +183 -0
  599. package/adapters/gemini/skills/forge-init/references/shared-conventions.md +572 -0
  600. package/adapters/gemini/skills/forge-verify/forge-verify.md +27 -30
  601. package/adapters/gemini/skills/forge-verify/references/findings-template.md +2 -2
  602. package/adapters/gemini/skills/forge-verify/references/select-outcome.md +138 -0
  603. package/adapters/gemini/skills/forge-verify/references/shared-conventions.md +188 -35
  604. package/adapters/gemini/skills/forge-verify/references/stage-exit-protocol.md +22 -7
  605. package/adapters/gemini/skills/forge-verify/references/verification-checklists/epic.md +2 -2
  606. package/adapters/gemini/skills/forge-verify/references/verifier-patterns/MEMORY.md +35 -0
  607. package/adapters/gemini/skills/forge-verify/references/verifier-patterns/pattern_absence_claims.md +36 -0
  608. package/adapters/gemini/skills/forge-verify/references/verifier-patterns/pattern_carveout_sibling_semantics.md +34 -0
  609. package/adapters/gemini/skills/forge-verify/references/verifier-patterns/pattern_deviation_judgment.md +36 -0
  610. package/adapters/gemini/skills/forge-verify/references/verifier-patterns/pattern_enum_vocabulary_ripple.md +43 -0
  611. package/adapters/gemini/skills/forge-verify/references/verifier-patterns/pattern_extend_the_existing.md +31 -0
  612. package/adapters/gemini/skills/forge-verify/references/verifier-patterns/pattern_guard_substitution_detection.md +34 -0
  613. package/adapters/gemini/skills/forge-verify/references/verifier-patterns/pattern_mechanical_rewrite_damage.md +30 -0
  614. package/adapters/gemini/skills/forge-verify/references/verifier-patterns/pattern_postfix_reverify.md +82 -0
  615. package/adapters/gemini/skills/forge-verify/references/verifier-patterns/pattern_proximity_window_guards.md +26 -0
  616. package/adapters/gemini/skills/forge-verify/references/verifier-patterns/pattern_scratch_root_probes.md +40 -0
  617. package/adapters/gemini/skills/forge-verify/references/verifier-patterns/pattern_self_referential_control.md +26 -0
  618. package/adapters/gemini/skills/forge-verify/references/verifier-patterns/pattern_sibling_docstring_sweep.md +28 -0
  619. package/adapters/gemini/skills/forge-verify/references/verifier-patterns/pattern_spec_literals_are_claims.md +45 -0
  620. package/adapters/gemini/skills/forge-verify/references/verifier-patterns/pattern_stale_counts_after_split.md +36 -0
  621. package/adapters/gemini/skills/forge-verify/references/verifier-patterns/pattern_test_count_units.md +20 -0
  622. package/adapters/gemini/skills/forge-verify/references/verifier-patterns/pattern_vacuous_self_reading_tests.md +32 -0
  623. package/adapters/pi/.feature-forge-bundle.json +1 -1
  624. package/adapters/pi/agents/forge-verifier.md +16 -5
  625. package/adapters/pi/extensions/forge-invocation-args/README.md +106 -0
  626. package/adapters/pi/extensions/forge-invocation-args/index.ts +31 -0
  627. package/adapters/pi/extensions/forge-invocation-args/wiring.ts +172 -0
  628. package/adapters/pi/package.json +1 -0
  629. package/adapters/pi/references/forge-config-schema.json +3 -3
  630. package/adapters/pi/references/portable-root.md +20 -16
  631. package/adapters/pi/references/preflight-and-self-heal.md +183 -0
  632. package/adapters/pi/references/select-outcome.md +138 -0
  633. package/adapters/pi/references/shared-conventions.md +175 -22
  634. package/adapters/pi/references/skill-frontmatter.schema.json +3 -1
  635. package/adapters/pi/references/stack-resolution.md +1 -1
  636. package/adapters/pi/references/stage-exit-protocol.md +19 -4
  637. package/adapters/pi/references/templates/root-hygiene/AGENTS.md +14 -0
  638. package/adapters/pi/references/templates/root-hygiene/CLAUDE.md +14 -0
  639. package/adapters/pi/references/templates/specs-hygiene/AGENTS.md +3 -1
  640. package/adapters/pi/references/templates/specs-hygiene/CLAUDE.md +2 -1
  641. package/adapters/pi/references/verifier-patterns/MEMORY.md +35 -0
  642. package/adapters/pi/references/verifier-patterns/pattern_absence_claims.md +36 -0
  643. package/adapters/pi/references/verifier-patterns/pattern_carveout_sibling_semantics.md +34 -0
  644. package/adapters/pi/references/verifier-patterns/pattern_deviation_judgment.md +36 -0
  645. package/adapters/pi/references/verifier-patterns/pattern_enum_vocabulary_ripple.md +43 -0
  646. package/adapters/pi/references/verifier-patterns/pattern_extend_the_existing.md +31 -0
  647. package/adapters/pi/references/verifier-patterns/pattern_guard_substitution_detection.md +34 -0
  648. package/adapters/pi/references/verifier-patterns/pattern_mechanical_rewrite_damage.md +30 -0
  649. package/adapters/pi/references/verifier-patterns/pattern_postfix_reverify.md +82 -0
  650. package/adapters/pi/references/verifier-patterns/pattern_proximity_window_guards.md +26 -0
  651. package/adapters/pi/references/verifier-patterns/pattern_scratch_root_probes.md +40 -0
  652. package/adapters/pi/references/verifier-patterns/pattern_self_referential_control.md +26 -0
  653. package/adapters/pi/references/verifier-patterns/pattern_sibling_docstring_sweep.md +28 -0
  654. package/adapters/pi/references/verifier-patterns/pattern_spec_literals_are_claims.md +45 -0
  655. package/adapters/pi/references/verifier-patterns/pattern_stale_counts_after_split.md +36 -0
  656. package/adapters/pi/references/verifier-patterns/pattern_test_count_units.md +20 -0
  657. package/adapters/pi/references/verifier-patterns/pattern_vacuous_self_reading_tests.md +32 -0
  658. package/adapters/pi/references/verify-state.md +98 -0
  659. package/adapters/pi/scripts/forge-bootstrap.py +23 -2
  660. package/adapters/pi/scripts/forge-root.sh +104 -21
  661. package/adapters/pi/scripts/forge-session.py +620 -6973
  662. package/adapters/pi/scripts/forge_session/__init__.py +11 -0
  663. package/adapters/pi/scripts/forge_session/_common.py +1566 -0
  664. package/adapters/pi/scripts/forge_session/_doctor_util.py +218 -0
  665. package/adapters/pi/scripts/forge_session/cli.py +1067 -0
  666. package/adapters/pi/scripts/forge_session/decisions.py +355 -0
  667. package/adapters/pi/scripts/forge_session/discover.py +472 -0
  668. package/adapters/pi/scripts/forge_session/doctor.py +2144 -0
  669. package/adapters/pi/scripts/forge_session/exit.py +1081 -0
  670. package/adapters/pi/scripts/forge_session/outcomes.py +574 -0
  671. package/adapters/pi/scripts/forge_session/routes.py +1162 -0
  672. package/adapters/pi/scripts/forge_session/state.py +1479 -0
  673. package/adapters/pi/scripts/forge_session/topology.py +443 -0
  674. package/adapters/pi/skills/forge/SKILL.md +9 -8
  675. package/adapters/pi/skills/forge/references/shared-conventions.md +175 -22
  676. package/adapters/pi/skills/forge/references/stage-exit-protocol.md +19 -4
  677. package/adapters/pi/skills/forge-0-epic/SKILL.md +9 -8
  678. package/adapters/pi/skills/forge-0-epic/references/edit-mode.md +9 -5
  679. package/adapters/pi/skills/forge-0-epic/references/epic-manifest-subcommands.md +6 -1
  680. package/adapters/pi/skills/forge-0-epic/references/portable-root.md +20 -16
  681. package/adapters/pi/skills/forge-0-epic/references/shared-conventions.md +175 -22
  682. package/adapters/pi/skills/forge-0-epic/references/stage-exit-protocol.md +19 -4
  683. package/adapters/pi/skills/forge-1-prd/SKILL.md +9 -8
  684. package/adapters/pi/skills/forge-1-prd/references/shared-conventions.md +175 -22
  685. package/adapters/pi/skills/forge-1-prd/references/stage-exit-protocol.md +19 -4
  686. package/adapters/pi/skills/forge-2-tech/SKILL.md +8 -7
  687. package/adapters/pi/skills/forge-2-tech/references/shared-conventions.md +175 -22
  688. package/adapters/pi/skills/forge-2-tech/references/stack-resolution.md +1 -1
  689. package/adapters/pi/skills/forge-2-tech/references/stage-exit-protocol.md +19 -4
  690. package/adapters/pi/skills/forge-3-specs/SKILL.md +7 -6
  691. package/adapters/pi/skills/forge-3-specs/references/shared-conventions.md +175 -22
  692. package/adapters/pi/skills/forge-3-specs/references/stage-exit-protocol.md +19 -4
  693. package/adapters/pi/skills/forge-4-backlog/SKILL.md +18 -9
  694. package/adapters/pi/skills/forge-4-backlog/references/shared-conventions.md +175 -22
  695. package/adapters/pi/skills/forge-4-backlog/references/stage-exit-protocol.md +19 -4
  696. package/adapters/pi/skills/forge-4-backlog/references/verify-state.md +98 -0
  697. package/adapters/pi/skills/forge-5-loop/SKILL.md +38 -42
  698. package/adapters/pi/skills/forge-5-loop/references/agent-selection.md +4 -0
  699. package/adapters/pi/skills/forge-5-loop/references/preflight-and-self-heal.md +183 -0
  700. package/adapters/pi/skills/forge-5-loop/references/recovery-procedure.md +10 -0
  701. package/adapters/pi/skills/forge-5-loop/references/runner-contract.md +15 -5
  702. package/adapters/pi/skills/forge-5-loop/references/shared-conventions.md +175 -22
  703. package/adapters/pi/skills/forge-5-loop/references/stage-exit-protocol.md +19 -4
  704. package/adapters/pi/skills/forge-5-loop/references/verify-state.md +98 -0
  705. package/adapters/pi/skills/forge-6-docs/SKILL.md +23 -15
  706. package/adapters/pi/skills/forge-6-docs/references/shared-conventions.md +175 -22
  707. package/adapters/pi/skills/forge-6-docs/references/stage-exit-protocol.md +19 -4
  708. package/adapters/pi/skills/forge-6-docs/references/verify-state.md +98 -0
  709. package/adapters/pi/skills/forge-bootstrap/SKILL.md +20 -11
  710. package/adapters/pi/skills/forge-bootstrap/references/shared-conventions.md +572 -0
  711. package/adapters/pi/skills/forge-fix/SKILL.md +17 -19
  712. package/adapters/pi/skills/forge-fix/references/select-outcome.md +138 -0
  713. package/adapters/pi/skills/forge-fix/references/shared-conventions.md +175 -22
  714. package/adapters/pi/skills/forge-fix/references/stage-exit-protocol.md +19 -4
  715. package/adapters/pi/skills/forge-guide/SKILL.md +79 -1
  716. package/adapters/pi/skills/forge-guide/references/forge-config-schema.json +3 -3
  717. package/adapters/pi/skills/forge-guide/references/preflight-and-self-heal.md +183 -0
  718. package/adapters/pi/skills/forge-guide/references/shared-conventions.md +175 -22
  719. package/adapters/pi/skills/forge-guide/references/stack-resolution.md +1 -1
  720. package/adapters/pi/skills/forge-init/SKILL.md +59 -8
  721. package/adapters/pi/skills/forge-init/references/preflight-and-self-heal.md +183 -0
  722. package/adapters/pi/skills/forge-init/references/shared-conventions.md +572 -0
  723. package/adapters/pi/skills/forge-verify/SKILL.md +17 -19
  724. package/adapters/pi/skills/forge-verify/references/findings-template.md +1 -1
  725. package/adapters/pi/skills/forge-verify/references/select-outcome.md +138 -0
  726. package/adapters/pi/skills/forge-verify/references/shared-conventions.md +175 -22
  727. package/adapters/pi/skills/forge-verify/references/stage-exit-protocol.md +19 -4
  728. package/adapters/pi/skills/forge-verify/references/verification-checklists/epic.md +1 -1
  729. package/adapters/pi/skills/forge-verify/references/verifier-patterns/MEMORY.md +35 -0
  730. package/adapters/pi/skills/forge-verify/references/verifier-patterns/pattern_absence_claims.md +36 -0
  731. package/adapters/pi/skills/forge-verify/references/verifier-patterns/pattern_carveout_sibling_semantics.md +34 -0
  732. package/adapters/pi/skills/forge-verify/references/verifier-patterns/pattern_deviation_judgment.md +36 -0
  733. package/adapters/pi/skills/forge-verify/references/verifier-patterns/pattern_enum_vocabulary_ripple.md +43 -0
  734. package/adapters/pi/skills/forge-verify/references/verifier-patterns/pattern_extend_the_existing.md +31 -0
  735. package/adapters/pi/skills/forge-verify/references/verifier-patterns/pattern_guard_substitution_detection.md +34 -0
  736. package/adapters/pi/skills/forge-verify/references/verifier-patterns/pattern_mechanical_rewrite_damage.md +30 -0
  737. package/adapters/pi/skills/forge-verify/references/verifier-patterns/pattern_postfix_reverify.md +82 -0
  738. package/adapters/pi/skills/forge-verify/references/verifier-patterns/pattern_proximity_window_guards.md +26 -0
  739. package/adapters/pi/skills/forge-verify/references/verifier-patterns/pattern_scratch_root_probes.md +40 -0
  740. package/adapters/pi/skills/forge-verify/references/verifier-patterns/pattern_self_referential_control.md +26 -0
  741. package/adapters/pi/skills/forge-verify/references/verifier-patterns/pattern_sibling_docstring_sweep.md +28 -0
  742. package/adapters/pi/skills/forge-verify/references/verifier-patterns/pattern_spec_literals_are_claims.md +45 -0
  743. package/adapters/pi/skills/forge-verify/references/verifier-patterns/pattern_stale_counts_after_split.md +36 -0
  744. package/adapters/pi/skills/forge-verify/references/verifier-patterns/pattern_test_count_units.md +20 -0
  745. package/adapters/pi/skills/forge-verify/references/verifier-patterns/pattern_vacuous_self_reading_tests.md +32 -0
  746. package/dist/manifest.d.ts +1 -1
  747. package/dist/rauf.d.ts +3 -3
  748. package/dist/rauf.js +2 -2
  749. package/dist/source.d.ts +5 -2
  750. package/dist/source.js +5 -2
  751. package/dist/types.d.ts +1 -1
  752. package/dist/types.js +1 -1
  753. package/package.json +2 -1
@@ -0,0 +1,1081 @@
1
+ """The ``stage-exit`` verb — the Scripted Stage Exit (#279).
2
+
3
+ Extracted verbatim from the ``forge-session.py`` monolith (issue #279, item 007).
4
+ ``stage_exit`` is the pipeline's largest verb; its routing engine lives in
5
+ :mod:`forge_session.routes`, its outcome logic in :mod:`forge_session.outcomes`,
6
+ and its write helpers in :mod:`forge_session.state`. This module imports those and
7
+ the shared primitives in :mod:`forge_session._common`; it never imports the shim,
8
+ so there is no circular import. The CLI contract — flags, branch routing, exit
9
+ codes, and the full JSON payload shape — is FROZEN.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import json
15
+ import sys
16
+ from pathlib import Path
17
+ from typing import Final, get_args
18
+
19
+ from forge_session._common import (
20
+ EPIC_MEMBER_FALLBACK_WARNING,
21
+ EPIC_STATE_FILENAME,
22
+ EXIT_HOSTS,
23
+ EXIT_OUTCOMES,
24
+ EXIT_STAGES,
25
+ INVALID_AUTO_VERIFY_KEY_WARNING,
26
+ MANIFEST_FILENAME,
27
+ NEXT_STEPS_SENTINEL,
28
+ PIPELINE_STATE_FILENAME,
29
+ PRODUCTION_STAGES,
30
+ ProductionStage,
31
+ SAFE_NAME_RE,
32
+ STAGE_NOUN,
33
+ StageExitPayload,
34
+ UsageError,
35
+ VERIFY_MODE_TO_STAGE,
36
+ VERIFY_TOKEN_BY_STAGE,
37
+ ExitOwner,
38
+ VerifyCapability,
39
+ _EXIT_VERIFY_TOKEN,
40
+ _git_output,
41
+ load_effective_config,
42
+ _read_state,
43
+ _resolve_feature_dir,
44
+ _stage_version,
45
+ _verify_entry,
46
+ auto_verify_for,
47
+ invalid_auto_verify_keys,
48
+ next_stage,
49
+ pending_verify,
50
+ )
51
+ from forge_session.outcomes import _classify_verify_entry, _verify_state_for
52
+ from forge_session.routes import (
53
+ _branch_route,
54
+ _debt_metadata_warnings,
55
+ _docs_route,
56
+ _epic_terminal_state,
57
+ _host_command,
58
+ _loop_route,
59
+ _next_steps_block,
60
+ _production_stage_of,
61
+ _promote_reconcile,
62
+ _render_status,
63
+ _schedule_auto_verify_debt,
64
+ )
65
+ from forge_session.state import _assert_safe_name
66
+
67
+ #: The seven production stages as a ROUTING domain. Deliberately not
68
+ #: ``PRODUCTION_STAGES``, which is the six-stage member walk that excludes
69
+ #: ``forge-0-epic``; derived from the shared ``ProductionStage`` alias so the two
70
+ #: cannot drift.
71
+ _EXIT_PRODUCTION_STAGES: Final[tuple[str, ...]] = get_args(ProductionStage)
72
+ #: The two direct branch skills — every exit stage that is not a production stage.
73
+ #: Derived, so adding a branch skill to ``ExitStage`` lands here automatically.
74
+ _BRANCH_STAGES: Final[tuple[str, ...]] = tuple(
75
+ stage for stage in EXIT_STAGES if stage not in _EXIT_PRODUCTION_STAGES
76
+ )
77
+ #: Inverse of ``VERIFY_MODE_TO_STAGE``. The mapping is injective, so the inverse is
78
+ #: total over its values; stages with no mode (``forge-6-docs``) are simply absent.
79
+ _STAGE_TO_VERIFY_MODE: Final[dict[str, str]] = {
80
+ stage: mode for mode, stage in VERIFY_MODE_TO_STAGE.items()
81
+ }
82
+ #: The stage each exit hands off to when pipeline state cannot say better. Also the
83
+ #: production-successor table a branch exit walks from its RESOLVED SERVED stage:
84
+ #: ``forge-6-docs`` is absent because the pipeline ends there — a docs exit routes to
85
+ #: a completion action, never to a nonexistent stage 7.
86
+ _EXIT_NEXT_STAGE: Final[dict[str, str]] = {
87
+ "forge-0-epic": "forge-1-prd",
88
+ "forge-1-prd": "forge-2-tech",
89
+ "forge-2-tech": "forge-3-specs",
90
+ "forge-3-specs": "forge-4-backlog",
91
+ "forge-4-backlog": "forge-5-loop",
92
+ "forge-5-loop": "forge-6-docs",
93
+ }
94
+ def _epic_verify_context(specs_dir: Path, epic_name: str) -> tuple[dict, int | None]:
95
+ """Read an epic's verification entry and manifest revision — tolerantly.
96
+
97
+ An epic's verification state lives in ``{specsDir}/{epic}/.epic-state.json``
98
+ and its artifact revision is the sibling manifest's ``revision``. Neither ever
99
+ comes from a member ``.pipeline-state.json``, and a member production-stage
100
+ ``version`` is never the epic's revision (REQ-SEC-01). This
101
+ is the READ half: it degrades to ``({}, None)`` on anything missing or
102
+ malformed, matching stage-exit's "never crash a stage closing" posture. The
103
+ strict, fail-closed resolution lives on the WRITE path
104
+ (``_load_epic_state_for_write``).
105
+
106
+ A legacy manifest with no ``revision`` reads as logical ``1``, matching
107
+ ``epic-manifest.py::load_manifest``; its bytes are not rewritten
108
+ (REQ-DEBT-06).
109
+
110
+ Args:
111
+ specs_dir: The configured specs directory.
112
+ epic_name: The epic — what ``--feature`` carries on an epic-scoped exit.
113
+
114
+ Returns:
115
+ ``(verify_entry, revision)``; ``revision`` is None when the manifest is
116
+ missing or its revision unusable.
117
+ """
118
+ epic_dir = specs_dir / epic_name
119
+ revision: int | None = None
120
+ manifest = _read_state(epic_dir / MANIFEST_FILENAME)
121
+ if manifest:
122
+ raw = manifest.get("revision", 1)
123
+ if isinstance(raw, int) and not isinstance(raw, bool) and raw >= 1:
124
+ revision = raw
125
+ entry = _verify_entry(_read_state(epic_dir / EPIC_STATE_FILENAME), "forge-verify-epic")
126
+ return entry, revision
127
+ def _same_named_candidates(specs_dir: Path, epic: str, member: str) -> list[Path]:
128
+ """Directories OTHER than ``{specsDir}/{epic}/{member}`` carrying that name's state.
129
+
130
+ Read-only, and used only to tell "no such feature anywhere" apart from "that
131
+ name belongs to someone else" when the selected epic does not contain the
132
+ member. Sorted, so the ambiguity error it feeds is deterministic.
133
+ """
134
+ contained = specs_dir / epic / member
135
+ out: list[Path] = []
136
+ flat = specs_dir / member
137
+ if flat != contained and (flat / PIPELINE_STATE_FILENAME).is_file():
138
+ out.append(flat)
139
+ if specs_dir.is_dir():
140
+ out.extend(
141
+ sorted(
142
+ p
143
+ for p in specs_dir.glob(f"*/{member}")
144
+ if p != contained and (p / PIPELINE_STATE_FILENAME).is_file()
145
+ )
146
+ )
147
+ return out
148
+ def _epic_member_state(specs_dir: Path, epic: str, member: str) -> tuple[dict, str | None]:
149
+ """Resolve ONE epic member's live pipeline state for edit-mode routing.
150
+
151
+ Identity containment comes first: the member is read from
152
+ ``{specsDir}/{epic}/{member}`` and nowhere else, so a same-named flat feature
153
+ or a member of a different epic can never be substituted for it (REQ-SEC-01).
154
+ ``_assert_safe_name`` has already rejected traversal by the time this runs.
155
+
156
+ Progress is then TOLERATED rather than demanded: an absent, unreadable,
157
+ malformed, or foreign-epic state yields a reason instead of an exception, so
158
+ the caller can degrade DOWN to ``forge-1-prd`` with a named warning rather
159
+ than crash a stage closing or infer progress it could not read (REQ-PROD-06).
160
+ This is the one documented new tolerant case.
161
+
162
+ Identity itself still fails closed: a member that is not under the selected
163
+ epic at all and whose bare name matches more than one other candidate cannot
164
+ be pinned to a single feature, and guessing would route a DIFFERENT feature's
165
+ pipeline (REQ-REL-02).
166
+
167
+ Args:
168
+ specs_dir: The configured specs directory.
169
+ epic: The selected epic — what ``--feature`` carries on an epic exit.
170
+ member: The selected member (``--next-feature``), already name-checked.
171
+
172
+ Returns:
173
+ ``(state, None)`` when the member's state resolved, else ``({}, reason)``
174
+ where ``reason`` is a member of ``EPIC_MEMBER_FALLBACK_REASONS``.
175
+
176
+ Raises:
177
+ UsageError: The member is not under the selected epic and its bare name is
178
+ ambiguous across the specs tree (→ exit 2, no route guessed).
179
+ """
180
+ member_dir = specs_dir / epic / member
181
+ state_path = member_dir / PIPELINE_STATE_FILENAME
182
+ # ``exists`` rather than ``is_file``: something occupying the state file's name
183
+ # that cannot be read as one is `unreadable`, not absent.
184
+ if not state_path.exists():
185
+ if member_dir.is_dir():
186
+ # Contained, just not started yet — the creation-mode case.
187
+ return {}, "missing"
188
+ elsewhere = _same_named_candidates(specs_dir, epic, member)
189
+ if len(elsewhere) > 1:
190
+ listed = ", ".join(str(p) for p in elsewhere)
191
+ raise UsageError(
192
+ f"ambiguous member {member!r} for epic {epic}: it is not under "
193
+ f"{member_dir} and {len(elsewhere)} other directories carry a state "
194
+ f"file for that name ({listed}) — refusing to guess which feature to "
195
+ f"route to. Re-run naming the epic that owns it."
196
+ )
197
+ return {}, "not a member of this epic" if elsewhere else "missing"
198
+ try:
199
+ raw = state_path.read_text(encoding="utf-8")
200
+ except OSError:
201
+ return {}, "unreadable"
202
+ try:
203
+ parsed = json.loads(raw)
204
+ except json.JSONDecodeError:
205
+ return {}, "malformed"
206
+ if not isinstance(parsed, dict):
207
+ return {}, "malformed"
208
+ back_pointer = parsed.get("epic")
209
+ if isinstance(back_pointer, str) and back_pointer != epic:
210
+ # The file sits under this epic but claims another one. Trusting either
211
+ # side would assert progress for a feature we cannot identify.
212
+ return {}, "not a member of this epic"
213
+ return parsed, None
214
+ def resolve_served_stage(
215
+ served_stage: str | None,
216
+ verify_mode: str | None,
217
+ ) -> str:
218
+ """Resolve one unambiguous production stage for a branch exit.
219
+
220
+ Args:
221
+ served_stage: Explicit production stage supplied by the branch caller.
222
+ verify_mode: Optional authoritative mode mapped by VERIFY_MODE_TO_STAGE.
223
+
224
+ Returns:
225
+ A member of the shared ProductionStage domain.
226
+
227
+ Raises:
228
+ UsageError: The explicit stage is invalid, mode is invalid, both inputs
229
+ disagree, or neither input identifies a stage.
230
+ """
231
+ if served_stage is not None and served_stage not in _EXIT_PRODUCTION_STAGES:
232
+ raise UsageError(
233
+ f"--served-stage {served_stage!r} is not a production stage; expected "
234
+ f"one of {', '.join(_EXIT_PRODUCTION_STAGES)}"
235
+ )
236
+ if verify_mode is not None and verify_mode not in VERIFY_MODE_TO_STAGE:
237
+ raise UsageError(
238
+ f"--verify-mode {verify_mode!r} is not a known verify mode; expected "
239
+ f"one of {', '.join(VERIFY_MODE_TO_STAGE)}"
240
+ )
241
+ if served_stage is not None and verify_mode is not None:
242
+ mapped = VERIFY_MODE_TO_STAGE[verify_mode]
243
+ if mapped != served_stage:
244
+ # Both were supplied and they disagree. Name both flags and both
245
+ # resolutions — picking one silently is exactly the guess REQ-ROUTE-03
246
+ # forbids.
247
+ raise UsageError(
248
+ f"--served-stage {served_stage} conflicts with --verify-mode "
249
+ f"{verify_mode} (which maps to {mapped}); supply one, or supply "
250
+ "values that agree"
251
+ )
252
+ return served_stage
253
+ if served_stage is not None:
254
+ # Explicit stage takes precedence, and accepts any ProductionStage —
255
+ # including forge-6-docs, which no verify mode maps to.
256
+ return served_stage
257
+ if verify_mode is not None:
258
+ return VERIFY_MODE_TO_STAGE[verify_mode]
259
+ raise UsageError(
260
+ "forge-verify requires --served-stage or an unambiguous --verify-mode; "
261
+ "rerun with the production stage this verification served"
262
+ )
263
+ #: The deterministic sentence a TERMINAL epic exit renders (issue #248). Every route
264
+ #: fences NOTHING — a closed epic has no next pipeline command, and re-fencing the
265
+ #: dashboard is exactly the self-loop this text replaces — so the start-new-work
266
+ #: pointers are host-translated INLINE mentions, never the primary action.
267
+ #:
268
+ #: ``complete`` every member is genuinely done (the rollup says so)
269
+ #: ``declared`` the operator set the manifest `status` to `complete`; the counts are
270
+ #: stated as they really are, so the wording never claims members are
271
+ #: finished when they are not
272
+ #: ``abandoned`` the operator set the manifest `status` to `abandoned`. Checked
273
+ #: FIRST, ahead of the rollup: an abandoned epic whose members happen
274
+ #: to be complete must not be announced as completed work.
275
+ _EPIC_TERMINAL_TEXT: Final[dict[str, str]] = {
276
+ "complete": (
277
+ "Epic {epic} is complete — every member is done ({complete}/{total}) and "
278
+ "nothing is actionable, so there is no next pipeline command to run. "
279
+ "Optionally, you can start a new feature with `{new_feature}` or group "
280
+ "related work into an epic with `{new_epic}`; neither is required to finish "
281
+ "here. Re-open `{dashboard}` any time to inspect the epic's state."
282
+ ),
283
+ "declared": (
284
+ "Epic {epic} is marked complete in its manifest and nothing is actionable "
285
+ "({complete}/{total} members complete), so there is no next pipeline command "
286
+ "to run. If that count is not what you expect, re-open `{dashboard}` to "
287
+ "inspect the epic; otherwise you can start a new feature with "
288
+ "`{new_feature}` or a new epic with `{new_epic}`."
289
+ ),
290
+ "abandoned": (
291
+ "Epic {epic} is marked abandoned in its manifest, so the pipeline stops here "
292
+ "and nothing further runs against it — this is NOT a completion "
293
+ "({complete}/{total} members had been completed before it was abandoned). "
294
+ "Re-open `{dashboard}` to inspect or revive it, or start a new feature with "
295
+ "`{new_feature}`."
296
+ ),
297
+ }
298
+ def stage_exit(
299
+ feature: str,
300
+ stage: str,
301
+ specs_dir: Path,
302
+ config_path: Path,
303
+ epic: str | None,
304
+ host: str,
305
+ next_feature: str | None,
306
+ served_stage: str | None = None,
307
+ verify_mode: str | None = None,
308
+ outcome: str | None = None,
309
+ owner: str | None = None,
310
+ verify_capability: str = "manual",
311
+ cause: str | None = None,
312
+ ) -> StageExitPayload:
313
+ """Compute a deterministic stage-exit payload.
314
+
315
+ Args:
316
+ feature: Safe feature name, or epic name for an epic-scoped exit.
317
+ stage: One member of `EXIT_STAGES`.
318
+ specs_dir: Configured specs directory.
319
+ config_path: Path to `forge.config.json`.
320
+ epic: Owning epic for a nested member, otherwise None.
321
+ host: Command-rendering host: `claude`, `pi`, or `generic`.
322
+ next_feature: Explicit epic handoff member, when applicable.
323
+ served_stage: Production stage served by direct verify/fix.
324
+ verify_mode: Verify mode used to infer `served_stage` when unique.
325
+ outcome: Required stage-specific outcome for loop/docs/verify/fix.
326
+ owner: Required for verify/fix: `direct` or `nested`.
327
+ verify_capability: `interactive` only when both question and clean-room
328
+ verifier dispatch capabilities exist; otherwise `manual`. Dispatch
329
+ capability is permission, not tool presence: a dispatch permitted
330
+ only once the user has asked is still `interactive`, because the
331
+ `standard` gate's own prompt supplies that request.
332
+ cause: Pending-attribution annotation (`dependency-starvation`), valid
333
+ only with `--stage forge-5-loop --outcome partial` (REQ-ATTR-04).
334
+ It swaps the partial next-steps sentence for the starvation variant
335
+ and changes no routing.
336
+
337
+ Returns:
338
+ A JSON-serializable `StageExitPayload` dictionary.
339
+
340
+ Raises:
341
+ UsageError: Unsafe or ambiguous identity, unsupported stage/outcome,
342
+ missing ownership/served-stage metadata, or conflicting inference.
343
+
344
+ Directive semantics (the contract in ``references/stage-exit-protocol.md``):
345
+
346
+ - ``runInStageVerify`` — the effective auto-verify (per-stage override,
347
+ else global; strict-true) is on AND this stage's verify is not already
348
+ resolved (fresh/skipped) AND no findings report exists at the current
349
+ revision (that state routes to forge-fix instead — scheduling over the
350
+ report would delete its metadata, REQ-EXIT-04). The skill then dispatches
351
+ the clean-room verify in-session (principle #2: verify before the clear).
352
+ - ``autoVerifyDebtRecorded`` — the ``auto-verify-pending`` marker for this
353
+ stage is durably on disk. Written BEFORE this payload exists, so
354
+ a failed write raises ``UsageError`` and returns no payload at all and
355
+ ``runInStageVerify: True`` with ``autoVerifyDebtRecorded: False`` is
356
+ unreachable. Scheduling is idempotent by target revision: a repeat at the
357
+ same revision touches neither ``scheduledAt`` nor top-level ``updatedAt``.
358
+ - ``autoFixEligible`` — ``autoFix`` is strict-true AND the in-stage verify
359
+ runs AND the working tree is clean. Findings-level preconditions (zero
360
+ unresolved decisions) remain the skill's runtime check. Its clean-tree
361
+ snapshot is taken BEFORE the debt write, so that sanctioned control-plane
362
+ mutation cannot dirty its own precondition.
363
+ - ``verifyState``/``warnings``/``cleanTree`` — all PRE-mutation snapshots:
364
+ they describe the state the routing decision was made from, which is why a
365
+ first exit reports ``never`` while the debt it just recorded reads
366
+ ``auto-pending`` on the next one. Only ``autoVerifyDebtRecorded`` reports
367
+ the write.
368
+ - ``verifyGate`` — ``none`` when verify is resolved (including a tokenless
369
+ stage), the in-stage run covers it, or a live findings report routes to
370
+ forge-fix (the fenced fix IS the one action — a "verify now?" prompt
371
+ beside it would be a second, contradictory ask); ``standard`` when
372
+ auto-verify is off, verification is outstanding, and the CALLER declared
373
+ ``--verify-capability interactive``; ``manual-print`` for the same state
374
+ under ``manual`` (print ``verifyCommand`` instead of presenting the gate).
375
+ Never a function of ``--host``: capable Pi is ``standard`` and incapable
376
+ Claude is ``manual-print`` (REQ-EXIT-07).
377
+ - ``primaryCommand``/``deferredCommand`` — the verify-first pair. While
378
+ verification is unresolved ``primaryCommand`` is the verify command — or
379
+ the forge-fix command when a findings report is live at the current
380
+ revision — and is the ONLY fenced command; ``deferredCommand`` names the
381
+ production successor as unfenced conditional prose. ``nextCommand`` stays
382
+ compatibility/routing metadata and never overrides ``primaryCommand``
383
+ (REQ-EXIT-06). Both are None on the one TERMINAL exit — a finished epic
384
+ (see ``forge-0-epic`` below) — where the block fences nothing.
385
+ - ``nextStage``/``nextCommand`` — from pipeline state when it already
386
+ records this stage complete (first non-complete production stage), else
387
+ the fixed successor. ``--next-feature`` names the first actionable
388
+ feature for the epic handoff; without it an epic exit hands back to the
389
+ epic dashboard rather than naming a member it cannot resolve.
390
+ With it, the handoff is derived from THAT member's live state via
391
+ ``next_stage``: a progressed member resumes where it actually is,
392
+ a fully complete member hands back to the epic dashboard, and a member
393
+ whose state cannot be resolved falls back to ``forge-1-prd`` with
394
+ ``warnings`` entry 1 naming it.
395
+ - ``forge-0-epic`` — no route hands back the dashboard as the SOLE remaining
396
+ action (#248). An ACTIONABLE member with no next production stage owes
397
+ verification, so the exit routes to ``render-status``'s own answer for that
398
+ member (``forge-verify``/``forge-fix``, or ``forge-1-prd`` when its PRD is
399
+ missing) rather than to the dashboard. And a CLOSED epic exits terminally:
400
+ when live ``render-status`` reports nothing actionable AND the epic is
401
+ complete, declared complete, or abandoned (see ``_epic_terminal_state``),
402
+ ``primaryCommand``/``nextCommand``/``nextStage`` are all None
403
+ and the block fences nothing. The dashboard is still the answer where it is a
404
+ real action rather than a re-run: a ``UsageError`` recovery, a blocking
405
+ reconcile, and an active epic with no members decomposed yet.
406
+ Previously this state handed back
407
+ ``/skill:forge-0-epic {epic}`` — the command that produced it — so the
408
+ operator was prompted to re-run it indefinitely. The terminal answer applies
409
+ only where the routing already landed on that dashboard, so verify-first
410
+ ordering, a live findings report, and an open epic change request all keep
411
+ precedence, and a ``render-status`` failure still degrades to the recoverable
412
+ dashboard route rather than claiming completion.
413
+ - ``epicReconcile`` — present only when the exiting member carries
414
+ ``open`` ``epicChangeRequests`` (epic-backflow). ``required: true`` (any
415
+ ``blocksCurrent: true`` request) interposes a reconcile-first exit: the
416
+ NEXT-STEPS primary command becomes ``/skill:forge-0-epic {epic}``
417
+ and the normal next stage is deferred. Only non-blocking requests set
418
+ ``reminder: true`` and append a non-blocking reminder line. Absent when
419
+ there are no open requests (common path) or the epic name is unresolvable.
420
+ - ``servedStage``/``verifyMode``/``outcome``/``owner``/``terminalOwnedBy`` —
421
+ branch metadata. A production exit serves only itself, so ``servedStage``
422
+ is None there; ``verifyStage`` is the DISTINCT value ``pending_verify``
423
+ returns, naming the stage outstanding verification is owed on.
424
+ On a branch exit the outcome table in ``_branch_route`` — not
425
+ verify-first ordering — supplies ``primaryCommand``: a diversion rejoins
426
+ the production stage it served, and every recovery/defer route carries
427
+ ``--served-stage`` forward so the thread is never dropped (issue #176).
428
+ - ``forge-5-loop`` — routed by its required ``--outcome``. ``complete``
429
+ keeps verify-first ordering in front of the documentation/epic-member handoff,
430
+ which for a member is delegated to the live ``render-status`` payload rather
431
+ than re-derived here. The other four outcomes route to the loop resume
432
+ (``partial``/``deferred``) or the navigator (``blocked``/``needs-human``) and
433
+ suppress every downstream signal: ``nextStage``/``nextCommand`` are None,
434
+ ``runInStageVerify`` is False, no debt is scheduled, and ``verifyGate`` is
435
+ ``none`` — a loop still in flight has no finished implementation to verify and
436
+ nothing downstream may read as ready (REQ-PROD-02).
437
+ - ``forge-6-docs`` — the documentation terminus is decided by LIVE epic
438
+ state, never by the successor table: for an epic member the adjacent
439
+ ``epic-manifest.py render-status`` supplies the next actionable member's own
440
+ command, and anything else routes to the epic dashboard. A ``blocked``
441
+ outcome routes to recovery and never claims completion. Any helper failure
442
+ is an actionable ``UsageError`` — exit 2 with no payload, so no guessed
443
+ member command and no sentinel can escape (REQ-REL-02).
444
+ - ``warnings`` — non-fatal advisories in the documented fixed order. Always
445
+ present; ``[]`` means checked-and-clean, which is not the same as absent.
446
+
447
+ Read-only and deterministic. Syntactic validation fails closed with
448
+ ``UsageError`` (exit 2, no payload and no sentinel); everything after it
449
+ degrades to defaults rather than crashing a stage closing.
450
+ """
451
+ # ---- Deterministic validation order ----------------------------------- #
452
+ # 1. Safe names and containment, before any strict filesystem access.
453
+ _assert_safe_name(feature, "--feature")
454
+ if epic is not None:
455
+ _assert_safe_name(epic, "--epic")
456
+ if next_feature is not None:
457
+ _assert_safe_name(next_feature, "--next-feature")
458
+
459
+ # 2. The stage domain itself.
460
+ if stage not in EXIT_STAGES:
461
+ raise UsageError(
462
+ f"unsupported --stage {stage!r}; expected one of {', '.join(EXIT_STAGES)}"
463
+ )
464
+
465
+ # 3./4. Stages 0-4 reject an outcome; loop/docs/verify/fix require their own.
466
+ # argparse cannot express a different enum per stage, so the domain check is here.
467
+ allowed_outcomes = EXIT_OUTCOMES.get(stage)
468
+ if allowed_outcomes is None:
469
+ if outcome is not None:
470
+ raise UsageError(
471
+ f"--outcome is not accepted for {stage}; its exit is state-driven "
472
+ "and has a single outcome"
473
+ )
474
+ elif outcome is None:
475
+ raise UsageError(
476
+ f"{stage} requires --outcome; expected one of "
477
+ f"{', '.join(sorted(allowed_outcomes))}"
478
+ )
479
+ elif outcome not in allowed_outcomes:
480
+ raise UsageError(
481
+ f"--outcome {outcome!r} is not valid for {stage}; expected one of "
482
+ f"{', '.join(sorted(allowed_outcomes))}"
483
+ )
484
+
485
+ # --cause is a forge-5-loop/partial-only attribution annotation (REQ-ATTR-04).
486
+ # argparse `choices` already restricts the value; this restricts the combination.
487
+ if cause is not None and not (stage == "forge-5-loop" and outcome == "partial"):
488
+ raise UsageError(
489
+ "--cause dependency-starvation is valid only with "
490
+ "--stage forge-5-loop --outcome partial"
491
+ )
492
+
493
+ # 5. Ownership: required for the branch skills, rejected for stages 0-6.
494
+ if stage in _BRANCH_STAGES:
495
+ if owner is None:
496
+ raise UsageError(
497
+ f"{stage} requires --owner direct (this call prints the terminal "
498
+ "block) or --owner nested (an outer stage owns it)"
499
+ )
500
+ if owner not in get_args(ExitOwner):
501
+ raise UsageError(
502
+ f"--owner {owner!r} is not valid; expected direct or nested"
503
+ )
504
+ elif owner is not None:
505
+ raise UsageError(
506
+ f"--owner is not accepted for {stage}; only forge-verify and forge-fix "
507
+ "carry branch ownership, and stages 0-6 are always direct owners"
508
+ )
509
+
510
+ # 6. Host and capability, independently. A host NEVER implies a capability.
511
+ if host not in EXIT_HOSTS:
512
+ raise UsageError(
513
+ f"unknown --host {host!r}; expected one of {', '.join(EXIT_HOSTS)}"
514
+ )
515
+ if verify_capability not in get_args(VerifyCapability):
516
+ raise UsageError(
517
+ f"unknown --verify-capability {verify_capability!r}; expected "
518
+ f"{' or '.join(get_args(VerifyCapability))}"
519
+ )
520
+
521
+ # 7./8. Served stage for branch exits; branch-only flags rejected elsewhere.
522
+ if stage in _BRANCH_STAGES:
523
+ resolved_served: str | None = resolve_served_stage(served_stage, verify_mode)
524
+ else:
525
+ if served_stage is not None or verify_mode is not None:
526
+ raise UsageError(
527
+ "--served-stage and --verify-mode are branch-only; "
528
+ f"{stage} is a production stage and serves only itself"
529
+ )
530
+ resolved_served = None
531
+ if next_feature is not None and stage != "forge-0-epic":
532
+ raise UsageError(
533
+ f"--next-feature is accepted only for forge-0-epic, not {stage}"
534
+ )
535
+
536
+ # A loop that did not complete has NO production successor and owes no
537
+ # implementation verification yet. Everything downstream is suppressed below —
538
+ # `nextStage`/`nextCommand`, the epic-reconcile deferred line, the in-stage
539
+ # verify chain, its debt write, and the verify gate — because each of them
540
+ # would assert that the implementation is finished enough to move on, which is
541
+ # exactly the readiness claim REQ-PROD-02 forbids.
542
+ loop_incomplete = stage == "forge-5-loop" and outcome != "complete"
543
+
544
+ config = load_effective_config(config_path)
545
+ invalid_keys = invalid_auto_verify_keys(config)
546
+ for key in invalid_keys: # already sorted; advisory, never fatal
547
+ print(
548
+ INVALID_AUTO_VERIFY_KEY_WARNING.format(
549
+ key=key, valid=", ".join(VERIFY_TOKEN_BY_STAGE)
550
+ ),
551
+ file=sys.stderr,
552
+ )
553
+ feature_dir = _resolve_feature_dir(specs_dir, feature, epic)
554
+ state = _read_state(feature_dir / PIPELINE_STATE_FILENAME)
555
+
556
+ # Epic edit-mode: resolve the SELECTED member's live progress here, before
557
+ # the scheduling boundary below, so an ambiguous identity exits 2 without having
558
+ # mutated anything. `--next-feature` is accepted only for `forge-0-epic` (step 1),
559
+ # so this is exactly the epic edit-mode selection. Read-only: no candidate state
560
+ # file is opened for writing on this path.
561
+ member_state: dict = {}
562
+ member_reason: str | None = None
563
+ if next_feature is not None:
564
+ member_state, member_reason = _epic_member_state(specs_dir, feature, next_feature)
565
+
566
+ # The clean-tree snapshot is taken HERE, before the sanctioned debt write
567
+ # below, so the pending marker cannot dirty its own precondition.
568
+ # Every other directive is likewise a pre-mutation snapshot; only
569
+ # `autoVerifyDebtRecorded` describes what the write did.
570
+ git_repo = _git_output(["rev-parse", "--git-dir"]) is not None
571
+ clean_tree: bool | None = None
572
+ if git_repo:
573
+ porcelain = _git_output(["status", "--porcelain"])
574
+ clean_tree = porcelain is None or porcelain == ""
575
+
576
+ # A branch exit routes from the production stage it SERVED, never from itself:
577
+ # `forge-verify` has no artifact, no verify token, and no successor of its own.
578
+ # For a production exit the two are the same stage, so stages 0-4 are unchanged.
579
+ route_stage = resolved_served if resolved_served is not None else stage
580
+
581
+ # Verification context for the routed stage. An epic-scoped route reads
582
+ # `.epic-state.json` and the manifest revision DIRECTLY — never
583
+ # `_resolve_feature_dir`, never a member stage version (REQ-SEC-01).
584
+ verify_token = _EXIT_VERIFY_TOKEN.get(route_stage)
585
+ verify_key = f"forge-verify-{verify_token}" if verify_token else None
586
+ if route_stage == "forge-0-epic":
587
+ # EPIC-scoped: the entry and the revision come from `.epic-state.json` and the
588
+ # manifest, never from a member stage version, so this branch cannot route
589
+ # through the stage-scoped helper below. `forge-0-epic` always has a token.
590
+ verify_entry, verify_current = _epic_verify_context(specs_dir, feature)
591
+ verify_label = _classify_verify_entry(verify_entry, verify_key, verify_current)
592
+ else:
593
+ verify_entry = _verify_entry(state, verify_key) if verify_key else {}
594
+ verify_current = _stage_version(state, route_stage) if verify_key else None
595
+ # Classify through `_verify_state_for`, the designated stage-exit routing
596
+ # classifier, rather than re-deriving its two steps inline. The inline copy
597
+ # left `_verify_state_for` with no runtime
598
+ # caller, so `tests/test_auto_verify.py` could pin routing labels through a
599
+ # function the CLI never executed. It repeats the `_EXIT_VERIFY_TOKEN` lookup
600
+ # and `_classify_verify_entry` call above and returns "none" for a tokenless
601
+ # stage (forge-6-docs), where there is no verification to owe.
602
+ verify_label = _verify_state_for(state, route_stage)
603
+ # ``none`` is resolved for routing purposes: no verify command is promoted.
604
+ resolved = verify_label in ("fresh", "skipped", "none")
605
+ # A findings report AT THE CURRENT revision is live evidence, not owed debt.
606
+ # Scheduling over it would REPLACE the entry (`_verify_result_entry` builds
607
+ # replacements, not patches) and delete `findingsFile`/`findingsCount` —
608
+ # the same REQ-EXIT-04 clobber the branch-exit guard below forbids, reached
609
+ # instead from a production re-exit. The outstanding obligation is the FIX,
610
+ # so this exit routes to forge-fix and never re-schedules; a report left
611
+ # behind by a since-revised artifact is superseded normally.
612
+ reported_version = verify_entry.get("verifiedStageVersion")
613
+ live_findings_report = (
614
+ verify_label == "failing"
615
+ and isinstance(reported_version, int)
616
+ and not isinstance(reported_version, bool)
617
+ and verify_current is not None
618
+ and reported_version == verify_current
619
+ )
620
+ effective_auto_verify = auto_verify_for(config, route_stage)
621
+ # A BRANCH exit is already inside the verification diversion, so it never owes
622
+ # an in-stage verify chain and never schedules debt. Without this a
623
+ # `forge-verify --outcome findings` exit would both direct a re-dispatch of
624
+ # itself and overwrite the `findings-reported` entry it had just written with
625
+ # a fresh `auto-verify-pending` marker, losing the report (REQ-EXIT-04).
626
+ # Branch rejoin routing belongs to the outcome tables, not this boundary.
627
+ run_in_stage = (
628
+ effective_auto_verify
629
+ and not resolved
630
+ and not live_findings_report
631
+ and stage not in _BRANCH_STAGES
632
+ and not loop_incomplete
633
+ )
634
+ auto_fix_eligible = (
635
+ config.get("autoFix") is True and run_in_stage and clean_tree is True
636
+ )
637
+
638
+ # ---- Scheduling boundary ---------------------------------------------- #
639
+ # The debt lands BEFORE the payload exists, so a crash between here and the
640
+ # dispatch leaves durable state exposing the obligation, and a failed write
641
+ # raises UsageError with no payload at all — `runInStageVerify: True` with
642
+ # `autoVerifyDebtRecorded: False` is therefore unreachable.
643
+ auto_verify_debt_recorded = False
644
+ if run_in_stage and verify_key is not None:
645
+ _schedule_auto_verify_debt(specs_dir, feature, epic, route_stage, verify_key)
646
+ auto_verify_debt_recorded = True
647
+ # Priority table. The gate is a pure function of the verification state
648
+ # and the caller's declared capability: `--host` selects command syntax and
649
+ # fresh-session wording ONLY. A capable Pi session gets `standard`; an
650
+ # incapable Claude session gets `manual-print` (REQ-EXIT-07). Whether the
651
+ # caller needed user consent to dispatch is the CALLER's determination
652
+ # and is invisible here — a consent-required caller sends
653
+ # `interactive` and gets `standard`, which is the intended path.
654
+ #
655
+ # A BRANCH exit is already inside the diversion and its outcome table
656
+ # names the one action to take, so there is nothing left to gate: offering
657
+ # "verify now?" beside a fenced fix command would be a second, contradictory
658
+ # ask. The table's `verify` routes ARE the verification prompt.
659
+ #
660
+ # A non-complete loop outcome is gateless for the same reason it never
661
+ # schedules debt: there is no finished implementation to verify, so offering
662
+ # "verify now?" beside a fenced loop resume would ask for a verification of
663
+ # work that is still in flight.
664
+ # A live findings report is likewise gateless: the fenced forge-fix route IS
665
+ # the one action, and a "verify now?" prompt beside it would be a second,
666
+ # contradictory ask for a verification that already ran at this revision.
667
+ if (
668
+ resolved
669
+ or run_in_stage
670
+ or live_findings_report
671
+ or stage in _BRANCH_STAGES
672
+ or loop_incomplete
673
+ ):
674
+ verify_gate = "none"
675
+ elif verify_capability == "interactive":
676
+ verify_gate = "standard"
677
+ else:
678
+ verify_gate = "manual-print"
679
+
680
+ next_stage_id = _EXIT_NEXT_STAGE.get(route_stage)
681
+ state_next = next_stage(state)
682
+ if (
683
+ route_stage in PRODUCTION_STAGES
684
+ and state_next is not None
685
+ and PRODUCTION_STAGES.index(state_next) > PRODUCTION_STAGES.index(route_stage)
686
+ ):
687
+ # State records this stage complete AND its walk lands beyond it —
688
+ # trust it (it skips stages already completed out of order). A missing
689
+ # or behind-the-stage walk (state not yet flushed, corrupt file) falls
690
+ # back to the fixed successor, never to an earlier stage.
691
+ next_stage_id = state_next
692
+ # Keyed off the ROUTED stage, so a branch exit that served the epic decomposition
693
+ # hands off the same way the epic's own exit does. Identical to the previous
694
+ # behavior for every production exit, where `route_stage is stage`.
695
+ # Set when live epic state says this epic is FINISHED (#248). Carried to the single
696
+ # application point after the primary chain, so verify-first ordering, a live
697
+ # findings report, and a blocking reconcile all keep their precedence — a terminal
698
+ # epic is only ever allowed to replace a dashboard self-loop, never a real action.
699
+ epic_terminal: tuple[str, dict] | None = None
700
+ if route_stage == "forge-0-epic" and next_feature is None:
701
+ # A branch exit (verify/fix) that served forge-0-epic cannot carry
702
+ # --next-feature (the CLI rejects it for non-epic stages), so the member
703
+ # must be resolved HERE from live epic state. Creation-mode exits pass
704
+ # --next-feature and skip this block entirely (#230).
705
+ try:
706
+ status = _render_status(specs_dir, feature)
707
+ actionable = status["actionable"]
708
+ epic_terminal = _epic_terminal_state(status, feature)
709
+ if actionable:
710
+ resolved_member = actionable[0]
711
+ ms, mr = _epic_member_state(specs_dir, feature, resolved_member)
712
+ if mr is not None:
713
+ next_stage_id = "forge-1-prd"
714
+ next_command = f"/skill:forge-1-prd {resolved_member}"
715
+ else:
716
+ member_next = next_stage(ms)
717
+ if member_next is None:
718
+ # An ACTIONABLE member with no next production stage has
719
+ # finished all six but is not complete for orchestration —
720
+ # it owes verification (`auto-verify-pending`) or carries an
721
+ # unapplied report (`findings-reported`). Handing back the
722
+ # dashboard here was the #248 self-loop on a not-quite-complete
723
+ # epic: the dashboard's own exit reproduces this state and
724
+ # re-fences itself. `render-status` already resolved the right
725
+ # answer for exactly this member — `forge-verify` or
726
+ # `forge-fix`, per its own debt rules — so it is used rather
727
+ # than re-derived here (derivation belongs to epic-manifest.py).
728
+ #
729
+ # It is USUALLY a branch command, but not always: the helper
730
+ # answers `forge-1-prd` for a member whose `PRD.md` is missing
731
+ # from disk, ahead of its debt fork. So `nextStage` is read back
732
+ # OUT of the command it chose rather than assumed None —
733
+ # reporting a production command with `nextStage: null` would
734
+ # break this payload's own contract (see `nextCommand` above).
735
+ next_command = (
736
+ status["nextCommand"]
737
+ or f"/skill:forge-0-epic {feature}"
738
+ )
739
+ next_stage_id = _production_stage_of(next_command)
740
+ else:
741
+ next_stage_id = member_next
742
+ next_command = f"/skill:{member_next} {resolved_member}"
743
+ else:
744
+ next_stage_id = None
745
+ next_command = f"/skill:forge-0-epic {feature}"
746
+ except UsageError:
747
+ next_stage_id = None
748
+ next_command = f"/skill:forge-0-epic {feature}"
749
+ else:
750
+ next_arg = next_feature or feature
751
+ next_command = (
752
+ f"/skill:{next_stage_id} {next_arg}" if next_stage_id else None
753
+ )
754
+
755
+ # ---- Epic edit-mode live member routing (issue #175) -------------------- #
756
+ # The fixed `forge-0-epic -> forge-1-prd` successor above is a CREATION-mode
757
+ # answer: a member that has just been decomposed has no completed production
758
+ # stage, so PRD is right. In edit mode the selected member may be anywhere in
759
+ # the pipeline, and sending it back to PRD would ask for work already done.
760
+ # The live position comes from the member's own state via `next_stage`, never
761
+ # from the epic's state, the successor table, or conversational context.
762
+ epic_member_warning: str | None = None
763
+ if next_feature is not None:
764
+ if member_reason is not None:
765
+ # Degrade DOWN, never up: an unreadable member cannot be assumed to
766
+ # have progressed, and inferring a later stage would fabricate the
767
+ # very progress this exit failed to read (REQ-PROD-06).
768
+ epic_member_warning = EPIC_MEMBER_FALLBACK_WARNING.format(
769
+ member=next_feature, epic=feature, reason=member_reason
770
+ )
771
+ next_stage_id = "forge-1-prd"
772
+ next_command = f"/skill:forge-1-prd {next_feature}"
773
+ else:
774
+ member_next = next_stage(member_state)
775
+ if member_next is None:
776
+ # Every production stage is complete. There is no stage 7 to
777
+ # fabricate, so the handoff is the epic dashboard itself — unless the
778
+ # EPIC is finished too, in which case that hand-back is the #248
779
+ # self-loop and the exit goes terminal instead. Only this path can
780
+ # reach the dashboard, so it is the only one that pays for the extra
781
+ # render-status read; an unreadable graph degrades to the hand-back
782
+ # rather than converting a stage closing into a failure.
783
+ next_stage_id = None
784
+ next_command = f"/skill:forge-0-epic {feature}"
785
+ try:
786
+ epic_terminal = _epic_terminal_state(
787
+ _render_status(specs_dir, feature), feature
788
+ )
789
+ except UsageError:
790
+ epic_terminal = None
791
+ else:
792
+ next_stage_id = member_next
793
+ next_command = f"/skill:{member_next} {next_feature}"
794
+
795
+ if loop_incomplete:
796
+ # The pipeline has no next production stage from here, exactly as it has
797
+ # none after `forge-6-docs`. Cleared BEFORE the epic-backflow block below,
798
+ # so a blocking reconcile's `deferred` line cannot re-introduce
799
+ # `/skill:forge-6-docs` as text the loop resume did not earn.
800
+ next_stage_id = None
801
+ next_command = None
802
+
803
+ # Epic backflow routing: an exiting member may carry epic-level change requests
804
+ # (recorded by forge-1-prd/forge-2-tech). A `blocksCurrent: true` request means
805
+ # the current feature's next stage would build on a soon-to-change decomposition,
806
+ # so the exit interposes a reconcile-first step; only-`false` requests append a
807
+ # non-blocking reminder. Read-only; the common path (no open requests) is a no-op.
808
+ # The epic name comes from the `--epic` arg or the state's `epic` back-pointer.
809
+ epic_reconcile: dict | None = None
810
+ epic_name = epic or state.get("epic")
811
+ # The epic a documentation or completed-loop exit routes against:
812
+ # the explicit `--epic`, else the state's back-pointer. A back-pointer is
813
+ # untrusted on-disk data, so it is name-checked here rather than reaching the
814
+ # helper's argv (REQ-SEC-01); an unusable value degrades to the standalone route
815
+ # rather than crashing a stage closing. `--epic` itself was already validated in
816
+ # step 1.
817
+ route_epic = (
818
+ epic_name if isinstance(epic_name, str) and SAFE_NAME_RE.match(epic_name) else None
819
+ )
820
+ open_requests = [
821
+ r
822
+ for r in state.get("epicChangeRequests", [])
823
+ if isinstance(r, dict) and r.get("status") == "open"
824
+ ]
825
+ if open_requests and epic_name:
826
+ reconcile_command = f"/skill:forge-0-epic {epic_name}"
827
+ blocking = [r for r in open_requests if r.get("blocksCurrent") is True]
828
+ if blocking:
829
+ epic_reconcile = {
830
+ "required": True,
831
+ "command": reconcile_command,
832
+ "count": len(blocking),
833
+ "deferred": next_command,
834
+ }
835
+ else:
836
+ epic_reconcile = {
837
+ "required": False,
838
+ "reminder": True,
839
+ "command": reconcile_command,
840
+ "count": len(open_requests),
841
+ }
842
+
843
+ # ---- Verify-first primary routing ------------------------------------- #
844
+ # While verification is unresolved the verify command is THE action — except
845
+ # under a live findings report, whose one action is the forge-fix that applies
846
+ # it. Either way the production successor is demoted to unfenced conditional
847
+ # prose. No path may fence or recommend the deferred production command first
848
+ # (REQ-EXIT-06).
849
+ verify_canonical = f"/skill:forge-verify {feature}"
850
+ # The one action a live findings report promotes, on every route that can
851
+ # reach it: findings already exist at this exact revision, so re-dispatching
852
+ # verify would only restate them. The served stage is carried so the fix
853
+ # rejoins this production thread.
854
+ fix_canonical = f"/skill:forge-fix {feature} --served-stage {route_stage}"
855
+ verify_command = _host_command(verify_canonical, host)
856
+ blocking_reconcile = bool(epic_reconcile and epic_reconcile.get("required"))
857
+ primary_canonical: str | None
858
+ deferred_canonical: str | None
859
+ outcome_text: str | None = None
860
+ if stage in _BRANCH_STAGES:
861
+ # The outcome table alone decides a branch terminus — verify-first
862
+ # ordering does not apply, because the branch IS the verification work.
863
+ primary_canonical, deferred_canonical, outcome_text, advancing = _branch_route(
864
+ stage,
865
+ outcome,
866
+ feature,
867
+ resolved_served,
868
+ next_command,
869
+ resolved,
870
+ )
871
+ if advancing and blocking_reconcile:
872
+ # An advancing rejoin is subject to the same reconcile-first rule as a
873
+ # production exit; a non-advancing one already outranks the reconcile.
874
+ primary_canonical = epic_reconcile["command"]
875
+ deferred_canonical = None
876
+ elif stage == "forge-5-loop":
877
+ # Every loop result gets a deterministic resume or recovery action.
878
+ # `complete` keeps verify-first ordering (the table applies it to its own
879
+ # handoff); the other four never reach a production stage at all.
880
+ primary_canonical, deferred_canonical, outcome_text, advancing = _loop_route(
881
+ outcome,
882
+ feature,
883
+ route_epic,
884
+ specs_dir,
885
+ next_command,
886
+ resolved,
887
+ verify_canonical,
888
+ fix_canonical if live_findings_report else None,
889
+ cause,
890
+ )
891
+ if blocking_reconcile:
892
+ # Same reconcile-first rule as every other advancing route — but the
893
+ # continuation carried forward is the LOOP's, not the successor table's
894
+ # documentation stage, which this route deliberately did not choose.
895
+ primary_canonical, deferred_canonical, outcome_text = _promote_reconcile(
896
+ stage,
897
+ epic_reconcile,
898
+ feature,
899
+ epic_name,
900
+ primary_canonical,
901
+ deferred_canonical,
902
+ outcome_text,
903
+ advancing,
904
+ )
905
+ elif stage == "forge-6-docs":
906
+ # The documentation terminus is decided by LIVE epic state, not by
907
+ # the successor table — the pipeline ends here, so there is no next stage
908
+ # to fence and no verification to put first (docs is tokenless).
909
+ primary_canonical, deferred_canonical, outcome_text, advancing = _docs_route(
910
+ feature,
911
+ route_epic,
912
+ specs_dir,
913
+ outcome,
914
+ host,
915
+ )
916
+ if blocking_reconcile:
917
+ # Same reconcile-first rule as an advancing branch rejoin: handing off to
918
+ # the next member would build it on a decomposition that is about to
919
+ # change. A non-advancing docs route already lands on the epic itself. The
920
+ # successor table has no entry for this stage, so its seeded continuation
921
+ # is None — the live route's own primary is what must be carried forward.
922
+ primary_canonical, deferred_canonical, outcome_text = _promote_reconcile(
923
+ stage,
924
+ epic_reconcile,
925
+ feature,
926
+ epic_name,
927
+ primary_canonical,
928
+ deferred_canonical,
929
+ outcome_text,
930
+ advancing,
931
+ )
932
+ elif live_findings_report:
933
+ primary_canonical = fix_canonical
934
+ deferred_canonical = next_command
935
+ elif not resolved:
936
+ primary_canonical = verify_canonical
937
+ deferred_canonical = next_command
938
+ elif blocking_reconcile:
939
+ # Verification is settled, so the blocking reconcile is the primary
940
+ # action and `epicReconcile["deferred"]` carries the demoted successor.
941
+ primary_canonical = epic_reconcile["command"]
942
+ deferred_canonical = None
943
+ else:
944
+ primary_canonical = next_command or "/skill:forge"
945
+ deferred_canonical = None
946
+
947
+ # ---- Terminal epic exit (issue #248) ---------------------------------- #
948
+ # A finished epic gets a genuinely terminal block: no fenced command at all,
949
+ # because every command this exit could fence is the dashboard it was just run
950
+ # from. The equality guard below is what makes this safe to apply so late — it
951
+ # fires ONLY where the routing above already landed on the epic dashboard, so
952
+ # verify-first ordering (primary is the verify command), a live findings report
953
+ # (primary is the fix), and a `blocked` docs recovery all keep their precedence
954
+ # untouched. `not blocking_reconcile` is a SEPARATE, load-bearing guard rather than
955
+ # a restatement of anything above: the reconcile command is the same STRING as the
956
+ # dashboard when the exiting feature IS the epic, so without it a blocking reconcile
957
+ # would be silently swallowed by the equality test.
958
+ #
959
+ # A NON-blocking reminder does not block the terminal answer, and must not: fencing
960
+ # the dashboard for an optional reconcile reinstates the #248 loop (decline it,
961
+ # re-run, get the same block) while promoting a change the exit itself calls
962
+ # non-blocking to the one required action. The reminder survives as the inline line
963
+ # `_next_steps_block` already renders for it — an offer beside "nothing is
964
+ # required", which is exactly what a non-blocking request is.
965
+ epic_dashboard = f"/skill:forge-0-epic {feature}"
966
+ if (
967
+ epic_terminal is not None
968
+ and not blocking_reconcile
969
+ and primary_canonical == epic_dashboard
970
+ ):
971
+ terminal_kind, terminal_fields = epic_terminal
972
+ terminal_text = _EPIC_TERMINAL_TEXT[terminal_kind].format(
973
+ **terminal_fields,
974
+ new_feature=_host_command("/skill:forge-1-prd <new-feature>", host),
975
+ new_epic=_host_command("/skill:forge-0-epic <new-epic>", host),
976
+ dashboard=_host_command(epic_dashboard, host),
977
+ )
978
+ # A branch exit arrives with its own outcome sentence ("verification passed…"),
979
+ # which is still true and still worth saying; the terminal sentence follows it.
980
+ outcome_text = f"{outcome_text} {terminal_text}" if outcome_text else terminal_text
981
+ primary_canonical = None
982
+ deferred_canonical = None
983
+ # Downstream signals are suppressed for the same reason the fence is: there is
984
+ # no next stage, so `nextCommand` must not offer one for a consumer to promote.
985
+ next_command = None
986
+ next_stage_id = None
987
+
988
+ # Fixed order: entry 1 is the epic-member unreadable-state fallback,
989
+ # then the debt-metadata and revision-mismatch entries.
990
+ warnings: list[str] = []
991
+ if epic_member_warning is not None:
992
+ warnings.append(epic_member_warning)
993
+ warnings.extend(
994
+ _debt_metadata_warnings(
995
+ verify_entry, verify_key, route_stage, feature, verify_command, verify_current
996
+ )
997
+ )
998
+
999
+ # `owner == "nested"` means an outer authoring stage prints the terminal block.
1000
+ # The routing directives survive; the human-facing block does not exist at all,
1001
+ # so a nested chain can never emit a second sentinel (REQ-EXIT-03/04).
1002
+ nested = owner == "nested"
1003
+
1004
+ directives = {
1005
+ "stage": stage,
1006
+ "stageNoun": STAGE_NOUN.get(stage, stage),
1007
+ "servedStage": resolved_served,
1008
+ "verifyMode": _STAGE_TO_VERIFY_MODE.get(resolved_served or ""),
1009
+ "outcome": outcome,
1010
+ "owner": owner,
1011
+ "terminalOwnedBy": "outer" if nested else "self",
1012
+ "feature": feature,
1013
+ "runInStageVerify": run_in_stage,
1014
+ "verifyGate": verify_gate,
1015
+ "verifyCapability": verify_capability,
1016
+ "autoFixEligible": auto_fix_eligible,
1017
+ "verifyState": verify_label,
1018
+ "verifyStage": pending_verify(state),
1019
+ "verifyCommand": verify_command,
1020
+ "autoVerifyEffective": effective_auto_verify,
1021
+ "autoVerifyDebtRecorded": auto_verify_debt_recorded,
1022
+ "nextStage": next_stage_id,
1023
+ "nextCommand": _host_command(next_command, host) if next_command else next_command,
1024
+ "primaryCommand": (
1025
+ _host_command(primary_canonical, host) if primary_canonical else None
1026
+ ),
1027
+ "deferredCommand": (
1028
+ _host_command(deferred_canonical, host) if deferred_canonical else None
1029
+ ),
1030
+ "invalidAutoVerifyKeys": invalid_keys,
1031
+ "warnings": warnings,
1032
+ "gitRepo": git_repo,
1033
+ "cleanTree": clean_tree,
1034
+ "host": host,
1035
+ }
1036
+ if epic_reconcile is not None:
1037
+ directives["epicReconcile"] = epic_reconcile
1038
+ if nested:
1039
+ return {"directives": directives, "nextSteps": None, "sentinel": None}
1040
+ return {
1041
+ "directives": directives,
1042
+ "nextSteps": _next_steps_block(
1043
+ primary_canonical,
1044
+ host,
1045
+ epic_reconcile,
1046
+ deferred_command=deferred_canonical,
1047
+ outcome_text=outcome_text,
1048
+ ),
1049
+ "sentinel": NEXT_STEPS_SENTINEL,
1050
+ }
1051
+ def _print_stage_exit(payload: dict) -> None:
1052
+ """Print DIRECTIVES then the NEXT-STEPS block (the skill-facing form).
1053
+
1054
+ A NESTED branch payload carries ``nextSteps is None``: an outer authoring stage
1055
+ owns the terminal block, so this printer emits the directives and stops. Printing
1056
+ a terminal section here — even an empty one — is the ownership leak REQ-EXIT-04
1057
+ forbids.
1058
+ """
1059
+ print("DIRECTIVES:")
1060
+ print(json.dumps(payload["directives"], indent=2, ensure_ascii=False))
1061
+ if payload.get("nextSteps") is None:
1062
+ return
1063
+ print(
1064
+ "NEXT-STEPS (print this block verbatim as your absolute last output — "
1065
+ "nothing after the sentinel):"
1066
+ )
1067
+ print(payload["nextSteps"])
1068
+
1069
+ __all__ = [
1070
+ "_EXIT_PRODUCTION_STAGES",
1071
+ "_BRANCH_STAGES",
1072
+ "_STAGE_TO_VERIFY_MODE",
1073
+ "_EXIT_NEXT_STAGE",
1074
+ "_epic_verify_context",
1075
+ "_same_named_candidates",
1076
+ "_epic_member_state",
1077
+ "resolve_served_stage",
1078
+ "_EPIC_TERMINAL_TEXT",
1079
+ "stage_exit",
1080
+ "_print_stage_exit",
1081
+ ]