@specship/specship-win32-arm64 0.24.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. package/bin/specship.cmd +31 -1
  2. package/lib/dist/activation/cheatsheet.d.ts +17 -1
  3. package/lib/dist/activation/cheatsheet.d.ts.map +1 -1
  4. package/lib/dist/activation/cheatsheet.js +47 -3
  5. package/lib/dist/activation/cheatsheet.js.map +1 -1
  6. package/lib/dist/activation/hook-output.d.ts +80 -0
  7. package/lib/dist/activation/hook-output.d.ts.map +1 -0
  8. package/lib/dist/activation/hook-output.js +103 -0
  9. package/lib/dist/activation/hook-output.js.map +1 -0
  10. package/lib/dist/activation/search-intercept.d.ts +60 -29
  11. package/lib/dist/activation/search-intercept.d.ts.map +1 -1
  12. package/lib/dist/activation/search-intercept.js +159 -55
  13. package/lib/dist/activation/search-intercept.js.map +1 -1
  14. package/lib/dist/activation/steering.d.ts +11 -13
  15. package/lib/dist/activation/steering.d.ts.map +1 -1
  16. package/lib/dist/activation/steering.js +25 -10
  17. package/lib/dist/activation/steering.js.map +1 -1
  18. package/lib/dist/behaviour/behaviour-surface.d.ts +47 -0
  19. package/lib/dist/behaviour/behaviour-surface.d.ts.map +1 -1
  20. package/lib/dist/behaviour/behaviour-surface.js +108 -2
  21. package/lib/dist/behaviour/behaviour-surface.js.map +1 -1
  22. package/lib/dist/bin/node-version-check.d.ts +22 -7
  23. package/lib/dist/bin/node-version-check.d.ts.map +1 -1
  24. package/lib/dist/bin/node-version-check.js +39 -9
  25. package/lib/dist/bin/node-version-check.js.map +1 -1
  26. package/lib/dist/bin/specship.js +989 -116
  27. package/lib/dist/bin/specship.js.map +1 -1
  28. package/lib/dist/commands/specship/learn.md +27 -3
  29. package/lib/dist/commands/specship/spec.md +85 -15
  30. package/lib/dist/db/spec-queries.d.ts +73 -3
  31. package/lib/dist/db/spec-queries.d.ts.map +1 -1
  32. package/lib/dist/db/spec-queries.js +139 -5
  33. package/lib/dist/db/spec-queries.js.map +1 -1
  34. package/lib/dist/db/sqlite-adapter.d.ts +7 -5
  35. package/lib/dist/db/sqlite-adapter.d.ts.map +1 -1
  36. package/lib/dist/db/sqlite-adapter.js +11 -9
  37. package/lib/dist/db/sqlite-adapter.js.map +1 -1
  38. package/lib/dist/enforce/changed-files.d.ts +39 -0
  39. package/lib/dist/enforce/changed-files.d.ts.map +1 -0
  40. package/lib/dist/enforce/changed-files.js +114 -0
  41. package/lib/dist/enforce/changed-files.js.map +1 -0
  42. package/lib/dist/enforce/enforce.d.ts +53 -2
  43. package/lib/dist/enforce/enforce.d.ts.map +1 -1
  44. package/lib/dist/enforce/enforce.js +95 -8
  45. package/lib/dist/enforce/enforce.js.map +1 -1
  46. package/lib/dist/enforce/mutation.d.ts +46 -0
  47. package/lib/dist/enforce/mutation.d.ts.map +1 -0
  48. package/lib/dist/enforce/mutation.js +104 -0
  49. package/lib/dist/enforce/mutation.js.map +1 -0
  50. package/lib/dist/enforce/suppressions.d.ts +33 -0
  51. package/lib/dist/enforce/suppressions.d.ts.map +1 -0
  52. package/lib/dist/enforce/suppressions.js +159 -0
  53. package/lib/dist/enforce/suppressions.js.map +1 -0
  54. package/lib/dist/extraction/grammars.d.ts +10 -0
  55. package/lib/dist/extraction/grammars.d.ts.map +1 -1
  56. package/lib/dist/extraction/grammars.js +32 -3
  57. package/lib/dist/extraction/grammars.js.map +1 -1
  58. package/lib/dist/extraction/index.d.ts +7 -0
  59. package/lib/dist/extraction/index.d.ts.map +1 -1
  60. package/lib/dist/extraction/index.js.map +1 -1
  61. package/lib/dist/extraction/specs/markdown-spec-extractor.d.ts +7 -0
  62. package/lib/dist/extraction/specs/markdown-spec-extractor.d.ts.map +1 -1
  63. package/lib/dist/extraction/specs/markdown-spec-extractor.js +97 -10
  64. package/lib/dist/extraction/specs/markdown-spec-extractor.js.map +1 -1
  65. package/lib/dist/extraction/wasm/tree-sitter-pascal.wasm.gz +0 -0
  66. package/lib/dist/extraction/wasm/tree-sitter-scala.wasm.gz +0 -0
  67. package/lib/dist/graph/spec-coverage.d.ts +142 -0
  68. package/lib/dist/graph/spec-coverage.d.ts.map +1 -0
  69. package/lib/dist/graph/spec-coverage.js +276 -0
  70. package/lib/dist/graph/spec-coverage.js.map +1 -0
  71. package/lib/dist/health/smoke-check.js +2 -2
  72. package/lib/dist/health/smoke-check.js.map +1 -1
  73. package/lib/dist/index.d.ts +75 -7
  74. package/lib/dist/index.d.ts.map +1 -1
  75. package/lib/dist/index.js +191 -23
  76. package/lib/dist/index.js.map +1 -1
  77. package/lib/dist/installer/gemini-commands.d.ts +107 -0
  78. package/lib/dist/installer/gemini-commands.d.ts.map +1 -0
  79. package/lib/dist/installer/gemini-commands.js +264 -0
  80. package/lib/dist/installer/gemini-commands.js.map +1 -0
  81. package/lib/dist/installer/index.d.ts +55 -8
  82. package/lib/dist/installer/index.d.ts.map +1 -1
  83. package/lib/dist/installer/index.js +72 -53
  84. package/lib/dist/installer/index.js.map +1 -1
  85. package/lib/dist/installer/init-offer.d.ts +14 -9
  86. package/lib/dist/installer/init-offer.d.ts.map +1 -1
  87. package/lib/dist/installer/init-offer.js +13 -9
  88. package/lib/dist/installer/init-offer.js.map +1 -1
  89. package/lib/dist/installer/install-defaults.d.ts +81 -0
  90. package/lib/dist/installer/install-defaults.d.ts.map +1 -0
  91. package/lib/dist/installer/install-defaults.js +95 -0
  92. package/lib/dist/installer/install-defaults.js.map +1 -0
  93. package/lib/dist/installer/instructions-template.d.ts +22 -0
  94. package/lib/dist/installer/instructions-template.d.ts.map +1 -1
  95. package/lib/dist/installer/instructions-template.js +37 -1
  96. package/lib/dist/installer/instructions-template.js.map +1 -1
  97. package/lib/dist/installer/targets/claude.d.ts +18 -0
  98. package/lib/dist/installer/targets/claude.d.ts.map +1 -1
  99. package/lib/dist/installer/targets/claude.js +70 -23
  100. package/lib/dist/installer/targets/claude.js.map +1 -1
  101. package/lib/dist/installer/targets/gemini.d.ts +85 -12
  102. package/lib/dist/installer/targets/gemini.d.ts.map +1 -1
  103. package/lib/dist/installer/targets/gemini.js +527 -30
  104. package/lib/dist/installer/targets/gemini.js.map +1 -1
  105. package/lib/dist/installer/targets/shared.d.ts +13 -0
  106. package/lib/dist/installer/targets/shared.d.ts.map +1 -1
  107. package/lib/dist/installer/targets/shared.js +22 -4
  108. package/lib/dist/installer/targets/shared.js.map +1 -1
  109. package/lib/dist/installer/targets/types.d.ts +12 -0
  110. package/lib/dist/installer/targets/types.d.ts.map +1 -1
  111. package/lib/dist/jira/auto-publish.d.ts +71 -4
  112. package/lib/dist/jira/auto-publish.d.ts.map +1 -1
  113. package/lib/dist/jira/auto-publish.js +186 -17
  114. package/lib/dist/jira/auto-publish.js.map +1 -1
  115. package/lib/dist/jira/reconcile.d.ts +24 -1
  116. package/lib/dist/jira/reconcile.d.ts.map +1 -1
  117. package/lib/dist/jira/reconcile.js +46 -2
  118. package/lib/dist/jira/reconcile.js.map +1 -1
  119. package/lib/dist/jira/repo-config.d.ts +9 -0
  120. package/lib/dist/jira/repo-config.d.ts.map +1 -1
  121. package/lib/dist/jira/repo-config.js +11 -0
  122. package/lib/dist/jira/repo-config.js.map +1 -1
  123. package/lib/dist/jira/types.d.ts +7 -0
  124. package/lib/dist/jira/types.d.ts.map +1 -1
  125. package/lib/dist/jira/types.js.map +1 -1
  126. package/lib/dist/knowledge/enforce.d.ts +75 -0
  127. package/lib/dist/knowledge/enforce.d.ts.map +1 -0
  128. package/lib/dist/knowledge/enforce.js +250 -0
  129. package/lib/dist/knowledge/enforce.js.map +1 -0
  130. package/lib/dist/knowledge/facts.d.ts +33 -0
  131. package/lib/dist/knowledge/facts.d.ts.map +1 -0
  132. package/lib/dist/knowledge/facts.js +117 -0
  133. package/lib/dist/knowledge/facts.js.map +1 -0
  134. package/lib/dist/knowledge/proposals.d.ts +88 -0
  135. package/lib/dist/knowledge/proposals.d.ts.map +1 -0
  136. package/lib/dist/knowledge/proposals.js +360 -0
  137. package/lib/dist/knowledge/proposals.js.map +1 -0
  138. package/lib/dist/knowledge/stale.d.ts +64 -0
  139. package/lib/dist/knowledge/stale.d.ts.map +1 -0
  140. package/lib/dist/knowledge/stale.js +174 -0
  141. package/lib/dist/knowledge/stale.js.map +1 -0
  142. package/lib/dist/mcp/fitness-tool.d.ts +5 -6
  143. package/lib/dist/mcp/fitness-tool.d.ts.map +1 -1
  144. package/lib/dist/mcp/fitness-tool.js +0 -16
  145. package/lib/dist/mcp/fitness-tool.js.map +1 -1
  146. package/lib/dist/mcp/gemini-session.d.ts +50 -0
  147. package/lib/dist/mcp/gemini-session.d.ts.map +1 -0
  148. package/lib/dist/mcp/gemini-session.js +159 -0
  149. package/lib/dist/mcp/gemini-session.js.map +1 -0
  150. package/lib/dist/mcp/health-tool.d.ts +26 -0
  151. package/lib/dist/mcp/health-tool.d.ts.map +1 -0
  152. package/lib/dist/mcp/health-tool.js +50 -0
  153. package/lib/dist/mcp/health-tool.js.map +1 -0
  154. package/lib/dist/mcp/jira-tools.d.ts.map +1 -1
  155. package/lib/dist/mcp/jira-tools.js +25 -0
  156. package/lib/dist/mcp/jira-tools.js.map +1 -1
  157. package/lib/dist/mcp/maintainability-tool.d.ts +5 -7
  158. package/lib/dist/mcp/maintainability-tool.d.ts.map +1 -1
  159. package/lib/dist/mcp/maintainability-tool.js +0 -16
  160. package/lib/dist/mcp/maintainability-tool.js.map +1 -1
  161. package/lib/dist/mcp/model-context.d.ts +31 -7
  162. package/lib/dist/mcp/model-context.d.ts.map +1 -1
  163. package/lib/dist/mcp/model-context.js +70 -21
  164. package/lib/dist/mcp/model-context.js.map +1 -1
  165. package/lib/dist/mcp/proxy.d.ts.map +1 -1
  166. package/lib/dist/mcp/proxy.js +5 -2
  167. package/lib/dist/mcp/proxy.js.map +1 -1
  168. package/lib/dist/mcp/server-instructions.d.ts +16 -15
  169. package/lib/dist/mcp/server-instructions.d.ts.map +1 -1
  170. package/lib/dist/mcp/server-instructions.js +72 -5
  171. package/lib/dist/mcp/server-instructions.js.map +1 -1
  172. package/lib/dist/mcp/session.js +1 -1
  173. package/lib/dist/mcp/session.js.map +1 -1
  174. package/lib/dist/mcp/spec-tools.d.ts +9 -1
  175. package/lib/dist/mcp/spec-tools.d.ts.map +1 -1
  176. package/lib/dist/mcp/spec-tools.js +282 -49
  177. package/lib/dist/mcp/spec-tools.js.map +1 -1
  178. package/lib/dist/mcp/tools.d.ts +49 -9
  179. package/lib/dist/mcp/tools.d.ts.map +1 -1
  180. package/lib/dist/mcp/tools.js +210 -58
  181. package/lib/dist/mcp/tools.js.map +1 -1
  182. package/lib/dist/pulse/pulse.d.ts +93 -0
  183. package/lib/dist/pulse/pulse.d.ts.map +1 -0
  184. package/lib/dist/pulse/pulse.js +401 -0
  185. package/lib/dist/pulse/pulse.js.map +1 -0
  186. package/lib/dist/reflect/apply.d.ts.map +1 -1
  187. package/lib/dist/reflect/apply.js +38 -3
  188. package/lib/dist/reflect/apply.js.map +1 -1
  189. package/lib/dist/reflect/auto-sweep.d.ts +26 -0
  190. package/lib/dist/reflect/auto-sweep.d.ts.map +1 -0
  191. package/lib/dist/reflect/auto-sweep.js +90 -0
  192. package/lib/dist/reflect/auto-sweep.js.map +1 -0
  193. package/lib/dist/reflect/cli.d.ts +62 -0
  194. package/lib/dist/reflect/cli.d.ts.map +1 -0
  195. package/lib/dist/reflect/cli.js +163 -0
  196. package/lib/dist/reflect/cli.js.map +1 -0
  197. package/lib/dist/reflect/miner.d.ts.map +1 -1
  198. package/lib/dist/reflect/miner.js +119 -0
  199. package/lib/dist/reflect/miner.js.map +1 -1
  200. package/lib/dist/reflect/sweep.d.ts +1 -0
  201. package/lib/dist/reflect/sweep.d.ts.map +1 -1
  202. package/lib/dist/reflect/sweep.js +62 -13
  203. package/lib/dist/reflect/sweep.js.map +1 -1
  204. package/lib/dist/reflect/targets.d.ts +2 -0
  205. package/lib/dist/reflect/targets.d.ts.map +1 -1
  206. package/lib/dist/reflect/targets.js +33 -0
  207. package/lib/dist/reflect/targets.js.map +1 -1
  208. package/lib/dist/reflect/types.d.ts +19 -2
  209. package/lib/dist/reflect/types.d.ts.map +1 -1
  210. package/lib/dist/resolution/spec-link-resolver.d.ts +44 -2
  211. package/lib/dist/resolution/spec-link-resolver.d.ts.map +1 -1
  212. package/lib/dist/resolution/spec-link-resolver.js +132 -0
  213. package/lib/dist/resolution/spec-link-resolver.js.map +1 -1
  214. package/lib/dist/resolution/test-title-links.d.ts +62 -0
  215. package/lib/dist/resolution/test-title-links.d.ts.map +1 -0
  216. package/lib/dist/resolution/test-title-links.js +97 -0
  217. package/lib/dist/resolution/test-title-links.js.map +1 -0
  218. package/lib/dist/search/query-parser.d.ts.map +1 -1
  219. package/lib/dist/search/query-parser.js +6 -1
  220. package/lib/dist/search/query-parser.js.map +1 -1
  221. package/lib/dist/server/project-registry.js +1 -1
  222. package/lib/dist/server/routes/claude.js +24 -43
  223. package/lib/dist/server/routes/graph.js +53 -7
  224. package/lib/dist/server/routes/knowledge.js +118 -0
  225. package/lib/dist/server/routes/mcp.js +4 -3
  226. package/lib/dist/server/routes/projects.js +4 -4
  227. package/lib/dist/server/routes/spec.js +49 -7
  228. package/lib/dist/server/server.js +31 -17
  229. package/lib/dist/skills/spec-author/SKILL.md +164 -0
  230. package/lib/dist/skills/spec-author/references/format.md +210 -0
  231. package/lib/dist/skills/spec-author/references/gap-questions.md +152 -0
  232. package/lib/dist/skills/spec-author/references/quality-rubric.md +125 -0
  233. package/lib/dist/skills/spec-author/references/review-checklist.md +175 -0
  234. package/lib/dist/spec-lint/lint.d.ts +82 -0
  235. package/lib/dist/spec-lint/lint.d.ts.map +1 -0
  236. package/lib/dist/spec-lint/lint.js +491 -0
  237. package/lib/dist/spec-lint/lint.js.map +1 -0
  238. package/lib/dist/spec-quality/criterion-ids.d.ts +13 -0
  239. package/lib/dist/spec-quality/criterion-ids.d.ts.map +1 -0
  240. package/lib/dist/spec-quality/criterion-ids.js +38 -0
  241. package/lib/dist/spec-quality/criterion-ids.js.map +1 -0
  242. package/lib/dist/spec-quality/evidence-kind.d.ts +54 -0
  243. package/lib/dist/spec-quality/evidence-kind.d.ts.map +1 -0
  244. package/lib/dist/spec-quality/evidence-kind.js +183 -0
  245. package/lib/dist/spec-quality/evidence-kind.js.map +1 -0
  246. package/lib/dist/spec-quality/link-persist.d.ts +65 -0
  247. package/lib/dist/spec-quality/link-persist.d.ts.map +1 -0
  248. package/lib/dist/spec-quality/link-persist.js +249 -0
  249. package/lib/dist/spec-quality/link-persist.js.map +1 -0
  250. package/lib/dist/spec-quality/plan-scope.d.ts +38 -0
  251. package/lib/dist/spec-quality/plan-scope.d.ts.map +1 -0
  252. package/lib/dist/spec-quality/plan-scope.js +129 -0
  253. package/lib/dist/spec-quality/plan-scope.js.map +1 -0
  254. package/lib/dist/spec-quality/postcheck.d.ts +85 -0
  255. package/lib/dist/spec-quality/postcheck.d.ts.map +1 -0
  256. package/lib/dist/spec-quality/postcheck.js +390 -0
  257. package/lib/dist/spec-quality/postcheck.js.map +1 -0
  258. package/lib/dist/spec-quality/readiness.d.ts +72 -0
  259. package/lib/dist/spec-quality/readiness.d.ts.map +1 -0
  260. package/lib/dist/spec-quality/readiness.js +226 -0
  261. package/lib/dist/spec-quality/readiness.js.map +1 -0
  262. package/lib/dist/statusline/index.js.map +1 -1
  263. package/lib/dist/statusline/render.d.ts +1 -1
  264. package/lib/dist/statusline/render.d.ts.map +1 -1
  265. package/lib/dist/statusline/render.js +3 -1
  266. package/lib/dist/statusline/render.js.map +1 -1
  267. package/lib/dist/ui/assets/index-BzuUWUAE.js +50 -0
  268. package/lib/dist/ui/index.html +1 -1
  269. package/lib/dist/verify/report-ingest.d.ts +88 -0
  270. package/lib/dist/verify/report-ingest.d.ts.map +1 -0
  271. package/lib/dist/verify/report-ingest.js +269 -0
  272. package/lib/dist/verify/report-ingest.js.map +1 -0
  273. package/lib/dist/workflows/agents/claude.d.ts +16 -0
  274. package/lib/dist/workflows/agents/claude.d.ts.map +1 -0
  275. package/lib/dist/workflows/agents/claude.js +82 -0
  276. package/lib/dist/workflows/agents/claude.js.map +1 -0
  277. package/lib/dist/workflows/agents/gemini.d.ts +21 -0
  278. package/lib/dist/workflows/agents/gemini.d.ts.map +1 -0
  279. package/lib/dist/workflows/agents/gemini.js +124 -0
  280. package/lib/dist/workflows/agents/gemini.js.map +1 -0
  281. package/lib/dist/workflows/agents/index.d.ts +28 -0
  282. package/lib/dist/workflows/agents/index.d.ts.map +1 -0
  283. package/lib/dist/workflows/agents/index.js +104 -0
  284. package/lib/dist/workflows/agents/index.js.map +1 -0
  285. package/lib/dist/workflows/agents/types.d.ts +55 -0
  286. package/lib/dist/workflows/agents/types.d.ts.map +1 -0
  287. package/lib/dist/workflows/agents/types.js +46 -0
  288. package/lib/dist/workflows/agents/types.js.map +1 -0
  289. package/lib/dist/workflows/batch.d.ts +63 -0
  290. package/lib/dist/workflows/batch.d.ts.map +1 -0
  291. package/lib/dist/workflows/batch.js +151 -0
  292. package/lib/dist/workflows/batch.js.map +1 -0
  293. package/lib/dist/workflows/defaults/claude-design-implement.yaml +3 -4
  294. package/lib/dist/workflows/defaults/spec-author.yaml +11 -9
  295. package/lib/dist/workflows/defaults/spec-fix.yaml +103 -12
  296. package/lib/dist/workflows/defaults/spec-implement-mixed.yaml +21 -193
  297. package/lib/dist/workflows/defaults/spec-implement.yaml +701 -41
  298. package/lib/dist/workflows/defaults/spec-verify.yaml +60 -9
  299. package/lib/dist/workflows/discovery.d.ts +13 -0
  300. package/lib/dist/workflows/discovery.d.ts.map +1 -1
  301. package/lib/dist/workflows/discovery.js +79 -17
  302. package/lib/dist/workflows/discovery.js.map +1 -1
  303. package/lib/dist/workflows/executor.d.ts +18 -0
  304. package/lib/dist/workflows/executor.d.ts.map +1 -1
  305. package/lib/dist/workflows/executor.js +130 -33
  306. package/lib/dist/workflows/executor.js.map +1 -1
  307. package/lib/dist/workflows/gc.d.ts +50 -0
  308. package/lib/dist/workflows/gc.d.ts.map +1 -0
  309. package/lib/dist/workflows/gc.js +120 -0
  310. package/lib/dist/workflows/gc.js.map +1 -0
  311. package/lib/dist/workflows/merge-queue.d.ts +64 -0
  312. package/lib/dist/workflows/merge-queue.d.ts.map +1 -0
  313. package/lib/dist/workflows/merge-queue.js +162 -0
  314. package/lib/dist/workflows/merge-queue.js.map +1 -0
  315. package/lib/dist/workflows/runners/approval.d.ts +1 -1
  316. package/lib/dist/workflows/runners/approval.d.ts.map +1 -1
  317. package/lib/dist/workflows/runners/approval.js +11 -2
  318. package/lib/dist/workflows/runners/approval.js.map +1 -1
  319. package/lib/dist/workflows/runners/bash.d.ts +15 -0
  320. package/lib/dist/workflows/runners/bash.d.ts.map +1 -1
  321. package/lib/dist/workflows/runners/bash.js +50 -1
  322. package/lib/dist/workflows/runners/bash.js.map +1 -1
  323. package/lib/dist/workflows/runners/prompt.d.ts +16 -41
  324. package/lib/dist/workflows/runners/prompt.d.ts.map +1 -1
  325. package/lib/dist/workflows/runners/prompt.js +55 -120
  326. package/lib/dist/workflows/runners/prompt.js.map +1 -1
  327. package/lib/dist/workflows/runners/types.d.ts +12 -0
  328. package/lib/dist/workflows/runners/types.d.ts.map +1 -1
  329. package/lib/dist/workflows/schemas/workflow.d.ts +17 -0
  330. package/lib/dist/workflows/schemas/workflow.d.ts.map +1 -1
  331. package/lib/dist/workflows/schemas/workflow.js +27 -0
  332. package/lib/dist/workflows/schemas/workflow.js.map +1 -1
  333. package/lib/node_modules/.package-lock.json +1 -1
  334. package/lib/package.json +8 -4
  335. package/package.json +5 -3
  336. package/lib/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  337. package/lib/dist/extraction/wasm/tree-sitter-scala.wasm +0 -0
  338. package/lib/dist/server/ingest/impact-query.js +0 -343
  339. package/lib/dist/server/routes/events.js +0 -152
  340. package/lib/dist/ui/assets/index-CjjaftDw.js +0 -50
  341. package/node.exe +0 -4
@@ -14,8 +14,9 @@ inputs:
14
14
  systemPromptAppend: |
15
15
  You are implementing a specific requirement. Before editing code, call
16
16
  specship_spec($INPUT.SPEC_ID) to see the spec body and any code it
17
- already links to. After editing, call specship_link_assert with the
18
- spec_id and the symbol you implemented BEFORE reporting done.
17
+ already links to. After editing, record each symbol you implemented as a
18
+ `path:Symbol` bullet under the requirement's `implementations:` block in
19
+ its spec file — links in the spec file survive any reindex.
19
20
 
20
21
  JIRA-backed specs (REQ-JIRAPUB-004): if the spec file's frontmatter
21
22
  carries a `jira_issue:` key, EVERY git commit message you write for this
@@ -41,9 +42,30 @@ nodes:
41
42
  (b) which code areas / symbols are most relevant, and (c) whether
42
43
  any code currently claims to implement it (look at linked_code).
43
44
 
45
+ - id: readiness
46
+ kind: bash
47
+ depends_on: [fetch_spec]
48
+ output_type: readiness_report
49
+ timeout: 120000
50
+ bash: |
51
+ # REQ-PIPETRUST-004 — implement must not start on a spec that is not
52
+ # ready (lint errors, no criteria, a criterion still [needs review]).
53
+ # Also records the run's base commit so postcheck can diff against it.
54
+ git rev-parse HEAD > "$(git rev-parse --git-dir)/specship-run-base" 2>/dev/null || true
55
+ if [ -n "$SPECSHIP_CLI_SCRIPT" ]; then ss_cli() { "$SPECSHIP_CLI_NODE" "$SPECSHIP_CLI_SCRIPT" "$@"; }; SS=ss_cli
56
+ elif command -v specship >/dev/null 2>&1; then SS=specship
57
+ elif [ -x node_modules/.bin/specship ]; then SS=node_modules/.bin/specship
58
+ else SS=""
59
+ fi
60
+ if [ -z "$SS" ]; then
61
+ echo "READY=unavailable — specship CLI not found; the readiness gate did NOT run"
62
+ exit 1
63
+ fi
64
+ $SS ready $INPUT.SPEC_ID --path .
65
+
44
66
  - id: plan
45
67
  kind: prompt
46
- depends_on: [fetch_spec]
68
+ depends_on: [fetch_spec, readiness]
47
69
  output_type: plan
48
70
  allowed_tools:
49
71
  - mcp__specship__specship_explore
@@ -55,6 +77,17 @@ nodes:
55
77
 
56
78
  $fetch_spec.output
57
79
 
80
+ Readiness + evidence plan (every acceptance criterion needs a test of
81
+ the stated kind, written as a NAMED function passed to it() with the
82
+ criterion id in its title):
83
+ $readiness.output
84
+
85
+ Domain facts that `specship_spec` returns with the requirement are
86
+ constraints, not background: honour each external fact's verified
87
+ version, and treat every `## Enforced` rule (`<glob>` must not match
88
+ `/<regex>/`) as a hard constraint — `specship knowledge check` fails the
89
+ run on a violation (REQ-KNOW-005).
90
+
58
91
  The plan should list:
59
92
  - Files to create or modify (with project-relative paths)
60
93
  - Specific functions/methods/classes to add or change
@@ -63,23 +96,93 @@ nodes:
63
96
 
64
97
  Keep it tight (≤500 words). Do NOT edit code in this step.
65
98
 
99
+ - id: plan_scope
100
+ kind: bash
101
+ depends_on: [plan]
102
+ output_type: plan_scope
103
+ bash: |
104
+ # REQ-THRU-003 — opt-in auto-approval of a small, in-scope plan. Prints
105
+ # AUTO_APPROVE=yes only when specship.config.json opts in, the plan
106
+ # names only the requirement's implementation files (plus tests, its
107
+ # spec and CHANGELOG.md), and the requirement has few criteria.
108
+ PLAN=$plan.output
109
+ F="$(mktemp)"
110
+ printf '%s\n' "$PLAN" > "$F"
111
+ if [ -n "$SPECSHIP_CLI_SCRIPT" ]; then "$SPECSHIP_CLI_NODE" "$SPECSHIP_CLI_SCRIPT" plan-scope $INPUT.SPEC_ID --plan-file "$F" --path .
112
+ elif command -v specship >/dev/null 2>&1; then specship plan-scope $INPUT.SPEC_ID --plan-file "$F" --path . || echo "AUTO_APPROVE=no"
113
+ else echo "AUTO_APPROVE=no"; echo "- specship CLI not found"
114
+ fi
115
+ rm -f "$F"
116
+
66
117
  - id: approve_plan
67
118
  kind: approval
68
- depends_on: [plan]
119
+ depends_on: [plan, plan_scope]
120
+ auto_approve:
121
+ from: plan_scope
122
+ marker: AUTO_APPROVE=yes
69
123
  message: |
70
124
  Plan ready for $INPUT.SPEC_ID. Review the plan output and approve
71
125
  to proceed with implementation, or reject with feedback to revise.
72
126
  capture_response: true
73
127
  on_reject:
128
+ # The reviewer's rejection reason is appended to this prompt
129
+ # automatically; the revision replaces $plan.output downstream.
74
130
  prompt: |
75
- The plan was rejected with this feedback. Revise:
131
+ The plan was rejected (feedback follows). Revise the plan to
132
+ address it and output the full revised plan.
76
133
 
77
- $approve_plan.output
134
+ Current plan:
135
+ $plan.output
136
+ revises: plan
78
137
  max_attempts: 3
79
138
 
139
+ - id: quality_baseline
140
+ kind: bash
141
+ depends_on: [approve_plan]
142
+ output_type: quality_report
143
+ timeout: 600000
144
+ bash: |
145
+ # REQ-AUTHG-008.A1 — run the project's quality command BEFORE implement
146
+ # edits anything, so the gate can tell errors this change introduced
147
+ # from errors the worktree already had (a sub-package with no built
148
+ # parent, known lint debt). The baseline lives under the worktree's git
149
+ # dir, so it never shows up in the diff. It is written ONLY when the
150
+ # pre-change command fails: a clean baseline means any later failure is
151
+ # the change's, so the gate keeps its full strictness (A5 fallback).
152
+ qnorm() { sed -E 's/\([0-9]+,[0-9]+\)//g; s/:[0-9]+:[0-9]+/:/g; s/:[0-9]+:/:/g' | sed -E '/^[[:space:]]*$/d' | sort -u; }
153
+ BASELINE="$(git rev-parse --git-dir 2>/dev/null)/specship-quality-baseline.txt"
154
+ rm -f "$BASELINE"
155
+ QUALITY_CMD=""
156
+ if [ -f package.json ]; then
157
+ if [ ! -d node_modules ]; then
158
+ npm ci --no-audit --no-fund >/dev/null 2>&1 || npm install --no-audit --no-fund >/dev/null 2>&1 || true
159
+ fi
160
+ if node -e 'const s=(require("./package.json").scripts)||{};process.exit(s.lint?0:1)' 2>/dev/null; then
161
+ QUALITY_CMD="npm run lint --if-present"
162
+ elif [ -f tsconfig.json ]; then
163
+ QUALITY_CMD="npx --no-install tsc --noEmit"
164
+ fi
165
+ elif [ -f Cargo.toml ]; then
166
+ if command -v cargo >/dev/null 2>&1; then QUALITY_CMD="cargo clippy --no-deps"; fi
167
+ elif [ -f pyproject.toml ] || [ -f requirements.txt ]; then
168
+ if command -v ruff >/dev/null 2>&1; then QUALITY_CMD="ruff check ."; fi
169
+ fi
170
+ if [ -z "$QUALITY_CMD" ]; then
171
+ echo "QUALITY_BASELINE=skipped"
172
+ exit 0
173
+ fi
174
+ if OUT=$($QUALITY_CMD 2>&1); then
175
+ echo "QUALITY_BASELINE=clean"
176
+ else
177
+ printf '%s\n' "$OUT" | qnorm > "$BASELINE"
178
+ echo "QUALITY_BASELINE=recorded"
179
+ echo "QUALITY_BASELINE_LINES=$(wc -l < "$BASELINE" | tr -d ' ')"
180
+ fi
181
+ exit 0
182
+
80
183
  - id: implement
81
184
  kind: prompt
82
- depends_on: [approve_plan]
185
+ depends_on: [approve_plan, quality_baseline]
83
186
  output_type: diff
84
187
  allowed_tools:
85
188
  - Read
@@ -92,17 +195,302 @@ nodes:
92
195
  prompt: |
93
196
  Implement the approved plan. Follow it closely.
94
197
 
198
+ Domain facts that `specship_spec` returns with the requirement are
199
+ constraints, not background: honour each external fact's verified
200
+ version, and treat every `## Enforced` rule (`<glob>` must not match
201
+ `/<regex>/`) as a hard constraint — `specship knowledge check` fails the
202
+ run on a violation (REQ-KNOW-005).
203
+
95
204
  Plan:
96
205
  $plan.output
97
206
 
98
207
  Approval feedback (if any):
99
208
  $approve_plan.output
100
209
 
210
+ Readiness + evidence plan (every acceptance criterion needs a test of
211
+ the stated kind, written as a NAMED function passed to it() with the
212
+ criterion id in its title):
213
+ $readiness.output
214
+
101
215
  When done, output a short diff summary (which files changed and why).
102
216
 
103
- - id: verify
217
+ - id: quality_gate
104
218
  kind: bash
105
219
  depends_on: [implement]
220
+ output_type: quality_report
221
+ timeout: 600000
222
+ bash: |
223
+ # REQ-AUTHG-005.A1 — the project's own lint/typecheck, run in the
224
+ # worktree right after implement. The QUALITY_GATE marker is the
225
+ # contract the remediation node routes on: a project with neither a lint
226
+ # script nor a typecheck config says so explicitly, so "nothing ran" can
227
+ # never be mistaken for "clean" (same doctrine as VERIFY_RESULT=skipped).
228
+ QUALITY_CMD=""
229
+ if [ -f package.json ]; then
230
+ # The worktree may not have dependencies yet (verify installs them
231
+ # later) and lint needs them — bootstrap quietly, and treat a failed
232
+ # install as "no gate" rather than as a lint failure.
233
+ if [ ! -d node_modules ]; then
234
+ npm ci --no-audit --no-fund >/dev/null 2>&1 || npm install --no-audit --no-fund >/dev/null 2>&1 || true
235
+ fi
236
+ if node -e 'const s=(require("./package.json").scripts)||{};process.exit(s.lint?0:1)' 2>/dev/null; then
237
+ QUALITY_CMD="npm run lint --if-present"
238
+ elif [ -f tsconfig.json ]; then
239
+ QUALITY_CMD="npx --no-install tsc --noEmit"
240
+ fi
241
+ elif [ -f Cargo.toml ]; then
242
+ if command -v cargo >/dev/null 2>&1; then QUALITY_CMD="cargo clippy --no-deps"; fi
243
+ elif [ -f pyproject.toml ] || [ -f requirements.txt ]; then
244
+ if command -v ruff >/dev/null 2>&1; then QUALITY_CMD="ruff check ."; fi
245
+ fi
246
+ if [ -z "$QUALITY_CMD" ]; then
247
+ echo "QUALITY_GATE=skipped"
248
+ echo "QUALITY_REASON=no lint script and no typecheck configuration in this project"
249
+ exit 0
250
+ fi
251
+ echo "QUALITY_CMD=$QUALITY_CMD"
252
+ qnorm() { sed -E 's/\([0-9]+,[0-9]+\)//g; s/:[0-9]+:[0-9]+/:/g; s/:[0-9]+:/:/g' | sed -E '/^[[:space:]]*$/d' | sort -u; }
253
+ BASELINE="$(git rev-parse --git-dir 2>/dev/null)/specship-quality-baseline.txt"
254
+ if OUT=$($QUALITY_CMD 2>&1); then
255
+ echo "QUALITY_GATE=passed"
256
+ elif [ -f "$BASELINE" ]; then
257
+ # REQ-AUTHG-008.A2/A3 — judge only error lines absent from the
258
+ # pre-change baseline; report pre-existing ones, never hide them.
259
+ NEW=$(printf '%s\n' "$OUT" | qnorm | comm -13 "$BASELINE" - | grep -iE 'error' || true)
260
+ PRE=$(printf '%s\n' "$OUT" | qnorm | comm -12 "$BASELINE" - | grep -ciE 'error' || true)
261
+ if [ -z "$NEW" ]; then
262
+ echo "QUALITY_GATE=passed"
263
+ echo "QUALITY_PREEXISTING=$PRE"
264
+ echo "Pre-existing errors (already failing before implement; not caused by this change), sample:"
265
+ printf '%s\n' "$OUT" | grep -iE 'error' | head -n 5
266
+ else
267
+ echo "QUALITY_GATE=failed"
268
+ echo "QUALITY_PREEXISTING=$PRE"
269
+ echo "New errors introduced by this change:"
270
+ printf '%s\n' "$NEW" | tail -n 80
271
+ fi
272
+ else
273
+ echo "QUALITY_GATE=failed"
274
+ printf '%s\n' "$OUT" | tail -n 80
275
+ fi
276
+ # Exiting 0 on a lint failure is deliberate: the marker — not this node's
277
+ # exit code — drives the revise loop. quality_recheck is what halts.
278
+
279
+ - id: fix_quality
280
+ kind: prompt
281
+ depends_on: [quality_gate]
282
+ output_type: quality_fix
283
+ allowed_tools:
284
+ - Read
285
+ - Edit
286
+ - Write
287
+ - Bash
288
+ - mcp__specship__specship_explore
289
+ - mcp__specship__specship_node
290
+ prompt: |
291
+ Lint / typecheck result for the code you just wrote:
292
+
293
+ $quality_gate.output
294
+
295
+ Route on the QUALITY_GATE marker — do not re-judge the raw output:
296
+
297
+ - QUALITY_GATE=passed or QUALITY_GATE=skipped → change NOTHING. Output
298
+ exactly `FIX_QUALITY=not-needed` and stop.
299
+ - QUALITY_GATE=failed → fix every reported error, then re-run the
300
+ QUALITY_CMD shown above to confirm. Fix the code, never the lint
301
+ configuration; never silence or disable a rule to make it pass.
302
+
303
+ Output `FIX_QUALITY=fixed` (clean re-run) or `FIX_QUALITY=still-failing`
304
+ followed by the errors that remain.
305
+
306
+ - id: quality_recheck
307
+ kind: bash
308
+ depends_on: [fix_quality]
309
+ output_type: quality_result
310
+ timeout: 600000
311
+ bash: |
312
+ # REQ-AUTHG-005.A1 — the authoritative half of the gate. The fix node is
313
+ # a prompt, so its "FIX_QUALITY=fixed" claim is a claim, not evidence:
314
+ # this node re-runs the same command and halts the run while it still
315
+ # fails, so failing lint/typecheck never reaches verify, the link node,
316
+ # or the human gate.
317
+ GATE_OUT=$quality_gate.output
318
+ MARKER=$(printf '%s' "$GATE_OUT" | grep -oE 'QUALITY_GATE=[a-z-]+' | tail -n 1 | cut -d= -f2)
319
+ CMD=$(printf '%s' "$GATE_OUT" | grep -E '^QUALITY_CMD=' | tail -n 1 | cut -d= -f2-)
320
+ if [ "$MARKER" = "skipped" ] || [ -z "$CMD" ]; then
321
+ echo "QUALITY_RESULT=skipped"
322
+ echo "This project has no lint or typecheck gate — nothing was checked."
323
+ exit 0
324
+ fi
325
+ if OUT=$($CMD 2>&1); then
326
+ echo "QUALITY_RESULT=clean"
327
+ exit 0
328
+ fi
329
+ qnorm() { sed -E 's/\([0-9]+,[0-9]+\)//g; s/:[0-9]+:[0-9]+/:/g; s/:[0-9]+:/:/g' | sed -E '/^[[:space:]]*$/d' | sort -u; }
330
+ BASELINE="$(git rev-parse --git-dir 2>/dev/null)/specship-quality-baseline.txt"
331
+ if [ -f "$BASELINE" ]; then
332
+ # REQ-AUTHG-008.A2/A3 — only errors absent from the baseline halt.
333
+ NEW=$(printf '%s\n' "$OUT" | qnorm | comm -13 "$BASELINE" - | grep -iE 'error' || true)
334
+ if [ -z "$NEW" ]; then
335
+ echo "QUALITY_RESULT=clean"
336
+ echo "QUALITY_PREEXISTING=$(printf '%s\n' "$OUT" | qnorm | comm -12 "$BASELINE" - | grep -ciE 'error' || true)"
337
+ exit 0
338
+ fi
339
+ echo "QUALITY_RESULT=failed"
340
+ printf '%s\n' "$NEW" | tail -n 80
341
+ else
342
+ echo "QUALITY_RESULT=failed"
343
+ printf '%s\n' "$OUT" | tail -n 80
344
+ fi
345
+ echo "HALT_CAUSE=lint/typecheck still fails after the fix attempt — the change is not reviewable yet"
346
+ exit 1
347
+
348
+ - id: postcheck
349
+ kind: bash
350
+ depends_on: [quality_recheck]
351
+ output_type: postcheck_report
352
+ timeout: 600000
353
+ bash: |
354
+ # REQ-PIPETRUST-005 — check the agent's work mechanically: verifies
355
+ # bullets are real named test symbols covering every criterion, a src
356
+ # change has a CHANGELOG entry, and project-configured checks pass
357
+ # (their fix commands run first). Exit 0 always: the POSTCHECK marker
358
+ # routes the fix step; postcheck_recheck is what halts.
359
+ if [ -n "$SPECSHIP_CLI_SCRIPT" ]; then ss_cli() { "$SPECSHIP_CLI_NODE" "$SPECSHIP_CLI_SCRIPT" "$@"; }; SS=ss_cli
360
+ elif command -v specship >/dev/null 2>&1; then SS=specship
361
+ elif [ -x node_modules/.bin/specship ]; then SS=node_modules/.bin/specship
362
+ else SS=""
363
+ fi
364
+ if [ -z "$SS" ]; then
365
+ echo "POSTCHECK=failed"
366
+ echo "specship CLI not found — postcheck did NOT run"
367
+ exit 0
368
+ fi
369
+ $SS postcheck $INPUT.SPEC_ID --path . --fix || true
370
+
371
+ - id: fix_postcheck
372
+ kind: prompt
373
+ depends_on: [postcheck]
374
+ output_type: postcheck_fix
375
+ allowed_tools:
376
+ - Read
377
+ - Edit
378
+ - Write
379
+ - Bash
380
+ - mcp__specship__specship_spec
381
+ prompt: |
382
+ Mechanical post-implement check for $INPUT.SPEC_ID:
383
+
384
+ $postcheck.output
385
+
386
+ Route on the POSTCHECK marker — do not re-judge:
387
+
388
+ - POSTCHECK=passed → change NOTHING. Output exactly
389
+ `FIX_POSTCHECK=not-needed` and stop.
390
+ - POSTCHECK=failed → fix every ✗ finding:
391
+ - [verifies] — every acceptance criterion needs a test written as a
392
+ NAMED function passed to it(), with the criterion id in the it()
393
+ title, listed under the requirement's `verifies:` block in its spec
394
+ file as `path:FunctionName` (never an it() title string).
395
+ - [changelog] — add a plain-sentence, user-facing entry under
396
+ `## [Unreleased]` in CHANGELOG.md (no internal paths or symbols).
397
+ - [command] — make the named check pass by fixing the cause.
398
+ A bash step re-runs postcheck after you; any finding left halts the run.
399
+
400
+ Output `FIX_POSTCHECK=fixed` or `FIX_POSTCHECK=still-failing` followed
401
+ by what remains.
402
+
403
+ - id: postcheck_recheck
404
+ kind: bash
405
+ depends_on: [fix_postcheck]
406
+ output_type: postcheck_result
407
+ timeout: 600000
408
+ bash: |
409
+ # REQ-PIPETRUST-005.A5 — the authoritative half: re-run and halt while
410
+ # anything still fails, so an unlinked or undocumented change never
411
+ # reaches verify or the human gate.
412
+ if [ -n "$SPECSHIP_CLI_SCRIPT" ]; then ss_cli() { "$SPECSHIP_CLI_NODE" "$SPECSHIP_CLI_SCRIPT" "$@"; }; SS=ss_cli
413
+ elif command -v specship >/dev/null 2>&1; then SS=specship
414
+ elif [ -x node_modules/.bin/specship ]; then SS=node_modules/.bin/specship
415
+ else SS=""
416
+ fi
417
+ if [ -z "$SS" ]; then
418
+ echo "POSTCHECK_RESULT=failed"
419
+ echo "HALT_CAUSE=specship CLI not found — postcheck did NOT run"
420
+ exit 1
421
+ fi
422
+ if OUT=$($SS postcheck $INPUT.SPEC_ID --path . 2>&1); then
423
+ echo "POSTCHECK_RESULT=clean"
424
+ printf '%s\n' "$OUT"
425
+ exit 0
426
+ fi
427
+ echo "POSTCHECK_RESULT=failed"
428
+ printf '%s\n' "$OUT"
429
+ echo "HALT_CAUSE=postcheck still fails after the fix attempt — the change is not reviewable yet"
430
+ exit 1
431
+
432
+ - id: suppression_gate
433
+ kind: bash
434
+ depends_on: [postcheck_recheck]
435
+ output_type: suppression_report
436
+ bash: |
437
+ # REQ-QGATE-004.A3 — deterministic scan for lint/type suppression
438
+ # comments the change introduces (noqa, eslint-disable, @ts-ignore, …).
439
+ # The fix_quality prompt forbids silencing rules; this node enforces it
440
+ # in code. SUPPRESSION_GATE is the marker contract: a scan that could
441
+ # not run is `skipped`, never `clean`.
442
+ # REQ-PIPETRUST-006.A4 — prefer the CLI running this workflow over any
443
+ # (possibly older) `specship` on PATH.
444
+ if [ -n "$SPECSHIP_CLI_SCRIPT" ]; then ss_cli() { "$SPECSHIP_CLI_NODE" "$SPECSHIP_CLI_SCRIPT" "$@"; }; SS=ss_cli
445
+ elif command -v specship >/dev/null 2>&1; then SS=specship
446
+ elif [ -x node_modules/.bin/specship ]; then SS=node_modules/.bin/specship
447
+ elif [ -x dist/bin/specship.js ]; then SS="node dist/bin/specship.js"
448
+ else SS=""; fi
449
+ if [ -z "$SS" ]; then
450
+ echo "SUPPRESSION_GATE=skipped"
451
+ echo "SUPPRESSION_REASON=specship CLI not found — suppression scan did NOT run"
452
+ exit 0
453
+ fi
454
+ if OUT=$($SS suppressions 2>&1); then
455
+ printf '%s\n' "$OUT"
456
+ else
457
+ printf '%s\n' "$OUT"
458
+ echo "HALT_CAUSE=the change introduces lint/type suppression comments — fix the code instead of silencing the rule"
459
+ exit 1
460
+ fi
461
+
462
+ - id: diff
463
+ kind: bash
464
+ depends_on: [suppression_gate]
465
+ output_type: diff
466
+ bash: |
467
+ # REQ-AUTHG-004.A1 — the full diff, from git, into a run artifact. The
468
+ # human gate and the adversarial reviewer read THIS; the implement node's
469
+ # prose about what it believes it changed is not the change.
470
+ # `git add -A -N` registers new files as intent-to-add so untracked work
471
+ # appears in `git diff HEAD` without staging any content.
472
+ git add -A -N >/dev/null 2>&1 || true
473
+ echo "DIFF_BEGIN"
474
+ git --no-pager diff HEAD | head -c 400000
475
+ echo
476
+ echo "DIFF_END"
477
+
478
+ - id: diff_stat
479
+ kind: bash
480
+ depends_on: [diff]
481
+ output_type: diff_stat
482
+ bash: |
483
+ # REQ-AUTHG-004.A2 — the stat block the final gate shows: file names and
484
+ # insertions/deletions counted by git. Its own small node so the approval
485
+ # message carries the summary while the full diff stays in the `diff`
486
+ # artifact.
487
+ echo "DIFF_STAT_BEGIN"
488
+ git --no-pager diff HEAD --stat
489
+ echo "DIFF_STAT_END"
490
+
491
+ - id: verify
492
+ kind: bash
493
+ depends_on: [diff_stat]
106
494
  output_type: test_results
107
495
  timeout: 600000
108
496
  bash: |
@@ -111,43 +499,110 @@ nodes:
111
499
  # The VERIFY_RESULT marker is the machine-readable contract the link
112
500
  # node routes on (REQ-IMPLINT-001.A1): a skipped run must never be
113
501
  # mistaken for a passing one.
502
+ # Install and build failures fail fast with their own markers
503
+ # (REQ-REVINT-006.A1) — a broken environment or a build break is NOT a
504
+ # test failure, and must never be attributed to one.
114
505
  if [ -f package.json ]; then
115
506
  # A fresh worktree has no node_modules/ or build output — a verify
116
507
  # failure must mean the tests failed, not that the environment was
117
508
  # missing (WF-REJECT-DOC, REQ-WFREJ-005).
118
- if [ ! -d node_modules ]; then npm ci --no-audit --no-fund || npm install --no-audit --no-fund; fi
119
- npm run build --if-present
120
- if npm test; then echo "VERIFY_RESULT=ran-and-passed"; else echo "VERIFY_RESULT=ran-and-failed"; exit 1; fi
509
+ if [ ! -d node_modules ]; then
510
+ if ! { npm ci --no-audit --no-fund || npm install --no-audit --no-fund; }; then
511
+ echo "VERIFY_RESULT=env-failed"; exit 1
512
+ fi
513
+ fi
514
+ if ! npm run build --if-present; then echo "VERIFY_RESULT=build-failed"; exit 1; fi
515
+ # REQ-PIPETRUST-006.A3 — a vitest project also leaves a JSON report in
516
+ # the run's artifacts, ingestible with `specship verify --report`.
517
+ TEST_ARGS=""
518
+ if [ -n "$SPECSHIP_ARTIFACTS_DIR" ] && node -e 'const s=((require("./package.json").scripts)||{}).test||"";process.exit(/^\s*vitest\b/.test(s)?0:1)' 2>/dev/null; then
519
+ mkdir -p "$SPECSHIP_ARTIFACTS_DIR"
520
+ REPORT="$SPECSHIP_ARTIFACTS_DIR/test-report.json"
521
+ TEST_ARGS="-- --reporter=default --reporter=json --outputFile.json=$REPORT"
522
+ echo "VERIFY_REPORT=$REPORT (ingest after merge: specship verify --report $REPORT)"
523
+ fi
524
+ if npm test $TEST_ARGS; then echo "VERIFY_RESULT=ran-and-passed"; else echo "VERIFY_RESULT=ran-and-failed"; exit 1; fi
121
525
  elif [ -f Cargo.toml ]; then
526
+ if ! cargo build; then echo "VERIFY_RESULT=build-failed"; exit 1; fi
122
527
  if cargo test; then echo "VERIFY_RESULT=ran-and-passed"; else echo "VERIFY_RESULT=ran-and-failed"; exit 1; fi
123
528
  elif [ -f pyproject.toml ] || [ -f requirements.txt ]; then
529
+ if ! python -c 'import pytest' 2>/dev/null; then echo "VERIFY_RESULT=env-failed"; exit 1; fi
124
530
  if python -m pytest; then echo "VERIFY_RESULT=ran-and-passed"; else echo "VERIFY_RESULT=ran-and-failed"; exit 1; fi
125
531
  else
126
532
  echo "no recognised test framework; tests did NOT run"
127
533
  echo "VERIFY_RESULT=skipped"
128
534
  fi
129
535
 
536
+ - id: route_verify
537
+ kind: bash
538
+ depends_on: [verify]
539
+ output_type: verify_route
540
+ bash: |
541
+ # Deterministic verify routing (REQ-VSTATE-005.A1): the VERIFY_RESULT
542
+ # marker is parsed HERE, in code, and turned into a LINK_ACTION the link
543
+ # node obeys. It used to be a prompt reading a test log — the one decision
544
+ # that governs whether links may be promoted was hallucinable.
545
+ VERIFY_OUT=$verify.output
546
+ MARKER=$(printf '%s' "$VERIFY_OUT" | grep -oE 'VERIFY_RESULT=[a-z-]+' | tail -n 1 | cut -d= -f2)
547
+ [ -n "$MARKER" ] || MARKER=missing
548
+ echo "VERIFY_RESULT=$MARKER"
549
+ # env-failed / build-failed halt BEFORE any link mutation, naming the
550
+ # environment cause (REQ-VSTATE-005.A3) — neither is a test failure.
551
+ case "$MARKER" in
552
+ ran-and-passed)
553
+ echo "LINK_ACTION=assert-and-verify-pass" ;;
554
+ ran-and-failed)
555
+ echo "LINK_ACTION=assert-and-verify-fail" ;;
556
+ skipped)
557
+ echo "LINK_ACTION=assert-only" ;;
558
+ env-failed)
559
+ echo "HALT_CAUSE=environment setup failed (dependency install) — nothing is known about the tests"
560
+ exit 1 ;;
561
+ build-failed)
562
+ echo "HALT_CAUSE=build failed — nothing is known about the tests"
563
+ exit 1 ;;
564
+ *)
565
+ echo "HALT_CAUSE=no VERIFY_RESULT marker found in the verify output — an unknown result is never a pass"
566
+ exit 1 ;;
567
+ esac
568
+
130
569
  - id: link
131
570
  kind: prompt
132
- depends_on: [verify]
571
+ depends_on: [route_verify]
133
572
  output_type: link_summary
134
573
  allowed_tools:
135
- - mcp__specship__specship_link_assert
136
- - mcp__specship__specship_link_verify
574
+ - Read
575
+ - Edit
137
576
  - mcp__specship__specship_spec
138
577
  prompt: |
139
- Assert spec→code links for the changes made in the implement step.
140
- For each symbol that implements $INPUT.SPEC_ID (or its child
141
- requirements), call specship_link_assert with the spec_id, the
142
- file path, and the qualified symbol name.
143
-
144
- Then route on the VERIFY_RESULT marker in the test results below
145
- (REQ-IMPLINT-001):
146
- - VERIFY_RESULT=ran-and-passed → after each assert, call
147
- specship_link_verify with result="pass".
148
- - VERIFY_RESULT=skipped → do NOT call specship_link_verify at all;
149
- the links stay in the `implemented` state. Tests that did not run
150
- are never reported as passing.
578
+ Record spec→code links for the changes made in the implement step —
579
+ durably, in the spec file (REQ-PIPETRUST-006). Do NOT call
580
+ specship_link_assert or specship_link_verify: runtime links vanish on a
581
+ full reindex, and a headless run cannot be granted them.
582
+
583
+ Open the spec file that declares $INPUT.SPEC_ID. Under the requirement:
584
+ - `implementations:` — one `path:Symbol` bullet per symbol that
585
+ implements it (qualified `Class.method` for methods). Add any that
586
+ are missing; never duplicate an existing bullet.
587
+ - `verifies:` — leave as the postcheck step left it (named test
588
+ functions only). Never add an it() title string.
589
+
590
+ You MUST NOT assert a `tests`-kind link (REQ-REVINT-005.A1) —
591
+ never pass kind: 'tests', and never mint test evidence: it comes only
592
+ from the spec's `verifies:` block or an `@verifies` comment on a test
593
+ symbol.
594
+
595
+ Verification state is not yours to set, so you
596
+ do NOT call specship_link_verify, whatever LINK_ACTION says. On
597
+ LINK_ACTION=assert-and-verify-pass, LINK_ACTION=assert-and-verify-fail
598
+ or LINK_ACTION=assert-only alike, record the implementations bullets
599
+ only: tests that ran are in the run's JSON test report (see the verify
600
+ output) and are ingested with `specship verify --report` after the
601
+ branch merges; tests that did not run are never reported as passing.
602
+
603
+ Routing context (computed by a bash node from VERIFY_RESULT):
604
+
605
+ $route_verify.output
151
606
 
152
607
  Implementation diff summary:
153
608
  $implement.output
@@ -155,39 +610,244 @@ nodes:
155
610
  Test results:
156
611
  $verify.output
157
612
 
158
- Output: a short list of "spec_id → file:symbol [verified|implemented]"
159
- lines, ending with one line stating whether verification ran or was
160
- skipped.
613
+ Output: a short list of "spec_id → file:symbol" lines you recorded,
614
+ ending with the VERIFY_REPORT line from the verify output if present.
161
615
 
162
616
  - id: coverage
163
- kind: prompt
617
+ kind: bash
164
618
  depends_on: [link]
165
619
  output_type: coverage_summary
620
+ bash: |
621
+ # The acceptance-criteria coverage line is computed FROM THE DB
622
+ # (REQ-VSTATE-005.A2) — `specship coverage --line` renders the same
623
+ # `getSpecCoverage` report the MCP tool and the dashboard read, so the
624
+ # number on the approval gate cannot disagree with them. It used to be
625
+ # counted by a prompt.
626
+ # REQ-PIPETRUST-006.A4 — prefer the CLI running this workflow over any
627
+ # (possibly older) `specship` on PATH.
628
+ if [ -n "$SPECSHIP_CLI_SCRIPT" ]; then ss_cli() { "$SPECSHIP_CLI_NODE" "$SPECSHIP_CLI_SCRIPT" "$@"; }; SS=ss_cli
629
+ elif command -v specship >/dev/null 2>&1; then SS=specship
630
+ elif [ -x node_modules/.bin/specship ]; then SS=node_modules/.bin/specship
631
+ elif [ -x dist/bin/specship.js ]; then SS="node dist/bin/specship.js"
632
+ else SS=""; fi
633
+ if [ -z "$SS" ]; then
634
+ echo "COVERAGE_UNAVAILABLE=specship CLI not found — acceptance-criteria coverage was NOT computed"
635
+ exit 0
636
+ fi
637
+ LINE=$($SS coverage $INPUT.SPEC_ID --line) || {
638
+ echo "COVERAGE_UNAVAILABLE=specship coverage failed — coverage was NOT computed"
639
+ exit 0
640
+ }
641
+ echo "$LINE"
642
+ N=$(printf '%s' "$LINE" | awk '{print $1}')
643
+ M=$(printf '%s' "$LINE" | awk '{print $3}')
644
+ if [ "$N" != "$M" ]; then
645
+ echo "Close the gap with /specship:spec behaviour $INPUT.SPEC_ID."
646
+ fi
647
+
648
+ - id: adversarial_review
649
+ kind: prompt
650
+ depends_on: [coverage]
651
+ output_type: adversarial_review
652
+ allowed_tools:
653
+ - Read
654
+ - Grep
655
+ - mcp__specship__specship_spec
656
+ - mcp__specship__specship_explore
657
+ - mcp__specship__specship_node
658
+ - mcp__specship__specship_search
659
+ prompt: |
660
+ Adversarial review (REQ-AUTHG-005.A3). You are NOT the implementer — your
661
+ job is to find what the implementation missed. This node is read-only:
662
+ you have no Edit, Write or Bash tool and must not try to fix anything.
663
+
664
+ Call specship_spec($INPUT.SPEC_ID) and read the spec body, including
665
+ every acceptance criterion.
666
+
667
+ The change under review (from git, not from the implementer's summary):
668
+
669
+ $diff.output
670
+
671
+ Report this and nothing else:
672
+
673
+ 1. One line per acceptance criterion:
674
+ `<AC id>: satisfied | partial | unaddressed — <one sentence, grounded
675
+ in the diff>`. Judge from the diff and the code you read; "the
676
+ implementer said so" is not evidence.
677
+ 2. `Untested new code paths:` the branches/functions this diff adds that
678
+ no test in the diff exercises (`none` if there are none).
679
+ 3. `Verdict:` one line — the most serious gap a reviewer should look at
680
+ first, or `no gaps found`.
681
+
682
+ - id: probe_baseline
683
+ kind: bash
684
+ depends_on: [adversarial_review]
685
+ output_type: probe_baseline
686
+ bash: |
687
+ # REQ-THRU-008 — fingerprint every tracked change before the probe step,
688
+ # so the guard can prove the probes touched nothing tracked.
689
+ git add -A -N >/dev/null 2>&1 || true
690
+ git --no-pager diff HEAD | git hash-object --stdin > "$(git rev-parse --git-dir)/specship-probe-baseline"
691
+ echo "PROBE_BASELINE=recorded"
692
+
693
+ - id: review_probes
694
+ kind: prompt
695
+ depends_on: [probe_baseline]
696
+ output_type: review_probes
166
697
  allowed_tools:
698
+ - Read
699
+ - Write
700
+ - Bash
167
701
  - mcp__specship__specship_spec
168
702
  prompt: |
169
- Report acceptance-criteria test coverage for $INPUT.SPEC_ID
170
- (REQ-IMPLINT-002). Call specship_spec with the spec_id, count its
171
- acceptance criteria (the .A<N> children; if the spec has none,
172
- treat the requirement itself as one criterion), and count how many
173
- of them carry a `tests`-kind link.
703
+ Try to BREAK the change for $INPUT.SPEC_ID (REQ-THRU-008). You are not
704
+ the implementer. For EACH acceptance criterion write at least one small
705
+ probe test that would fail if the criterion were not really met — an
706
+ edge case, a boundary, a negative case — ONLY under
707
+ `.specship/review-probes/` (create it). NEVER edit any tracked file: a
708
+ guard step fails the run if you do.
709
+
710
+ Run each probe with the project's test runner. Report one line per
711
+ criterion: `<AC id>: held | broke | unrun — <probe file> — <one
712
+ sentence>`. `unrun` means the probe could not be executed (say why);
713
+ never report a probe you did not run as held.
714
+
715
+ The read-only review already found:
716
+ $adversarial_review.output
717
+
718
+ The change (from git):
719
+ $diff.output
720
+
721
+ - id: review_guard
722
+ kind: bash
723
+ depends_on: [review_probes]
724
+ output_type: review_guard
725
+ bash: |
726
+ # REQ-THRU-008.A2 — the probe step may not change tracked files.
727
+ git add -A -N >/dev/null 2>&1 || true
728
+ NOW=$(git --no-pager diff HEAD | git hash-object --stdin)
729
+ BEFORE=$(cat "$(git rev-parse --git-dir)/specship-probe-baseline" 2>/dev/null)
730
+ if [ -n "$BEFORE" ] && [ "$NOW" != "$BEFORE" ]; then
731
+ echo "REVIEW_GUARD=failed"
732
+ echo "HALT_CAUSE=the probe step changed tracked files — probes belong under .specship/review-probes/"
733
+ git --no-pager diff --stat HEAD
734
+ exit 1
735
+ fi
736
+ echo "REVIEW_GUARD=clean"
737
+
738
+ - id: knowledge_proposals
739
+ kind: prompt
740
+ depends_on: [review_guard]
741
+ output_type: knowledge_proposals
742
+ allowed_tools:
743
+ - Read
744
+ - Write
745
+ - mcp__specship__specship_spec
746
+ - mcp__specship__specship_explore
747
+ prompt: |
748
+ Propose knowledge this change relied on, for a HUMAN to confirm
749
+ (REQ-KNOW-004). Domain knowledge is human-confirmed, never
750
+ auto-extracted (DOM-SPECSHIP-003): you only PROPOSE.
174
751
 
175
- Output exactly one or two lines:
176
- - "N of M acceptance criteria for $INPUT.SPEC_ID have linked tests."
177
- - If N < M, add: "Close the gap with /specship:spec behaviour
178
- $INPUT.SPEC_ID."
752
+ Write 0–5 proposal files, one fact each, ONLY under
753
+ `.specship/knowledge/proposals/` (create the directory). NEVER edit
754
+ `specs/domain/` or any tracked file. Each file is a domain fact:
755
+
756
+ ---
757
+ title: <short name>
758
+ type: term | rule | decision | constraint | external
759
+ area: <AREA, e.g. the prefix used by existing DOM-<AREA>-NNN facts>
760
+ depends_on: $INPUT.SPEC_ID
761
+ # external facts ONLY — verified from the installed dependency, not memory:
762
+ dependency: <package or tool name>
763
+ version: <installed version you verified against>
764
+ source: <file and symbol in that dependency>
765
+ ---
766
+
767
+ # <title>
768
+
769
+ <one paragraph: the fact, stated plainly>
770
+
771
+ ## Enforced (optional — only for a mechanically checkable rule)
772
+ - `<glob>` must not match `/<regex>/`
773
+
774
+ Propose only what this change actually depended on and a future change
775
+ would otherwise rediscover — especially facts about third-party tools.
776
+ If there is nothing worth keeping, write no files.
777
+
778
+ Implementation diff summary:
779
+ $implement.output
780
+
781
+ Output one line per proposal file you wrote, or `NO_PROPOSALS`.
782
+
783
+ - id: knowledge_publish
784
+ kind: bash
785
+ depends_on: [knowledge_proposals]
786
+ output_type: knowledge_report
787
+ bash: |
788
+ # REQ-KNOW-004.A1 — the proposal step may write ONLY proposals. Any
789
+ # change under specs/domain/ halts the run. Proposals are then copied to
790
+ # the main project's .specship/knowledge/proposals/ (a run worktree's
791
+ # .specship/ is gitignored and would vanish), where
792
+ # `specship knowledge proposals` / `apply` / `reject` find them.
793
+ if [ -n "$(git status --porcelain -- specs/domain 2>/dev/null)" ]; then
794
+ echo "KNOWLEDGE_PROPOSALS=refused"
795
+ echo "HALT_CAUSE=the knowledge step changed specs/domain/ — only a human applies knowledge"
796
+ git status --porcelain -- specs/domain
797
+ exit 1
798
+ fi
799
+ SRC=".specship/knowledge/proposals"
800
+ N=0
801
+ if [ -d "$SRC" ]; then N=$(ls "$SRC"/*.md 2>/dev/null | wc -l | tr -d ' '); fi
802
+ echo "KNOWLEDGE_PROPOSALS=$N"
803
+ if [ "$N" -gt 0 ] && [ -n "$SPECSHIP_ARTIFACTS_DIR" ]; then
804
+ DEST="$(cd "$SPECSHIP_ARTIFACTS_DIR/../../.." 2>/dev/null && pwd)/knowledge/proposals"
805
+ mkdir -p "$DEST"
806
+ for f in "$SRC"/*.md; do
807
+ base=$(basename "$f")
808
+ target="$DEST/$base"
809
+ i=2
810
+ while [ -e "$target" ]; do target="$DEST/${base%.md}-$i.md"; i=$((i+1)); done
811
+ cp "$f" "$target"
812
+ echo "- $(basename "$target") ($(sed -n 's/^title: //p' "$f" | head -1))"
813
+ done
814
+ echo "Review with: specship knowledge proposals · apply <id> · reject <id>"
815
+ fi
179
816
 
180
817
  - id: final_review
181
818
  kind: approval
182
- depends_on: [coverage]
819
+ depends_on: [adversarial_review, knowledge_publish]
183
820
  message: |
184
821
  Implementation complete for $INPUT.SPEC_ID.
185
822
 
186
- Links asserted:
823
+ Changes (git diff --stat — measured, not summarized):
824
+ $diff_stat.output
825
+
826
+ Lint / typecheck:
827
+ $quality_recheck.output
828
+
829
+ Mechanical postcheck (verifies symbols, CHANGELOG, project checks):
830
+ $postcheck_recheck.output
831
+
832
+ Suppression scan (new noqa / eslint-disable / @ts-ignore):
833
+ $suppression_gate.output
834
+
835
+ Links recorded in the spec (ingest the test report after merge with the
836
+ `specship verify --report` command named here):
187
837
  $link.output
188
838
 
189
839
  Acceptance-criteria test coverage:
190
840
  $coverage.output
191
841
 
842
+ Adversarial review:
843
+ $adversarial_review.output
844
+
845
+ Review probes (held | broke | unrun per criterion):
846
+ $review_probes.output
847
+
848
+ Knowledge proposals (nothing is applied until you run
849
+ `specship knowledge apply <id>`):
850
+ $knowledge_publish.output
851
+
192
852
  Approve to finalize the run (worktree is left for you to inspect
193
853
  and merge), or reject to abandon.