@garygentry/feature-forge 0.2.13 → 0.2.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/adapters/claude/.feature-forge-bundle.json +1 -1
  2. package/adapters/claude/references/epic-manifest-schema.json +5 -0
  3. package/adapters/claude/references/shared-conventions.md +12 -0
  4. package/adapters/claude/references/stacks/_generic.md +21 -0
  5. package/adapters/claude/references/stacks/go.md +19 -0
  6. package/adapters/claude/references/stacks/python.md +21 -0
  7. package/adapters/claude/references/stacks/rust.md +19 -0
  8. package/adapters/claude/references/stacks/typescript.md +23 -0
  9. package/adapters/claude/scripts/epic-manifest.py +75 -5
  10. package/adapters/claude/scripts/forge-root.sh +55 -11
  11. package/adapters/claude/scripts/forge-session.py +38 -1
  12. package/adapters/claude/skills/forge/references/shared-conventions.md +12 -0
  13. package/adapters/claude/skills/forge-0-epic/references/shared-conventions.md +12 -0
  14. package/adapters/claude/skills/forge-1-prd/SKILL.md +2 -0
  15. package/adapters/claude/skills/forge-1-prd/references/shared-conventions.md +12 -0
  16. package/adapters/claude/skills/forge-2-tech/SKILL.md +2 -0
  17. package/adapters/claude/skills/forge-2-tech/references/shared-conventions.md +12 -0
  18. package/adapters/claude/skills/forge-2-tech/references/stacks/_generic.md +21 -0
  19. package/adapters/claude/skills/forge-2-tech/references/stacks/go.md +19 -0
  20. package/adapters/claude/skills/forge-2-tech/references/stacks/python.md +21 -0
  21. package/adapters/claude/skills/forge-2-tech/references/stacks/rust.md +19 -0
  22. package/adapters/claude/skills/forge-2-tech/references/stacks/typescript.md +23 -0
  23. package/adapters/claude/skills/forge-3-specs/SKILL.md +2 -0
  24. package/adapters/claude/skills/forge-3-specs/references/shared-conventions.md +12 -0
  25. package/adapters/claude/skills/forge-3-specs/references/stacks/_generic.md +21 -0
  26. package/adapters/claude/skills/forge-3-specs/references/stacks/go.md +19 -0
  27. package/adapters/claude/skills/forge-3-specs/references/stacks/python.md +21 -0
  28. package/adapters/claude/skills/forge-3-specs/references/stacks/rust.md +19 -0
  29. package/adapters/claude/skills/forge-3-specs/references/stacks/typescript.md +23 -0
  30. package/adapters/claude/skills/forge-4-backlog/SKILL.md +5 -0
  31. package/adapters/claude/skills/forge-4-backlog/references/shared-conventions.md +12 -0
  32. package/adapters/claude/skills/forge-5-loop/SKILL.md +7 -9
  33. package/adapters/claude/skills/forge-5-loop/references/runner-contract.md +41 -0
  34. package/adapters/claude/skills/forge-5-loop/references/shared-conventions.md +12 -0
  35. package/adapters/claude/skills/forge-6-docs/references/shared-conventions.md +12 -0
  36. package/adapters/claude/skills/forge-fix/references/shared-conventions.md +12 -0
  37. package/adapters/claude/skills/forge-guide/references/shared-conventions.md +12 -0
  38. package/adapters/claude/skills/forge-guide/references/stacks/_generic.md +21 -0
  39. package/adapters/claude/skills/forge-guide/references/stacks/go.md +19 -0
  40. package/adapters/claude/skills/forge-guide/references/stacks/python.md +21 -0
  41. package/adapters/claude/skills/forge-guide/references/stacks/rust.md +19 -0
  42. package/adapters/claude/skills/forge-guide/references/stacks/typescript.md +23 -0
  43. package/adapters/claude/skills/forge-verify/SKILL.md +3 -3
  44. package/adapters/claude/skills/forge-verify/references/shared-conventions.md +12 -0
  45. package/adapters/claude/skills/forge-verify/references/verification-checklists.md +82 -3
  46. package/adapters/codex/.feature-forge-bundle.json +1 -1
  47. package/adapters/codex/references/epic-manifest-schema.json +5 -0
  48. package/adapters/codex/references/shared-conventions.md +12 -0
  49. package/adapters/codex/references/stacks/_generic.md +21 -0
  50. package/adapters/codex/references/stacks/go.md +19 -0
  51. package/adapters/codex/references/stacks/python.md +21 -0
  52. package/adapters/codex/references/stacks/rust.md +19 -0
  53. package/adapters/codex/references/stacks/typescript.md +23 -0
  54. package/adapters/codex/scripts/epic-manifest.py +75 -5
  55. package/adapters/codex/scripts/forge-root.sh +55 -11
  56. package/adapters/codex/scripts/forge-session.py +38 -1
  57. package/adapters/codex/skills/forge/references/shared-conventions.md +12 -0
  58. package/adapters/codex/skills/forge-0-epic/references/shared-conventions.md +12 -0
  59. package/adapters/codex/skills/forge-1-prd/SKILL.md +2 -0
  60. package/adapters/codex/skills/forge-1-prd/references/shared-conventions.md +12 -0
  61. package/adapters/codex/skills/forge-2-tech/SKILL.md +2 -0
  62. package/adapters/codex/skills/forge-2-tech/references/shared-conventions.md +12 -0
  63. package/adapters/codex/skills/forge-2-tech/references/stacks/_generic.md +21 -0
  64. package/adapters/codex/skills/forge-2-tech/references/stacks/go.md +19 -0
  65. package/adapters/codex/skills/forge-2-tech/references/stacks/python.md +21 -0
  66. package/adapters/codex/skills/forge-2-tech/references/stacks/rust.md +19 -0
  67. package/adapters/codex/skills/forge-2-tech/references/stacks/typescript.md +23 -0
  68. package/adapters/codex/skills/forge-3-specs/SKILL.md +2 -0
  69. package/adapters/codex/skills/forge-3-specs/references/shared-conventions.md +12 -0
  70. package/adapters/codex/skills/forge-3-specs/references/stacks/_generic.md +21 -0
  71. package/adapters/codex/skills/forge-3-specs/references/stacks/go.md +19 -0
  72. package/adapters/codex/skills/forge-3-specs/references/stacks/python.md +21 -0
  73. package/adapters/codex/skills/forge-3-specs/references/stacks/rust.md +19 -0
  74. package/adapters/codex/skills/forge-3-specs/references/stacks/typescript.md +23 -0
  75. package/adapters/codex/skills/forge-4-backlog/SKILL.md +5 -0
  76. package/adapters/codex/skills/forge-4-backlog/references/shared-conventions.md +12 -0
  77. package/adapters/codex/skills/forge-5-loop/SKILL.md +7 -9
  78. package/adapters/codex/skills/forge-5-loop/references/runner-contract.md +41 -0
  79. package/adapters/codex/skills/forge-5-loop/references/shared-conventions.md +12 -0
  80. package/adapters/codex/skills/forge-6-docs/references/shared-conventions.md +12 -0
  81. package/adapters/codex/skills/forge-fix/references/shared-conventions.md +12 -0
  82. package/adapters/codex/skills/forge-guide/references/shared-conventions.md +12 -0
  83. package/adapters/codex/skills/forge-guide/references/stacks/_generic.md +21 -0
  84. package/adapters/codex/skills/forge-guide/references/stacks/go.md +19 -0
  85. package/adapters/codex/skills/forge-guide/references/stacks/python.md +21 -0
  86. package/adapters/codex/skills/forge-guide/references/stacks/rust.md +19 -0
  87. package/adapters/codex/skills/forge-guide/references/stacks/typescript.md +23 -0
  88. package/adapters/codex/skills/forge-verify/SKILL.md +3 -3
  89. package/adapters/codex/skills/forge-verify/references/shared-conventions.md +12 -0
  90. package/adapters/codex/skills/forge-verify/references/verification-checklists.md +82 -3
  91. package/adapters/copilot/.feature-forge-bundle.json +1 -1
  92. package/adapters/copilot/references/epic-manifest-schema.json +5 -0
  93. package/adapters/copilot/references/shared-conventions.md +12 -0
  94. package/adapters/copilot/references/stacks/_generic.md +21 -0
  95. package/adapters/copilot/references/stacks/go.md +19 -0
  96. package/adapters/copilot/references/stacks/python.md +21 -0
  97. package/adapters/copilot/references/stacks/rust.md +19 -0
  98. package/adapters/copilot/references/stacks/typescript.md +23 -0
  99. package/adapters/copilot/scripts/epic-manifest.py +75 -5
  100. package/adapters/copilot/scripts/forge-root.sh +55 -11
  101. package/adapters/copilot/scripts/forge-session.py +38 -1
  102. package/adapters/copilot/skills/forge/references/shared-conventions.md +12 -0
  103. package/adapters/copilot/skills/forge-0-epic/references/shared-conventions.md +12 -0
  104. package/adapters/copilot/skills/forge-1-prd/forge-1-prd.md +2 -0
  105. package/adapters/copilot/skills/forge-1-prd/references/shared-conventions.md +12 -0
  106. package/adapters/copilot/skills/forge-2-tech/forge-2-tech.md +2 -0
  107. package/adapters/copilot/skills/forge-2-tech/references/shared-conventions.md +12 -0
  108. package/adapters/copilot/skills/forge-2-tech/references/stacks/_generic.md +21 -0
  109. package/adapters/copilot/skills/forge-2-tech/references/stacks/go.md +19 -0
  110. package/adapters/copilot/skills/forge-2-tech/references/stacks/python.md +21 -0
  111. package/adapters/copilot/skills/forge-2-tech/references/stacks/rust.md +19 -0
  112. package/adapters/copilot/skills/forge-2-tech/references/stacks/typescript.md +23 -0
  113. package/adapters/copilot/skills/forge-3-specs/forge-3-specs.md +2 -0
  114. package/adapters/copilot/skills/forge-3-specs/references/shared-conventions.md +12 -0
  115. package/adapters/copilot/skills/forge-3-specs/references/stacks/_generic.md +21 -0
  116. package/adapters/copilot/skills/forge-3-specs/references/stacks/go.md +19 -0
  117. package/adapters/copilot/skills/forge-3-specs/references/stacks/python.md +21 -0
  118. package/adapters/copilot/skills/forge-3-specs/references/stacks/rust.md +19 -0
  119. package/adapters/copilot/skills/forge-3-specs/references/stacks/typescript.md +23 -0
  120. package/adapters/copilot/skills/forge-4-backlog/forge-4-backlog.md +5 -0
  121. package/adapters/copilot/skills/forge-4-backlog/references/shared-conventions.md +12 -0
  122. package/adapters/copilot/skills/forge-5-loop/forge-5-loop.md +7 -9
  123. package/adapters/copilot/skills/forge-5-loop/references/runner-contract.md +41 -0
  124. package/adapters/copilot/skills/forge-5-loop/references/shared-conventions.md +12 -0
  125. package/adapters/copilot/skills/forge-6-docs/references/shared-conventions.md +12 -0
  126. package/adapters/copilot/skills/forge-fix/references/shared-conventions.md +12 -0
  127. package/adapters/copilot/skills/forge-guide/references/shared-conventions.md +12 -0
  128. package/adapters/copilot/skills/forge-guide/references/stacks/_generic.md +21 -0
  129. package/adapters/copilot/skills/forge-guide/references/stacks/go.md +19 -0
  130. package/adapters/copilot/skills/forge-guide/references/stacks/python.md +21 -0
  131. package/adapters/copilot/skills/forge-guide/references/stacks/rust.md +19 -0
  132. package/adapters/copilot/skills/forge-guide/references/stacks/typescript.md +23 -0
  133. package/adapters/copilot/skills/forge-verify/forge-verify.md +3 -3
  134. package/adapters/copilot/skills/forge-verify/references/shared-conventions.md +12 -0
  135. package/adapters/copilot/skills/forge-verify/references/verification-checklists.md +82 -3
  136. package/adapters/cursor/.feature-forge-bundle.json +1 -1
  137. package/adapters/cursor/references/epic-manifest-schema.json +5 -0
  138. package/adapters/cursor/references/shared-conventions.md +12 -0
  139. package/adapters/cursor/references/stacks/_generic.md +21 -0
  140. package/adapters/cursor/references/stacks/go.md +19 -0
  141. package/adapters/cursor/references/stacks/python.md +21 -0
  142. package/adapters/cursor/references/stacks/rust.md +19 -0
  143. package/adapters/cursor/references/stacks/typescript.md +23 -0
  144. package/adapters/cursor/scripts/epic-manifest.py +75 -5
  145. package/adapters/cursor/scripts/forge-root.sh +55 -11
  146. package/adapters/cursor/scripts/forge-session.py +38 -1
  147. package/adapters/cursor/skills/forge/references/shared-conventions.md +12 -0
  148. package/adapters/cursor/skills/forge-0-epic/references/shared-conventions.md +12 -0
  149. package/adapters/cursor/skills/forge-1-prd/forge-1-prd.mdc +2 -0
  150. package/adapters/cursor/skills/forge-1-prd/references/shared-conventions.md +12 -0
  151. package/adapters/cursor/skills/forge-2-tech/forge-2-tech.mdc +2 -0
  152. package/adapters/cursor/skills/forge-2-tech/references/shared-conventions.md +12 -0
  153. package/adapters/cursor/skills/forge-2-tech/references/stacks/_generic.md +21 -0
  154. package/adapters/cursor/skills/forge-2-tech/references/stacks/go.md +19 -0
  155. package/adapters/cursor/skills/forge-2-tech/references/stacks/python.md +21 -0
  156. package/adapters/cursor/skills/forge-2-tech/references/stacks/rust.md +19 -0
  157. package/adapters/cursor/skills/forge-2-tech/references/stacks/typescript.md +23 -0
  158. package/adapters/cursor/skills/forge-3-specs/forge-3-specs.mdc +2 -0
  159. package/adapters/cursor/skills/forge-3-specs/references/shared-conventions.md +12 -0
  160. package/adapters/cursor/skills/forge-3-specs/references/stacks/_generic.md +21 -0
  161. package/adapters/cursor/skills/forge-3-specs/references/stacks/go.md +19 -0
  162. package/adapters/cursor/skills/forge-3-specs/references/stacks/python.md +21 -0
  163. package/adapters/cursor/skills/forge-3-specs/references/stacks/rust.md +19 -0
  164. package/adapters/cursor/skills/forge-3-specs/references/stacks/typescript.md +23 -0
  165. package/adapters/cursor/skills/forge-4-backlog/forge-4-backlog.mdc +5 -0
  166. package/adapters/cursor/skills/forge-4-backlog/references/shared-conventions.md +12 -0
  167. package/adapters/cursor/skills/forge-5-loop/forge-5-loop.mdc +7 -9
  168. package/adapters/cursor/skills/forge-5-loop/references/runner-contract.md +41 -0
  169. package/adapters/cursor/skills/forge-5-loop/references/shared-conventions.md +12 -0
  170. package/adapters/cursor/skills/forge-6-docs/references/shared-conventions.md +12 -0
  171. package/adapters/cursor/skills/forge-fix/references/shared-conventions.md +12 -0
  172. package/adapters/cursor/skills/forge-guide/references/shared-conventions.md +12 -0
  173. package/adapters/cursor/skills/forge-guide/references/stacks/_generic.md +21 -0
  174. package/adapters/cursor/skills/forge-guide/references/stacks/go.md +19 -0
  175. package/adapters/cursor/skills/forge-guide/references/stacks/python.md +21 -0
  176. package/adapters/cursor/skills/forge-guide/references/stacks/rust.md +19 -0
  177. package/adapters/cursor/skills/forge-guide/references/stacks/typescript.md +23 -0
  178. package/adapters/cursor/skills/forge-verify/forge-verify.mdc +3 -3
  179. package/adapters/cursor/skills/forge-verify/references/shared-conventions.md +12 -0
  180. package/adapters/cursor/skills/forge-verify/references/verification-checklists.md +82 -3
  181. package/adapters/gemini/.feature-forge-bundle.json +1 -1
  182. package/adapters/gemini/gemini-extension.json +1 -1
  183. package/adapters/gemini/references/epic-manifest-schema.json +5 -0
  184. package/adapters/gemini/references/shared-conventions.md +12 -0
  185. package/adapters/gemini/references/stacks/_generic.md +21 -0
  186. package/adapters/gemini/references/stacks/go.md +19 -0
  187. package/adapters/gemini/references/stacks/python.md +21 -0
  188. package/adapters/gemini/references/stacks/rust.md +19 -0
  189. package/adapters/gemini/references/stacks/typescript.md +23 -0
  190. package/adapters/gemini/scripts/epic-manifest.py +75 -5
  191. package/adapters/gemini/scripts/forge-root.sh +55 -11
  192. package/adapters/gemini/scripts/forge-session.py +38 -1
  193. package/adapters/gemini/skills/forge/references/shared-conventions.md +12 -0
  194. package/adapters/gemini/skills/forge-0-epic/references/shared-conventions.md +12 -0
  195. package/adapters/gemini/skills/forge-1-prd/forge-1-prd.md +2 -0
  196. package/adapters/gemini/skills/forge-1-prd/references/shared-conventions.md +12 -0
  197. package/adapters/gemini/skills/forge-2-tech/forge-2-tech.md +2 -0
  198. package/adapters/gemini/skills/forge-2-tech/references/shared-conventions.md +12 -0
  199. package/adapters/gemini/skills/forge-2-tech/references/stacks/_generic.md +21 -0
  200. package/adapters/gemini/skills/forge-2-tech/references/stacks/go.md +19 -0
  201. package/adapters/gemini/skills/forge-2-tech/references/stacks/python.md +21 -0
  202. package/adapters/gemini/skills/forge-2-tech/references/stacks/rust.md +19 -0
  203. package/adapters/gemini/skills/forge-2-tech/references/stacks/typescript.md +23 -0
  204. package/adapters/gemini/skills/forge-3-specs/forge-3-specs.md +2 -0
  205. package/adapters/gemini/skills/forge-3-specs/references/shared-conventions.md +12 -0
  206. package/adapters/gemini/skills/forge-3-specs/references/stacks/_generic.md +21 -0
  207. package/adapters/gemini/skills/forge-3-specs/references/stacks/go.md +19 -0
  208. package/adapters/gemini/skills/forge-3-specs/references/stacks/python.md +21 -0
  209. package/adapters/gemini/skills/forge-3-specs/references/stacks/rust.md +19 -0
  210. package/adapters/gemini/skills/forge-3-specs/references/stacks/typescript.md +23 -0
  211. package/adapters/gemini/skills/forge-4-backlog/forge-4-backlog.md +5 -0
  212. package/adapters/gemini/skills/forge-4-backlog/references/shared-conventions.md +12 -0
  213. package/adapters/gemini/skills/forge-5-loop/forge-5-loop.md +7 -9
  214. package/adapters/gemini/skills/forge-5-loop/references/runner-contract.md +41 -0
  215. package/adapters/gemini/skills/forge-5-loop/references/shared-conventions.md +12 -0
  216. package/adapters/gemini/skills/forge-6-docs/references/shared-conventions.md +12 -0
  217. package/adapters/gemini/skills/forge-fix/references/shared-conventions.md +12 -0
  218. package/adapters/gemini/skills/forge-guide/references/shared-conventions.md +12 -0
  219. package/adapters/gemini/skills/forge-guide/references/stacks/_generic.md +21 -0
  220. package/adapters/gemini/skills/forge-guide/references/stacks/go.md +19 -0
  221. package/adapters/gemini/skills/forge-guide/references/stacks/python.md +21 -0
  222. package/adapters/gemini/skills/forge-guide/references/stacks/rust.md +19 -0
  223. package/adapters/gemini/skills/forge-guide/references/stacks/typescript.md +23 -0
  224. package/adapters/gemini/skills/forge-verify/forge-verify.md +3 -3
  225. package/adapters/gemini/skills/forge-verify/references/shared-conventions.md +12 -0
  226. package/adapters/gemini/skills/forge-verify/references/verification-checklists.md +82 -3
  227. package/package.json +1 -1
@@ -21,14 +21,56 @@ is_root() { # $1 = candidate dir
21
21
  [ -f "$1/.feature-forge-bundle.json" ] || [ -f "$1/.claude-plugin/plugin.json" ]
22
22
  }
23
23
 
24
+ # Completeness gate (#152). A dir can carry the sentinel yet be a partial/stale install —
25
+ # a skill-only extraction, or an install predating the shared-reference fan-out — where the
26
+ # scripts/references the skills load at every stage are absent. Such a root looks usable but
27
+ # runs DEGRADED silently (hand-improvised state schema, skipped Mint Guard + scripted exit).
28
+ # These are the assets a complete install MUST carry beyond the sentinel; a resolved root
29
+ # missing any of them is reported as degraded rather than handed back as if it were whole.
30
+ CORE_ASSETS=(
31
+ "scripts/forge-session.py"
32
+ "references/pipeline-state-schema.json"
33
+ "references/stage-exit-protocol.md"
34
+ )
35
+
36
+ # Echo the first missing core asset (relative path) for $1, or nothing when complete.
37
+ first_missing_asset() { # $1 = candidate dir
38
+ local a
39
+ for a in "${CORE_ASSETS[@]}"; do
40
+ [ -f "$1/$a" ] || { printf '%s' "$a"; return 0; }
41
+ done
42
+ }
43
+
44
+ # A sentinel-bearing candidate that lacks a core asset is remembered here (first one wins)
45
+ # so a COMPLETE root found later in the probe order still takes precedence; only if the probe
46
+ # ends with no complete root do we report this degraded install (step 4).
47
+ partial_root=""
48
+ partial_missing=""
49
+
50
+ # Given a candidate that already passed is_root: print + exit 0 if it is complete; otherwise
51
+ # record it as the degraded fallback and return so probing continues. Always returns 0 on the
52
+ # fall-through path so `set -e` never aborts the resolver on a partial candidate.
53
+ accept_root() { # $1 = candidate dir (already is_root)
54
+ local miss
55
+ miss="$(first_missing_asset "$1")"
56
+ if [ -z "$miss" ]; then
57
+ printf '%s\n' "$1"
58
+ exit 0
59
+ fi
60
+ if [ -z "$partial_root" ]; then
61
+ partial_root="$1"
62
+ partial_missing="$miss"
63
+ fi
64
+ return 0
65
+ }
66
+
24
67
  # ── Step 1: self-location — parent of this script's dir is the install root. ─────────────
25
68
  # This is the PRIMARY path: a bundle ships its own scripts/forge-root.sh, so the parent of
26
69
  # this script's dir is that bundle's root under any agent's layout.
27
70
  self_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
28
71
  root="$(cd -- "$self_dir/.." && pwd -P)"
29
72
  if is_root "$root"; then
30
- printf '%s\n' "$root"
31
- exit 0
73
+ accept_root "$root"
32
74
  fi
33
75
 
34
76
  # ── Step 2a: Claude marketplace-cache installs — ~/.claude/plugins/cache/<mp>/<plugin>/<ver>/.
@@ -42,8 +84,7 @@ fi
42
84
  while IFS= read -r manifest; do
43
85
  candidate="${manifest%/.claude-plugin/plugin.json}"
44
86
  if is_root "$candidate"; then
45
- printf '%s\n' "$candidate"
46
- exit 0
87
+ accept_root "$candidate"
47
88
  fi
48
89
  done < <(ls -t "$HOME"/.claude/plugins/cache/*/feature-forge/*/.claude-plugin/plugin.json 2>/dev/null || true)
49
90
 
@@ -69,8 +110,7 @@ for candidate in \
69
110
  "$PWD/.gemini/extensions/feature-forge" \
70
111
  ; do
71
112
  if is_root "$candidate"; then
72
- printf '%s\n' "$candidate"
73
- exit 0
113
+ accept_root "$candidate"
74
114
  fi
75
115
  done
76
116
 
@@ -78,14 +118,18 @@ done
78
118
  # The neutral override works for every agent; CLAUDE_PLUGIN_ROOT is kept only for backwards
79
119
  # compatibility with existing Claude installs (C-4).
80
120
  if [ -n "${FEATURE_FORGE_ROOT:-}" ] && is_root "$FEATURE_FORGE_ROOT"; then
81
- printf '%s\n' "$FEATURE_FORGE_ROOT"
82
- exit 0
121
+ accept_root "$FEATURE_FORGE_ROOT"
83
122
  fi
84
123
  if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ] && is_root "$CLAUDE_PLUGIN_ROOT"; then
85
- printf '%s\n' "$CLAUDE_PLUGIN_ROOT"
86
- exit 0
124
+ accept_root "$CLAUDE_PLUGIN_ROOT"
87
125
  fi
88
126
 
89
- # ── Step 4: failure — actionable message to stderr, exit 1 (REQ-RES-04). ─────────────────
127
+ # ── Step 4: failure. A sentinel-bearing but asset-incomplete root found along the way is a
128
+ # DEGRADED install (#152) — report it distinctly and actionably rather than the generic
129
+ # cannot-locate message, so the operator knows to reinstall/update rather than reconfigure. ─
130
+ if [ -n "$partial_root" ]; then
131
+ echo "feature-forge: install incomplete/degraded at $partial_root (missing $partial_missing) — reinstall with 'npx @garygentry/feature-forge' (or 'feature-forge update' for a stale install)." >&2
132
+ exit 1
133
+ fi
90
134
  echo "feature-forge: cannot locate install root. Set FEATURE_FORGE_ROOT to the bundle dir, or run from an installed skill dir." >&2
91
135
  exit 1
@@ -117,8 +117,20 @@ VERIFY_TOKEN_BY_STAGE: Final[dict[str, str]] = {
117
117
 
118
118
  #: A production stage status that counts as "done" for next-stage selection.
119
119
  _DONE_STATUS: Final = "complete"
120
- #: Verify statuses that count as "resolved" (no outstanding verify needed).
120
+ #: The authoritative forge-verify status vocabulary. SOURCE OF TRUTH:
121
+ #: references/pipeline-state-schema.json (definitions.verifyEntry.properties.status.enum).
122
+ #: A status outside this set is unrecognized and must not be silently interpreted (#148).
123
+ #: NOTE: epic-manifest.py keeps a byte-identical copy — flat, self-contained scripts have
124
+ #: no shared import module (each is copied verbatim into per-agent adapter bundles).
125
+ KNOWN_VERIFY_STATUSES: Final = frozenset(
126
+ {"pending", "passed", "findings-reported", "findings-applied", "skipped"}
127
+ )
128
+ #: Verify statuses that count as "resolved" (no outstanding verify needed). A STRICT
129
+ #: subset of KNOWN_VERIFY_STATUSES — not collapsible into it (different meaning).
121
130
  _VERIFY_RESOLVED: Final = frozenset({"passed", "findings-applied", "skipped"})
131
+ #: Per-process dedupe for the unknown-verify-status diagnostic (#148) so a single
132
+ #: bogus status is flagged once, not once per verify_state() call in a command.
133
+ _UNKNOWN_VERIFY_WARNED: set[str] = set()
122
134
 
123
135
  #: Default context window when the model can't be inferred and config is silent.
124
136
  _DEFAULT_WINDOW: Final = 200_000
@@ -257,6 +269,25 @@ def _verify_entry(state: dict, verify_key: str) -> dict:
257
269
  return entry if isinstance(entry, dict) else {}
258
270
 
259
271
 
272
+ def _warn_unknown_verify_status(stage_name: str, status: object) -> None:
273
+ """Emit a one-time stderr diagnostic for an out-of-vocabulary verify status (#148).
274
+
275
+ The freshness classifier maps an unrecognized status to "never verified" — correct,
276
+ but silent, so a typo poisons the downstream gate (e.g. forge-5-loop's dependency
277
+ check) with no clue. Flagging it here makes the bad value visible where it is read.
278
+ """
279
+ key = f"{stage_name}={status!r}"
280
+ if key in _UNKNOWN_VERIFY_WARNED:
281
+ return
282
+ _UNKNOWN_VERIFY_WARNED.add(key)
283
+ known = ", ".join(sorted(KNOWN_VERIFY_STATUSES))
284
+ print(
285
+ f"feature-forge: unknown {stage_name} status {status!r} "
286
+ f"(treated as unverified; expected one of {known})",
287
+ file=sys.stderr,
288
+ )
289
+
290
+
260
291
  def verify_state(state: dict) -> tuple[str | None, str]:
261
292
  """Classify verify freshness for the most-recently-completed stage.
262
293
 
@@ -299,6 +330,12 @@ def verify_state(state: dict) -> tuple[str | None, str]:
299
330
  if status not in _VERIFY_RESOLVED:
300
331
  if status == "findings-reported":
301
332
  return stage, "failing"
333
+ # An unrecognized status (outside KNOWN_VERIFY_STATUSES) is treated as
334
+ # "never verified" — defensible, but flag it once so a typo (e.g. the
335
+ # eye-slip 'findings-resolved') doesn't silently poison the gate that
336
+ # reads this label (#148). ``pending``/``None`` are known/absent → quiet.
337
+ if status is not None and status not in KNOWN_VERIFY_STATUSES:
338
+ _warn_unknown_verify_status(f"forge-verify-{token}", status)
302
339
  return stage, "never"
303
340
  verified_version = entry.get("verifiedStageVersion")
304
341
  stage_version = _stage_version(state, stage)
@@ -274,6 +274,18 @@ This write is **left uncommitted**: it is staged and committed as part of this s
274
274
 
275
275
  **Incremental artifact tracking:** When a stage writes multiple files (e.g. forge-3-specs writing a suite of spec documents), update the `stages.{stage}.artifacts` array in `.pipeline-state.json` after writing each file — not just at stage completion. This is what makes the Interrupted inventory above precise about which files were successfully written.
276
276
 
277
+ ## Stage-Completion Re-check
278
+
279
+ Invoke this block **at the head of any post-entry step that writes a stage artifact or runs the Scripted Stage Exit** — the Stage-Entry Guard runs only once, at the top of the skill. A **resumed or pasted mid-stage instruction** (e.g. "continue {stage}: write TRACEABILITY.md, run the stage exit") enters *below* the entry guard, so nothing re-checks completion before it overwrites a committed artifact and re-fires a finished exit — data-destructive if followed literally. This re-check is the idempotency backstop for that path. `{stage}` is the invoking skill's id.
280
+
281
+ **Re-read** `stages.{stage}` in `{resolvedFeatureDir}/.pipeline-state.json`, then classify by **provenance** — a legitimate completion runs in the same session that applied this stage's Entry Stamp; a replayed continuation finds a finished stage it did not produce:
282
+
283
+ 1. **Proceed** when `stages.{stage}.status` is `"in-progress"` (this session's Entry Stamp — you are finishing the run you started) or absent/`pending`. Run the write / exit normally.
284
+
285
+ 2. **Detect-and-refuse** when ALL of these hold: `stages.{stage}.status ∈ {"complete", "stale"}` **AND** the stage's artifacts (incl. `TRACEABILITY.md` for forge-3-specs) exist on disk **AND** a `commitHash` is recorded for the stage **AND** you did **not** author this stage earlier in the current session. This is a stale/replayed continuation of an already-finished, committed stage. Do **not** overwrite the artifact or re-run the exit. Route instead to the **Stage-Entry Guard**'s *Re-authoring* path: surface the same `AskUserQuestion` warning ("A completed {stage} artifact already exists for '{feature}' (v{n}{, marked stale}). Continuing will create a new version. Proceed?"). Only on explicit confirmation re-enter from the Entry Stamp (the version bumps at exit); otherwise **stop** and report that the stage is already complete — cite the recorded `commitHash` and offer `/feature-forge:forge {feature}` to see true state.
286
+
287
+ When you cannot confirm you authored the current run, treat it as a replay and refuse: a false refuse costs one confirmation click; a false proceed overwrites a committed artifact and re-churns a stage version. `--force` follows Force Mode (skip the gate, treat as a deliberate re-author).
288
+
277
289
  ## Force Mode
278
290
 
279
291
  If the user passes `--force` as an argument, skip prerequisite validation with a warning:
@@ -274,6 +274,18 @@ This write is **left uncommitted**: it is staged and committed as part of this s
274
274
 
275
275
  **Incremental artifact tracking:** When a stage writes multiple files (e.g. forge-3-specs writing a suite of spec documents), update the `stages.{stage}.artifacts` array in `.pipeline-state.json` after writing each file — not just at stage completion. This is what makes the Interrupted inventory above precise about which files were successfully written.
276
276
 
277
+ ## Stage-Completion Re-check
278
+
279
+ Invoke this block **at the head of any post-entry step that writes a stage artifact or runs the Scripted Stage Exit** — the Stage-Entry Guard runs only once, at the top of the skill. A **resumed or pasted mid-stage instruction** (e.g. "continue {stage}: write TRACEABILITY.md, run the stage exit") enters *below* the entry guard, so nothing re-checks completion before it overwrites a committed artifact and re-fires a finished exit — data-destructive if followed literally. This re-check is the idempotency backstop for that path. `{stage}` is the invoking skill's id.
280
+
281
+ **Re-read** `stages.{stage}` in `{resolvedFeatureDir}/.pipeline-state.json`, then classify by **provenance** — a legitimate completion runs in the same session that applied this stage's Entry Stamp; a replayed continuation finds a finished stage it did not produce:
282
+
283
+ 1. **Proceed** when `stages.{stage}.status` is `"in-progress"` (this session's Entry Stamp — you are finishing the run you started) or absent/`pending`. Run the write / exit normally.
284
+
285
+ 2. **Detect-and-refuse** when ALL of these hold: `stages.{stage}.status ∈ {"complete", "stale"}` **AND** the stage's artifacts (incl. `TRACEABILITY.md` for forge-3-specs) exist on disk **AND** a `commitHash` is recorded for the stage **AND** you did **not** author this stage earlier in the current session. This is a stale/replayed continuation of an already-finished, committed stage. Do **not** overwrite the artifact or re-run the exit. Route instead to the **Stage-Entry Guard**'s *Re-authoring* path: surface the same `AskUserQuestion` warning ("A completed {stage} artifact already exists for '{feature}' (v{n}{, marked stale}). Continuing will create a new version. Proceed?"). Only on explicit confirmation re-enter from the Entry Stamp (the version bumps at exit); otherwise **stop** and report that the stage is already complete — cite the recorded `commitHash` and offer `/feature-forge:forge {feature}` to see true state.
286
+
287
+ When you cannot confirm you authored the current run, treat it as a replay and refuse: a false refuse costs one confirmation click; a false proceed overwrites a committed artifact and re-churns a stage version. `--force` follows Force Mode (skip the gate, treat as a deliberate re-author).
288
+
277
289
  ## Force Mode
278
290
 
279
291
  If the user passes `--force` as an argument, skip prerequisite validation with a warning:
@@ -121,6 +121,8 @@ Iterate until the user confirms the PRD is complete.
121
121
 
122
122
  ## Step 6: Update Pipeline State and Commit
123
123
 
124
+ Before writing state or running the stage exit, invoke the **Stage-Completion Re-check** block in `references/shared-conventions.md` with `{stage}` = `forge-1-prd` — a resumed mid-stage continuation must not overwrite a committed `PRD.md` or re-fire a finished exit.
125
+
124
126
  Write pipeline state conforming to `references/pipeline-state-schema.json`.
125
127
 
126
128
  1. Create or update `{resolvedFeatureDir}/.pipeline-state.json`:
@@ -274,6 +274,18 @@ This write is **left uncommitted**: it is staged and committed as part of this s
274
274
 
275
275
  **Incremental artifact tracking:** When a stage writes multiple files (e.g. forge-3-specs writing a suite of spec documents), update the `stages.{stage}.artifacts` array in `.pipeline-state.json` after writing each file — not just at stage completion. This is what makes the Interrupted inventory above precise about which files were successfully written.
276
276
 
277
+ ## Stage-Completion Re-check
278
+
279
+ Invoke this block **at the head of any post-entry step that writes a stage artifact or runs the Scripted Stage Exit** — the Stage-Entry Guard runs only once, at the top of the skill. A **resumed or pasted mid-stage instruction** (e.g. "continue {stage}: write TRACEABILITY.md, run the stage exit") enters *below* the entry guard, so nothing re-checks completion before it overwrites a committed artifact and re-fires a finished exit — data-destructive if followed literally. This re-check is the idempotency backstop for that path. `{stage}` is the invoking skill's id.
280
+
281
+ **Re-read** `stages.{stage}` in `{resolvedFeatureDir}/.pipeline-state.json`, then classify by **provenance** — a legitimate completion runs in the same session that applied this stage's Entry Stamp; a replayed continuation finds a finished stage it did not produce:
282
+
283
+ 1. **Proceed** when `stages.{stage}.status` is `"in-progress"` (this session's Entry Stamp — you are finishing the run you started) or absent/`pending`. Run the write / exit normally.
284
+
285
+ 2. **Detect-and-refuse** when ALL of these hold: `stages.{stage}.status ∈ {"complete", "stale"}` **AND** the stage's artifacts (incl. `TRACEABILITY.md` for forge-3-specs) exist on disk **AND** a `commitHash` is recorded for the stage **AND** you did **not** author this stage earlier in the current session. This is a stale/replayed continuation of an already-finished, committed stage. Do **not** overwrite the artifact or re-run the exit. Route instead to the **Stage-Entry Guard**'s *Re-authoring* path: surface the same `AskUserQuestion` warning ("A completed {stage} artifact already exists for '{feature}' (v{n}{, marked stale}). Continuing will create a new version. Proceed?"). Only on explicit confirmation re-enter from the Entry Stamp (the version bumps at exit); otherwise **stop** and report that the stage is already complete — cite the recorded `commitHash` and offer `/feature-forge:forge {feature}` to see true state.
286
+
287
+ When you cannot confirm you authored the current run, treat it as a replay and refuse: a false refuse costs one confirmation click; a false proceed overwrites a committed artifact and re-churns a stage version. `--force` follows Force Mode (skip the gate, treat as a deliberate re-author).
288
+
277
289
  ## Force Mode
278
290
 
279
291
  If the user passes `--force` as an argument, skip prerequisite validation with a warning:
@@ -183,6 +183,8 @@ Use the host's question mechanism to collect this feedback.
183
183
 
184
184
  ## Step 7: Update Pipeline State and Commit
185
185
 
186
+ Before writing state or running the stage exit, invoke the **Stage-Completion Re-check** block in `references/shared-conventions.md` with `{stage}` = `forge-2-tech` — a resumed mid-stage continuation must not overwrite a committed `tech-spec.md` or re-fire a finished exit.
187
+
186
188
  Write pipeline state conforming to `references/pipeline-state-schema.json`.
187
189
 
188
190
  1. Update `{resolvedFeatureDir}/.pipeline-state.json`:
@@ -274,6 +274,18 @@ This write is **left uncommitted**: it is staged and committed as part of this s
274
274
 
275
275
  **Incremental artifact tracking:** When a stage writes multiple files (e.g. forge-3-specs writing a suite of spec documents), update the `stages.{stage}.artifacts` array in `.pipeline-state.json` after writing each file — not just at stage completion. This is what makes the Interrupted inventory above precise about which files were successfully written.
276
276
 
277
+ ## Stage-Completion Re-check
278
+
279
+ Invoke this block **at the head of any post-entry step that writes a stage artifact or runs the Scripted Stage Exit** — the Stage-Entry Guard runs only once, at the top of the skill. A **resumed or pasted mid-stage instruction** (e.g. "continue {stage}: write TRACEABILITY.md, run the stage exit") enters *below* the entry guard, so nothing re-checks completion before it overwrites a committed artifact and re-fires a finished exit — data-destructive if followed literally. This re-check is the idempotency backstop for that path. `{stage}` is the invoking skill's id.
280
+
281
+ **Re-read** `stages.{stage}` in `{resolvedFeatureDir}/.pipeline-state.json`, then classify by **provenance** — a legitimate completion runs in the same session that applied this stage's Entry Stamp; a replayed continuation finds a finished stage it did not produce:
282
+
283
+ 1. **Proceed** when `stages.{stage}.status` is `"in-progress"` (this session's Entry Stamp — you are finishing the run you started) or absent/`pending`. Run the write / exit normally.
284
+
285
+ 2. **Detect-and-refuse** when ALL of these hold: `stages.{stage}.status ∈ {"complete", "stale"}` **AND** the stage's artifacts (incl. `TRACEABILITY.md` for forge-3-specs) exist on disk **AND** a `commitHash` is recorded for the stage **AND** you did **not** author this stage earlier in the current session. This is a stale/replayed continuation of an already-finished, committed stage. Do **not** overwrite the artifact or re-run the exit. Route instead to the **Stage-Entry Guard**'s *Re-authoring* path: surface the same `AskUserQuestion` warning ("A completed {stage} artifact already exists for '{feature}' (v{n}{, marked stale}). Continuing will create a new version. Proceed?"). Only on explicit confirmation re-enter from the Entry Stamp (the version bumps at exit); otherwise **stop** and report that the stage is already complete — cite the recorded `commitHash` and offer `/feature-forge:forge {feature}` to see true state.
286
+
287
+ When you cannot confirm you authored the current run, treat it as a replay and refuse: a false refuse costs one confirmation click; a false proceed overwrites a committed artifact and re-churns a stage version. `--force` follows Force Mode (skip the gate, treat as a deliberate re-author).
288
+
277
289
  ## Force Mode
278
290
 
279
291
  If the user passes `--force` as an argument, skip prerequisite validation with a warning:
@@ -79,6 +79,27 @@ Replace stack-specific checks with these generic equivalents:
79
79
  | "JSDoc on every field" | "Documentation comments on every field" |
80
80
  | "tsconfig.json extends root" | "Build configuration follows project conventions" |
81
81
 
82
+ ### Runtime Entrypoints & Bootstrap-Wiring Sites
83
+
84
+ Used by `CHECK-I22` (a runtime-required bootstrap needs a **non-test** caller on one of these) and
85
+ `CHECK-I23` (a heavy init wired into a **universal** bootstrap entry should move to a lazier site).
86
+ Since no dedicated profile applies, identify these by role rather than exact filename:
87
+
88
+ - **Runtime entrypoints (a legitimate non-test call site):** the process/main entry the build's
89
+ run command actually launches (from the CI/`Makefile`/manifest "start" target), an HTTP/RPC handler
90
+ or route, a scheduled/worker task, or a CLI subcommand — **not** a file in the project's test
91
+ directory or matching its test-file convention.
92
+ - **Universal bootstrap entries (run on every startup — the `CHECK-I23` risk site):** any
93
+ module/hook the framework runs **once per process before serving** — a startup/lifecycle hook, an
94
+ import-time initializer, a global preload, or a shared test-setup module that bootstraps production
95
+ graphs. Side effects here run on every process start (and, under a dev/watch runtime, on every
96
+ reload).
97
+ - **Heavy server-only import markers (what makes an init "heavy" for `CHECK-I23`):** database/ORM
98
+ clients and connection pools, message/queue clients, telemetry/observability SDKs, or a broad
99
+ whole-service-layer import. An init pulling any of these into a universal bootstrap entry is the
100
+ CHECK-I23 pattern — recommend lazy initialization at the first handler/worker that needs the graph,
101
+ not eagerly at process start.
102
+
82
103
  ## Acceptance Criteria Patterns
83
104
 
84
105
  Use these placeholder patterns in backlog items. Replace `{typeCheckCommand}`, `{testCommand}`, and `{module}` with values from `forge.config.json` and the project structure:
@@ -84,6 +84,25 @@ When examining a Go project, check for:
84
84
  - **Formatting**: `gofmt -l .` or `goimports -l .` (should produce no output)
85
85
  - **Module tidiness**: `go mod tidy` (ensures `go.mod` and `go.sum` are consistent)
86
86
 
87
+ ### Runtime Entrypoints & Bootstrap-Wiring Sites
88
+
89
+ Used by `CHECK-I22` (a runtime-required bootstrap needs a **non-test** caller on one of these) and
90
+ `CHECK-I23` (a heavy init wired into a **universal** bootstrap entry should move to a lazier site).
91
+
92
+ - **Runtime entrypoints (a legitimate non-test call site):** `func main()` in a `package main` under
93
+ `cmd/…` or the module root, an HTTP handler registered on a `*http.ServeMux` / router, a gRPC service
94
+ registration, or a worker/consumer goroutine started from `main` — not a `*_test.go` file.
95
+ - **Universal bootstrap entries (run on every startup — the `CHECK-I23` risk site):** a package-level
96
+ `func init()` that constructs heavy clients, package-level `var` initializers that dial/connect at
97
+ import time, or a single `bootstrap`/`wire` module `main` calls before serving. `init()` and
98
+ package-var side effects run on **every** binary start, before `main` gets control.
99
+ - **Heavy server-only import markers (what makes an init "heavy" for `CHECK-I23`):** DB drivers/pools
100
+ (`database/sql` + a driver, `pgx`, `gorm`, `mongo-driver`), message/queue clients (`sarama`/Kafka,
101
+ `amqp`, `go-redis`), telemetry SDKs (`go.opentelemetry.io/*`, `sentry-go`), or a broad internal
102
+ service package. An `init()` or package-var that dials any of these is the CHECK-I23 pattern —
103
+ recommend a `sync.Once` lazy constructor invoked from the first handler that needs it, not an
104
+ eager package-level connect.
105
+
87
106
  ## Testing
88
107
 
89
108
  - **Framework**: `testing` stdlib package
@@ -111,6 +111,27 @@ async def refresh_session_token(
111
111
  - **Import validation**: `mypy` with `--strict` or `--disallow-untyped-defs` catches missing type annotations
112
112
  - **Module export validation**: `__all__` lists in `__init__.py` match spec's public API
113
113
 
114
+ ### Runtime Entrypoints & Bootstrap-Wiring Sites
115
+
116
+ Used by `CHECK-I22` (a runtime-required bootstrap needs a **non-test** caller on one of these) and
117
+ `CHECK-I23` (a heavy init wired into a **universal** bootstrap entry should move to a lazier site).
118
+
119
+ - **Runtime entrypoints (a legitimate non-test call site):** a `if __name__ == "__main__":` block, a
120
+ `[project.scripts]` console-script target in `pyproject.toml`, an ASGI/WSGI app object
121
+ (`app = FastAPI()` / Django `wsgi.py` / `asgi.py`), a route/view/handler, a Celery/RQ task module, or
122
+ a management command — not a `test_*.py` / `conftest.py`.
123
+ - **Universal bootstrap entries (run on every startup — the `CHECK-I23` risk site):** a package
124
+ `__init__.py` that eagerly instantiates heavy clients at import time, a framework startup hook
125
+ (FastAPI `lifespan` / `@app.on_event("startup")`, Django `AppConfig.ready()`, a Gunicorn/uvicorn
126
+ `--preload` module), or a `conftest.py` that bootstraps production graphs. Import-time side effects
127
+ here run on every process start.
128
+ - **Heavy server-only import markers (what makes an init "heavy" for `CHECK-I23`):** DB/ORM engines
129
+ (`sqlalchemy.create_engine`, `psycopg`, Django ORM, `pymongo`, `redis`), task queues (`celery`,
130
+ `rq`, `kafka`), telemetry SDKs (`opentelemetry`, `sentry_sdk`), or a whole-service-layer package
131
+ import. An init pulling any of these at a universal entry is the CHECK-I23 pattern — recommend lazy
132
+ init (module-level `@lru_cache` factory, `Depends()` provider, or first-use lazy import) at the
133
+ handler that needs it.
134
+
114
135
  ## Testing
115
136
 
116
137
  - **Framework**: pytest (with `conftest.py` for fixtures)
@@ -90,6 +90,25 @@ When examining a Rust project, check for:
90
90
  - **Formatting**: `cargo fmt --check` (ensures code matches `rustfmt` style)
91
91
  - **Unsafe audit**: `cargo geiger` (if security-sensitive — counts unsafe blocks)
92
92
 
93
+ ### Runtime Entrypoints & Bootstrap-Wiring Sites
94
+
95
+ Used by `CHECK-I22` (a runtime-required bootstrap needs a **non-test** caller on one of these) and
96
+ `CHECK-I23` (a heavy init wired into a **universal** bootstrap entry should move to a lazier site).
97
+
98
+ - **Runtime entrypoints (a legitimate non-test call site):** `fn main()` in `main.rs` or a `[[bin]]`
99
+ target, an async runtime entry (`#[tokio::main]`), an HTTP handler registered on an axum/actix/warp
100
+ router, or a worker task spawned from `main` — not a `#[cfg(test)]` module or a `tests/` integration
101
+ file.
102
+ - **Universal bootstrap entries (run on every startup — the `CHECK-I23` risk site):** a `lazy_static!`
103
+ / `once_cell::sync::Lazy` static that eagerly constructs heavy clients, a single `bootstrap`/`setup`
104
+ fn `main` calls before serving, or a `#[ctor]`-style pre-`main` initializer. A `Lazy` static is only
105
+ a risk when it is *forced* at startup rather than on first use.
106
+ - **Heavy server-only import markers (what makes an init "heavy" for `CHECK-I23`):** DB pools
107
+ (`sqlx::Pool`, `diesel`, `deadpool`, `mongodb`), message/queue clients (`rdkafka`, `lapin`,
108
+ `redis`), telemetry SDKs (`opentelemetry`, `sentry`), or a broad internal service crate. Constructing
109
+ any of these eagerly at a universal entry is the CHECK-I23 pattern — recommend a `OnceCell`/`Lazy`
110
+ forced on **first use** by the handler that needs it, not at process start.
111
+
93
112
  ## Testing
94
113
 
95
114
  - **Framework**: Built-in test harness (`#[test]`, `#[cfg(test)]`)
@@ -64,6 +64,29 @@ When examining a TypeScript project, check for:
64
64
  - **Cross-package type checks**: `bun run typecheck` (or equivalent) passes for both the feature package AND packages that depend on it
65
65
  - **Import path validation**: All import paths resolve correctly per the `exports` map in `package.json`
66
66
 
67
+ ### Runtime Entrypoints & Bootstrap-Wiring Sites
68
+
69
+ Used by `CHECK-I22` (a runtime-required bootstrap needs a **non-test** caller on one of these) and
70
+ `CHECK-I23` (a heavy init wired into a **universal** bootstrap entry should move to a lazier site).
71
+
72
+ - **Runtime entrypoints (a legitimate non-test call site):** a `package.json` `bin` / CLI `main`; a
73
+ server entry that actually starts listening (`src/server.ts`, `src/index.ts` invoking `listen`); a
74
+ worker/consumer file; and for Next.js — `middleware.ts`, `app/**/route.ts` route handlers,
75
+ `app/**/{page,layout,template}.tsx`, server actions, and `instrumentation.ts`. Express/Hono/Fastify:
76
+ the file that constructs the app and calls `.listen()`.
77
+ - **Universal bootstrap entries (run on every startup — the `CHECK-I23` risk site):** Next.js
78
+ `instrumentation.ts` / `instrumentation.js` (its `register()` runs once per server process before
79
+ any request), an app-server preload/`register`/`--import` hook, a root `app/layout.tsx` that
80
+ eagerly imports server singletons, and global test setup (`vitest.setup.ts`, `jest.setup.ts`) if it
81
+ bootstraps production graphs. Wiring a heavy init here loads it on **every** cold start (and, under
82
+ the dev server, on every module re-evaluation).
83
+ - **Heavy server-only import markers (what makes an init "heavy" for `CHECK-I23`):** DB/ORM clients
84
+ (`drizzle-orm`, `@prisma/client`, `pg`, `mongoose`, `kysely`), queue/worker libs (`bullmq`, `bull`,
85
+ `kafkajs`, `ioredis`), telemetry/observability SDKs (`@opentelemetry/*`, `@sentry/node`), and a
86
+ whole-service-layer barrel (`import * as services from "@repo/services"`). An init that pulls any of
87
+ these into a universal bootstrap entry is the CHECK-I23 pattern — recommend lazy init at the first
88
+ route/handler/worker that needs it.
89
+
67
90
  ## Testing
68
91
 
69
92
  - **Framework**: Vitest (most common in modern TS), Jest, or testing-library
@@ -116,6 +116,8 @@ do not reload everything. This keeps quality up under context pressure.
116
116
 
117
117
  ## Step 5: Cross-Reference Validation
118
118
 
119
+ Before writing `TRACEABILITY.md` or running the stage exit, invoke the **Stage-Completion Re-check** block in `references/shared-conventions.md` with `{stage}` = `forge-3-specs` — a resumed mid-stage continuation must not overwrite a committed `TRACEABILITY.md` or re-fire a finished exit.
120
+
119
121
  After writing all documents, verify:
120
122
 
121
123
  1. Every PRD requirement has coverage in at least one spec document
@@ -274,6 +274,18 @@ This write is **left uncommitted**: it is staged and committed as part of this s
274
274
 
275
275
  **Incremental artifact tracking:** When a stage writes multiple files (e.g. forge-3-specs writing a suite of spec documents), update the `stages.{stage}.artifacts` array in `.pipeline-state.json` after writing each file — not just at stage completion. This is what makes the Interrupted inventory above precise about which files were successfully written.
276
276
 
277
+ ## Stage-Completion Re-check
278
+
279
+ Invoke this block **at the head of any post-entry step that writes a stage artifact or runs the Scripted Stage Exit** — the Stage-Entry Guard runs only once, at the top of the skill. A **resumed or pasted mid-stage instruction** (e.g. "continue {stage}: write TRACEABILITY.md, run the stage exit") enters *below* the entry guard, so nothing re-checks completion before it overwrites a committed artifact and re-fires a finished exit — data-destructive if followed literally. This re-check is the idempotency backstop for that path. `{stage}` is the invoking skill's id.
280
+
281
+ **Re-read** `stages.{stage}` in `{resolvedFeatureDir}/.pipeline-state.json`, then classify by **provenance** — a legitimate completion runs in the same session that applied this stage's Entry Stamp; a replayed continuation finds a finished stage it did not produce:
282
+
283
+ 1. **Proceed** when `stages.{stage}.status` is `"in-progress"` (this session's Entry Stamp — you are finishing the run you started) or absent/`pending`. Run the write / exit normally.
284
+
285
+ 2. **Detect-and-refuse** when ALL of these hold: `stages.{stage}.status ∈ {"complete", "stale"}` **AND** the stage's artifacts (incl. `TRACEABILITY.md` for forge-3-specs) exist on disk **AND** a `commitHash` is recorded for the stage **AND** you did **not** author this stage earlier in the current session. This is a stale/replayed continuation of an already-finished, committed stage. Do **not** overwrite the artifact or re-run the exit. Route instead to the **Stage-Entry Guard**'s *Re-authoring* path: surface the same `AskUserQuestion` warning ("A completed {stage} artifact already exists for '{feature}' (v{n}{, marked stale}). Continuing will create a new version. Proceed?"). Only on explicit confirmation re-enter from the Entry Stamp (the version bumps at exit); otherwise **stop** and report that the stage is already complete — cite the recorded `commitHash` and offer `/feature-forge:forge {feature}` to see true state.
286
+
287
+ When you cannot confirm you authored the current run, treat it as a replay and refuse: a false refuse costs one confirmation click; a false proceed overwrites a committed artifact and re-churns a stage version. `--force` follows Force Mode (skip the gate, treat as a deliberate re-author).
288
+
277
289
  ## Force Mode
278
290
 
279
291
  If the user passes `--force` as an argument, skip prerequisite validation with a warning:
@@ -79,6 +79,27 @@ Replace stack-specific checks with these generic equivalents:
79
79
  | "JSDoc on every field" | "Documentation comments on every field" |
80
80
  | "tsconfig.json extends root" | "Build configuration follows project conventions" |
81
81
 
82
+ ### Runtime Entrypoints & Bootstrap-Wiring Sites
83
+
84
+ Used by `CHECK-I22` (a runtime-required bootstrap needs a **non-test** caller on one of these) and
85
+ `CHECK-I23` (a heavy init wired into a **universal** bootstrap entry should move to a lazier site).
86
+ Since no dedicated profile applies, identify these by role rather than exact filename:
87
+
88
+ - **Runtime entrypoints (a legitimate non-test call site):** the process/main entry the build's
89
+ run command actually launches (from the CI/`Makefile`/manifest "start" target), an HTTP/RPC handler
90
+ or route, a scheduled/worker task, or a CLI subcommand — **not** a file in the project's test
91
+ directory or matching its test-file convention.
92
+ - **Universal bootstrap entries (run on every startup — the `CHECK-I23` risk site):** any
93
+ module/hook the framework runs **once per process before serving** — a startup/lifecycle hook, an
94
+ import-time initializer, a global preload, or a shared test-setup module that bootstraps production
95
+ graphs. Side effects here run on every process start (and, under a dev/watch runtime, on every
96
+ reload).
97
+ - **Heavy server-only import markers (what makes an init "heavy" for `CHECK-I23`):** database/ORM
98
+ clients and connection pools, message/queue clients, telemetry/observability SDKs, or a broad
99
+ whole-service-layer import. An init pulling any of these into a universal bootstrap entry is the
100
+ CHECK-I23 pattern — recommend lazy initialization at the first handler/worker that needs the graph,
101
+ not eagerly at process start.
102
+
82
103
  ## Acceptance Criteria Patterns
83
104
 
84
105
  Use these placeholder patterns in backlog items. Replace `{typeCheckCommand}`, `{testCommand}`, and `{module}` with values from `forge.config.json` and the project structure:
@@ -84,6 +84,25 @@ When examining a Go project, check for:
84
84
  - **Formatting**: `gofmt -l .` or `goimports -l .` (should produce no output)
85
85
  - **Module tidiness**: `go mod tidy` (ensures `go.mod` and `go.sum` are consistent)
86
86
 
87
+ ### Runtime Entrypoints & Bootstrap-Wiring Sites
88
+
89
+ Used by `CHECK-I22` (a runtime-required bootstrap needs a **non-test** caller on one of these) and
90
+ `CHECK-I23` (a heavy init wired into a **universal** bootstrap entry should move to a lazier site).
91
+
92
+ - **Runtime entrypoints (a legitimate non-test call site):** `func main()` in a `package main` under
93
+ `cmd/…` or the module root, an HTTP handler registered on a `*http.ServeMux` / router, a gRPC service
94
+ registration, or a worker/consumer goroutine started from `main` — not a `*_test.go` file.
95
+ - **Universal bootstrap entries (run on every startup — the `CHECK-I23` risk site):** a package-level
96
+ `func init()` that constructs heavy clients, package-level `var` initializers that dial/connect at
97
+ import time, or a single `bootstrap`/`wire` module `main` calls before serving. `init()` and
98
+ package-var side effects run on **every** binary start, before `main` gets control.
99
+ - **Heavy server-only import markers (what makes an init "heavy" for `CHECK-I23`):** DB drivers/pools
100
+ (`database/sql` + a driver, `pgx`, `gorm`, `mongo-driver`), message/queue clients (`sarama`/Kafka,
101
+ `amqp`, `go-redis`), telemetry SDKs (`go.opentelemetry.io/*`, `sentry-go`), or a broad internal
102
+ service package. An `init()` or package-var that dials any of these is the CHECK-I23 pattern —
103
+ recommend a `sync.Once` lazy constructor invoked from the first handler that needs it, not an
104
+ eager package-level connect.
105
+
87
106
  ## Testing
88
107
 
89
108
  - **Framework**: `testing` stdlib package
@@ -111,6 +111,27 @@ async def refresh_session_token(
111
111
  - **Import validation**: `mypy` with `--strict` or `--disallow-untyped-defs` catches missing type annotations
112
112
  - **Module export validation**: `__all__` lists in `__init__.py` match spec's public API
113
113
 
114
+ ### Runtime Entrypoints & Bootstrap-Wiring Sites
115
+
116
+ Used by `CHECK-I22` (a runtime-required bootstrap needs a **non-test** caller on one of these) and
117
+ `CHECK-I23` (a heavy init wired into a **universal** bootstrap entry should move to a lazier site).
118
+
119
+ - **Runtime entrypoints (a legitimate non-test call site):** a `if __name__ == "__main__":` block, a
120
+ `[project.scripts]` console-script target in `pyproject.toml`, an ASGI/WSGI app object
121
+ (`app = FastAPI()` / Django `wsgi.py` / `asgi.py`), a route/view/handler, a Celery/RQ task module, or
122
+ a management command — not a `test_*.py` / `conftest.py`.
123
+ - **Universal bootstrap entries (run on every startup — the `CHECK-I23` risk site):** a package
124
+ `__init__.py` that eagerly instantiates heavy clients at import time, a framework startup hook
125
+ (FastAPI `lifespan` / `@app.on_event("startup")`, Django `AppConfig.ready()`, a Gunicorn/uvicorn
126
+ `--preload` module), or a `conftest.py` that bootstraps production graphs. Import-time side effects
127
+ here run on every process start.
128
+ - **Heavy server-only import markers (what makes an init "heavy" for `CHECK-I23`):** DB/ORM engines
129
+ (`sqlalchemy.create_engine`, `psycopg`, Django ORM, `pymongo`, `redis`), task queues (`celery`,
130
+ `rq`, `kafka`), telemetry SDKs (`opentelemetry`, `sentry_sdk`), or a whole-service-layer package
131
+ import. An init pulling any of these at a universal entry is the CHECK-I23 pattern — recommend lazy
132
+ init (module-level `@lru_cache` factory, `Depends()` provider, or first-use lazy import) at the
133
+ handler that needs it.
134
+
114
135
  ## Testing
115
136
 
116
137
  - **Framework**: pytest (with `conftest.py` for fixtures)
@@ -90,6 +90,25 @@ When examining a Rust project, check for:
90
90
  - **Formatting**: `cargo fmt --check` (ensures code matches `rustfmt` style)
91
91
  - **Unsafe audit**: `cargo geiger` (if security-sensitive — counts unsafe blocks)
92
92
 
93
+ ### Runtime Entrypoints & Bootstrap-Wiring Sites
94
+
95
+ Used by `CHECK-I22` (a runtime-required bootstrap needs a **non-test** caller on one of these) and
96
+ `CHECK-I23` (a heavy init wired into a **universal** bootstrap entry should move to a lazier site).
97
+
98
+ - **Runtime entrypoints (a legitimate non-test call site):** `fn main()` in `main.rs` or a `[[bin]]`
99
+ target, an async runtime entry (`#[tokio::main]`), an HTTP handler registered on an axum/actix/warp
100
+ router, or a worker task spawned from `main` — not a `#[cfg(test)]` module or a `tests/` integration
101
+ file.
102
+ - **Universal bootstrap entries (run on every startup — the `CHECK-I23` risk site):** a `lazy_static!`
103
+ / `once_cell::sync::Lazy` static that eagerly constructs heavy clients, a single `bootstrap`/`setup`
104
+ fn `main` calls before serving, or a `#[ctor]`-style pre-`main` initializer. A `Lazy` static is only
105
+ a risk when it is *forced* at startup rather than on first use.
106
+ - **Heavy server-only import markers (what makes an init "heavy" for `CHECK-I23`):** DB pools
107
+ (`sqlx::Pool`, `diesel`, `deadpool`, `mongodb`), message/queue clients (`rdkafka`, `lapin`,
108
+ `redis`), telemetry SDKs (`opentelemetry`, `sentry`), or a broad internal service crate. Constructing
109
+ any of these eagerly at a universal entry is the CHECK-I23 pattern — recommend a `OnceCell`/`Lazy`
110
+ forced on **first use** by the handler that needs it, not at process start.
111
+
93
112
  ## Testing
94
113
 
95
114
  - **Framework**: Built-in test harness (`#[test]`, `#[cfg(test)]`)
@@ -64,6 +64,29 @@ When examining a TypeScript project, check for:
64
64
  - **Cross-package type checks**: `bun run typecheck` (or equivalent) passes for both the feature package AND packages that depend on it
65
65
  - **Import path validation**: All import paths resolve correctly per the `exports` map in `package.json`
66
66
 
67
+ ### Runtime Entrypoints & Bootstrap-Wiring Sites
68
+
69
+ Used by `CHECK-I22` (a runtime-required bootstrap needs a **non-test** caller on one of these) and
70
+ `CHECK-I23` (a heavy init wired into a **universal** bootstrap entry should move to a lazier site).
71
+
72
+ - **Runtime entrypoints (a legitimate non-test call site):** a `package.json` `bin` / CLI `main`; a
73
+ server entry that actually starts listening (`src/server.ts`, `src/index.ts` invoking `listen`); a
74
+ worker/consumer file; and for Next.js — `middleware.ts`, `app/**/route.ts` route handlers,
75
+ `app/**/{page,layout,template}.tsx`, server actions, and `instrumentation.ts`. Express/Hono/Fastify:
76
+ the file that constructs the app and calls `.listen()`.
77
+ - **Universal bootstrap entries (run on every startup — the `CHECK-I23` risk site):** Next.js
78
+ `instrumentation.ts` / `instrumentation.js` (its `register()` runs once per server process before
79
+ any request), an app-server preload/`register`/`--import` hook, a root `app/layout.tsx` that
80
+ eagerly imports server singletons, and global test setup (`vitest.setup.ts`, `jest.setup.ts`) if it
81
+ bootstraps production graphs. Wiring a heavy init here loads it on **every** cold start (and, under
82
+ the dev server, on every module re-evaluation).
83
+ - **Heavy server-only import markers (what makes an init "heavy" for `CHECK-I23`):** DB/ORM clients
84
+ (`drizzle-orm`, `@prisma/client`, `pg`, `mongoose`, `kysely`), queue/worker libs (`bullmq`, `bull`,
85
+ `kafkajs`, `ioredis`), telemetry/observability SDKs (`@opentelemetry/*`, `@sentry/node`), and a
86
+ whole-service-layer barrel (`import * as services from "@repo/services"`). An init that pulls any of
87
+ these into a universal bootstrap entry is the CHECK-I23 pattern — recommend lazy init at the first
88
+ route/handler/worker that needs it.
89
+
67
90
  ## Testing
68
91
 
69
92
  - **Framework**: Vitest (most common in modern TS), Jest, or testing-library
@@ -93,6 +93,9 @@ After presenting the plan as text, use the host's question mechanism following t
93
93
 
94
94
  **Forge-specific item requirements** layered on top of `author-backlog`'s output:
95
95
  - `specReferences` must be paths **relative to the project root** (e.g. `specs/auth/00-core-definitions.md`), NOT relative to the backlog file. The validator resolves them from the project root (not from `--specs-dir`, which only gates the check).
96
+ - **Epic members — cross-member shared-state coupling (#144).** When authoring the backlog for an epic member, check whether this feature writes or migrates a file that a *sibling* member's already-shipped tests pin (its `mutatesShared[]` hint in `epic-manifest.json`, or a shared data corpus / generated fixture its specs say it mutates). If so, schedule a **reconciliation item up front** — before the first mutating item — that regenerates or re-pins the sibling's fixture (or updates the sibling test to the new shape), so the sibling suite stays green instead of red-gating every commit mid-loop. This is the authoring counterpart to forge-verify's CHECK-E10.
97
+ - **Generated-artifact freshness vs. `testCommand` `--check` gates (#145).** If the project's `testCommand` gates on staleness of generated artifacts (`<generator> --check`-style sub-commands that fail when a checked-in generated file is out of date), then any item that regenerates **one** such artifact must regenerate **and commit all** the sibling artifacts those `--check` gates depend on — enumerate the whole gated set, not just the artifact the item is "about", or the item passes locally yet red-gates on the stale-generated check. Pass this to `author-backlog` alongside `testCommand` so the full regeneration sequence lands in each affected item; forge-verify's CHECK-B26 flags a partial set or an ungated regenerator after the fact.
98
+ - **Lifecycle-gated artifacts — no test item forcing a forbidden transition (#150).** A test/e2e item whose acceptance asserts a *published / released / approved / human-reviewed* artifact state must either (a) `dependsOn` an explicit, human-gated publish/review item that legitimately produces that state, or (b) assert the state via a **dev-build / fixture path**. A test item must **never** be the only thing forcing a lifecycle transition another item pins the other way (e.g. one item keeps artifact `X` *draft* while a test demands it *published*, with nothing between to publish it) — the loop, unable to publish or obtain a human sign-off, will **fabricate** the provenance. `author-backlog` owns the general rule; forge-verify's CHECK-B27 flags a contradictory pair with no publisher in the dependency closure after the fact.
96
99
 
97
100
  > **Backlog schema & rauf contract are unchanged (REQ-COMPAT-03).** Epic membership adds **no** fields to backlog items — dependency edges live in the epic manifest, never in any backlog item. The JSON written here is byte-for-byte the same shape as a pre-epic standalone feature's backlog, and rauf is still launched against a single per-feature backlog path. Only the *path composition* changes (the `{feature}` segment in the backlog-directory rule above), not the schema or rauf's CLI surface.
98
101
 
@@ -130,6 +133,8 @@ State that the backlog is ready and invite adjustments before committing — a s
130
133
 
131
134
  ## Step 7: Update Pipeline State and Commit
132
135
 
136
+ Before writing state or running the stage exit, invoke the **Stage-Completion Re-check** block in `references/shared-conventions.md` with `{stage}` = `forge-4-backlog` — a resumed mid-stage continuation must not overwrite a committed `backlog.json` or re-fire a finished exit.
137
+
133
138
  Write pipeline state conforming to `references/pipeline-state-schema.json`. Follow the Git Commit Protocol in `references/shared-conventions.md`.
134
139
 
135
140
  1. Update `{resolvedFeatureDir}/.pipeline-state.json`: