@opengsd/gsd-core 1.11.0 → 1.12.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 (395) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/agents/gsd-code-fixer.md +1 -1
  4. package/agents/gsd-debug-session-manager.md +1 -1
  5. package/agents/gsd-debugger.md +1 -1
  6. package/agents/gsd-dom-verifier.md +169 -0
  7. package/agents/gsd-eval-auditor.md +1 -1
  8. package/agents/gsd-executor.md +17 -9
  9. package/agents/gsd-framework-selector.md +1 -3
  10. package/agents/gsd-intel-updater.md +1 -1
  11. package/agents/gsd-mempalace-curator.md +0 -1
  12. package/agents/gsd-pattern-mapper.md +11 -0
  13. package/agents/gsd-phase-researcher.md +3 -1
  14. package/agents/gsd-plan-checker.md +15 -55
  15. package/agents/gsd-planner.md +6 -4
  16. package/agents/gsd-project-researcher.md +1 -1
  17. package/agents/gsd-research-synthesizer.md +2 -2
  18. package/agents/gsd-roadmapper.md +15 -11
  19. package/agents/gsd-ui-checker.md +63 -4
  20. package/agents/gsd-ui-researcher.md +41 -3
  21. package/agents/gsd-verifier.md +1 -1
  22. package/bin/install.js +609 -134
  23. package/commands/gsd/discuss-phase.md +1 -1
  24. package/commands/gsd/import.md +1 -1
  25. package/commands/gsd/quick.md +8 -4
  26. package/gsd-core/bin/gsd-tools.cjs +567 -51
  27. package/gsd-core/bin/lib/active-workstream-store.cjs +8 -0
  28. package/gsd-core/bin/lib/adr-parser.cjs +13 -7
  29. package/gsd-core/bin/lib/agent-install-check.cjs +162 -0
  30. package/gsd-core/bin/lib/api-coverage.cjs +30 -9
  31. package/gsd-core/bin/lib/artifacts.cjs +2 -0
  32. package/gsd-core/bin/lib/assumption-delta.cjs +30 -11
  33. package/gsd-core/bin/lib/audit.cjs +163 -41
  34. package/gsd-core/bin/lib/broken-windows.cjs +306 -28
  35. package/gsd-core/bin/lib/capability-lock.cjs +10 -4
  36. package/gsd-core/bin/lib/capability-registry.cjs +336 -95
  37. package/gsd-core/bin/lib/capability-state.cjs +18 -3
  38. package/gsd-core/bin/lib/capability-validator.cjs +205 -18
  39. package/gsd-core/bin/lib/check-command-router.cjs +145 -5
  40. package/gsd-core/bin/lib/cli-exit.cjs +496 -10
  41. package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
  42. package/gsd-core/bin/lib/codex-agent-toml.cjs +410 -4
  43. package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
  44. package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
  45. package/gsd-core/bin/lib/commands.cjs +543 -44
  46. package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
  47. package/gsd-core/bin/lib/config-loader.cjs +118 -29
  48. package/gsd-core/bin/lib/config.cjs +92 -2
  49. package/gsd-core/bin/lib/configuration.cjs +129 -37
  50. package/gsd-core/bin/lib/core-utils.cjs +84 -7
  51. package/gsd-core/bin/lib/edge-probe.cjs +9 -1
  52. package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
  53. package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
  54. package/gsd-core/bin/lib/frontmatter.cjs +840 -305
  55. package/gsd-core/bin/lib/gap-checker.cjs +27 -3
  56. package/gsd-core/bin/lib/git-base-branch.cjs +174 -39
  57. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
  58. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +6 -3
  59. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +22 -8
  60. package/gsd-core/bin/lib/health-diagnostic.cjs +23 -3
  61. package/gsd-core/bin/lib/host-integration.cjs +39 -6
  62. package/gsd-core/bin/lib/init-command-router.cjs +118 -21
  63. package/gsd-core/bin/lib/init.cjs +120 -41
  64. package/gsd-core/bin/lib/install-engine.cjs +68 -3
  65. package/gsd-core/bin/lib/install-model-override-resolver.cjs +33 -1
  66. package/gsd-core/bin/lib/install-profiles.cjs +78 -4
  67. package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
  68. package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
  69. package/gsd-core/bin/lib/installer-migrations.cjs +10 -7
  70. package/gsd-core/bin/lib/intel.cjs +101 -26
  71. package/gsd-core/bin/lib/io.cjs +160 -15
  72. package/gsd-core/bin/lib/learnings.cjs +85 -14
  73. package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
  74. package/gsd-core/bin/lib/markdown-table.cjs +52 -4
  75. package/gsd-core/bin/lib/milestone.cjs +90 -5
  76. package/gsd-core/bin/lib/model-catalog.cjs +177 -19
  77. package/gsd-core/bin/lib/model-resolver.cjs +10 -28
  78. package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
  79. package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
  80. package/gsd-core/bin/lib/phase-id.cjs +70 -4
  81. package/gsd-core/bin/lib/phase-lifecycle.cjs +24 -16
  82. package/gsd-core/bin/lib/phase-locator.cjs +138 -17
  83. package/gsd-core/bin/lib/phase.cjs +405 -84
  84. package/gsd-core/bin/lib/plan-document.cjs +263 -0
  85. package/gsd-core/bin/lib/plan-scan.cjs +13 -2
  86. package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
  87. package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
  88. package/gsd-core/bin/lib/planning-snapshot.cjs +18 -14
  89. package/gsd-core/bin/lib/planning-workspace.cjs +56 -0
  90. package/gsd-core/bin/lib/probe-core.cjs +4 -1
  91. package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
  92. package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
  93. package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
  94. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +71 -45
  95. package/gsd-core/bin/lib/review-lane-descriptor.cjs +9 -9
  96. package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
  97. package/gsd-core/bin/lib/roadmap-parser.cjs +79 -16
  98. package/gsd-core/bin/lib/roadmap.cjs +74 -19
  99. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +96 -8
  100. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +34 -1
  101. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +287 -55
  102. package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
  103. package/gsd-core/bin/lib/runtime-slash.cjs +72 -2
  104. package/gsd-core/bin/lib/shell-command-projection.cjs +71 -8
  105. package/gsd-core/bin/lib/smart-entry.cjs +12 -22
  106. package/gsd-core/bin/lib/spec-section.cjs +12 -7
  107. package/gsd-core/bin/lib/state-command-router.cjs +47 -18
  108. package/gsd-core/bin/lib/state-contract.cjs +359 -0
  109. package/gsd-core/bin/lib/state-document.cjs +186 -0
  110. package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
  111. package/gsd-core/bin/lib/state-transition.cjs +517 -101
  112. package/gsd-core/bin/lib/state.cjs +946 -163
  113. package/gsd-core/bin/lib/surface.cjs +10 -2
  114. package/gsd-core/bin/lib/task-command-router.cjs +111 -1
  115. package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
  116. package/gsd-core/bin/lib/teams-status.cjs +4 -1
  117. package/gsd-core/bin/lib/uat-predicate.cjs +58 -20
  118. package/gsd-core/bin/lib/uat.cjs +1376 -125
  119. package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
  120. package/gsd-core/bin/lib/ui-safety-gate.cjs +37 -7
  121. package/gsd-core/bin/lib/unusable-input.cjs +13 -0
  122. package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
  123. package/gsd-core/bin/lib/vendor/README.md +43 -5
  124. package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
  125. package/gsd-core/bin/lib/verification.cjs +14 -1
  126. package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
  127. package/gsd-core/bin/lib/verify.cjs +95 -40
  128. package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
  129. package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
  130. package/gsd-core/bin/lib/worktree-safety.cjs +177 -21
  131. package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
  132. package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
  133. package/gsd-core/bin/shared/exit-codes.json +8 -0
  134. package/gsd-core/bin/shared/exit-codes.sh +20 -0
  135. package/gsd-core/bin/shared/model-catalog.json +8 -1
  136. package/gsd-core/references/agent-contracts.md +3 -2
  137. package/gsd-core/references/api-coverage.md +24 -2
  138. package/gsd-core/references/autonomous-smart-discuss.md +3 -3
  139. package/gsd-core/references/checkpoints.md +37 -19
  140. package/gsd-core/references/decimal-phase-calculation.md +5 -5
  141. package/gsd-core/references/edge-probe.md +8 -0
  142. package/gsd-core/references/execute-mvp-tdd.md +1 -3
  143. package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
  144. package/gsd-core/references/execute-phase-wave-guard.md +11 -9
  145. package/gsd-core/references/failing-direction.md +78 -0
  146. package/gsd-core/references/gate-prompts.md +1 -1
  147. package/gsd-core/references/git-integration.md +5 -5
  148. package/gsd-core/references/git-planning-commit.md +3 -3
  149. package/gsd-core/references/gsd-run-resolver.md +1 -1
  150. package/gsd-core/references/loop-hook-dispatch.md +22 -0
  151. package/gsd-core/references/model-profiles.md +1 -1
  152. package/gsd-core/references/nyquist-compliance.md +74 -0
  153. package/gsd-core/references/offer-next.md +3 -5
  154. package/gsd-core/references/phase-argument-parsing.md +3 -3
  155. package/gsd-core/references/planner-failing-direction.md +53 -0
  156. package/gsd-core/references/planner-human-verify-mode.md +15 -1
  157. package/gsd-core/references/planner-revision.md +1 -1
  158. package/gsd-core/references/planner-verify-command-grounding.md +17 -0
  159. package/gsd-core/references/planning-config.md +37 -8
  160. package/gsd-core/references/reviewer-instances.md +31 -0
  161. package/gsd-core/references/runtime-aware-dispatch.md +1 -1
  162. package/gsd-core/references/tdd.md +1 -3
  163. package/gsd-core/references/ui-brand.md +65 -21
  164. package/gsd-core/references/ui-consideration-probe.md +1 -1
  165. package/gsd-core/references/universal-anti-patterns.md +2 -2
  166. package/gsd-core/references/verify-command-path-resolvability.md +42 -0
  167. package/gsd-core/references/verify-mvp-mode.md +1 -1
  168. package/gsd-core/references/workstream-flag.md +11 -11
  169. package/gsd-core/templates/README.md +1 -1
  170. package/gsd-core/templates/SECURITY.md +3 -3
  171. package/gsd-core/templates/UI-SPEC.md +25 -3
  172. package/gsd-core/templates/VALIDATION.md +3 -3
  173. package/gsd-core/templates/phase-prompt.md +3 -0
  174. package/gsd-core/templates/state.md +7 -0
  175. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  176. package/gsd-core/workflows/add-backlog.md +1 -1
  177. package/gsd-core/workflows/add-phase.md +3 -3
  178. package/gsd-core/workflows/add-tests.md +3 -8
  179. package/gsd-core/workflows/add-todo.md +1 -1
  180. package/gsd-core/workflows/ai-integration-phase.md +4 -9
  181. package/gsd-core/workflows/audit-fix.md +12 -3
  182. package/gsd-core/workflows/audit-milestone.md +9 -9
  183. package/gsd-core/workflows/audit-uat.md +17 -2
  184. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
  185. package/gsd-core/workflows/autonomous.md +10 -26
  186. package/gsd-core/workflows/check-todos.md +1 -1
  187. package/gsd-core/workflows/cleanup.md +2 -2
  188. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +1 -1
  189. package/gsd-core/workflows/code-review-fix.md +1 -1
  190. package/gsd-core/workflows/code-review.md +121 -40
  191. package/gsd-core/workflows/complete-milestone.md +15 -10
  192. package/gsd-core/workflows/debug.md +5 -3
  193. package/gsd-core/workflows/diagnose-issues.md +12 -6
  194. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  195. package/gsd-core/workflows/discuss-phase/modes/chain.md +3 -7
  196. package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
  197. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
  198. package/gsd-core/workflows/discuss-phase-assumptions.md +2 -2
  199. package/gsd-core/workflows/discuss-phase.md +1 -1
  200. package/gsd-core/workflows/do.md +3 -6
  201. package/gsd-core/workflows/docs-update.md +5 -4
  202. package/gsd-core/workflows/edit-phase.md +1 -1
  203. package/gsd-core/workflows/eval-review.md +4 -9
  204. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
  205. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +113 -11
  206. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
  207. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
  208. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  209. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +22 -4
  210. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
  211. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
  212. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
  213. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
  214. package/gsd-core/workflows/execute-phase.md +38 -54
  215. package/gsd-core/workflows/execute-plan.md +17 -12
  216. package/gsd-core/workflows/explore.md +1 -1
  217. package/gsd-core/workflows/extract-learnings.md +1 -1
  218. package/gsd-core/workflows/fast.md +2 -2
  219. package/gsd-core/workflows/forensics.md +1 -1
  220. package/gsd-core/workflows/graduation.md +5 -5
  221. package/gsd-core/workflows/health.md +3 -6
  222. package/gsd-core/workflows/import.md +14 -11
  223. package/gsd-core/workflows/inbox.md +4 -5
  224. package/gsd-core/workflows/ingest-docs.md +44 -11
  225. package/gsd-core/workflows/insert-phase.md +5 -5
  226. package/gsd-core/workflows/list-seeds.md +5 -3
  227. package/gsd-core/workflows/list-workspaces.md +1 -1
  228. package/gsd-core/workflows/manager.md +12 -23
  229. package/gsd-core/workflows/map-codebase.md +1 -1
  230. package/gsd-core/workflows/milestone-summary.md +1 -1
  231. package/gsd-core/workflows/mvp-phase.md +2 -2
  232. package/gsd-core/workflows/new-milestone.md +9 -21
  233. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  234. package/gsd-core/workflows/new-project.md +12 -26
  235. package/gsd-core/workflows/new-workspace.md +1 -1
  236. package/gsd-core/workflows/next.md +2 -2
  237. package/gsd-core/workflows/pause-work.md +1 -1
  238. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
  239. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
  240. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
  241. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
  242. package/gsd-core/workflows/plan-phase.md +121 -42
  243. package/gsd-core/workflows/plan-review-convergence.md +46 -9
  244. package/gsd-core/workflows/plant-seed.md +2 -2
  245. package/gsd-core/workflows/pr-branch.md +187 -51
  246. package/gsd-core/workflows/profile-user.md +16 -14
  247. package/gsd-core/workflows/progress.md +27 -12
  248. package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
  249. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +1 -3
  250. package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
  251. package/gsd-core/workflows/quick/steps/research-phase.md +2 -4
  252. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
  253. package/gsd-core/workflows/quick.md +20 -29
  254. package/gsd-core/workflows/remove-phase.md +4 -4
  255. package/gsd-core/workflows/remove-workspace.md +2 -2
  256. package/gsd-core/workflows/resume-project.md +8 -12
  257. package/gsd-core/workflows/review.md +193 -15
  258. package/gsd-core/workflows/scan.md +1 -1
  259. package/gsd-core/workflows/secure-phase.md +2 -2
  260. package/gsd-core/workflows/settings-advanced.md +7 -9
  261. package/gsd-core/workflows/settings-integrations.md +64 -31
  262. package/gsd-core/workflows/settings.md +3 -5
  263. package/gsd-core/workflows/ship.md +12 -6
  264. package/gsd-core/workflows/sketch-wrap-up.md +11 -17
  265. package/gsd-core/workflows/sketch.md +12 -18
  266. package/gsd-core/workflows/smart-entry.md +3 -5
  267. package/gsd-core/workflows/spec-phase.md +23 -1
  268. package/gsd-core/workflows/spike-wrap-up.md +7 -11
  269. package/gsd-core/workflows/spike.md +20 -31
  270. package/gsd-core/workflows/stats.md +2 -2
  271. package/gsd-core/workflows/sync-skills.md +1 -1
  272. package/gsd-core/workflows/thread.md +11 -7
  273. package/gsd-core/workflows/transition.md +5 -5
  274. package/gsd-core/workflows/ui-phase.md +10 -16
  275. package/gsd-core/workflows/ui-review.md +6 -10
  276. package/gsd-core/workflows/ultraplan-phase.md +5 -13
  277. package/gsd-core/workflows/undo.md +8 -16
  278. package/gsd-core/workflows/update.md +6 -10
  279. package/gsd-core/workflows/validate-phase.md +2 -2
  280. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
  281. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
  282. package/gsd-core/workflows/verify-work.md +57 -18
  283. package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
  284. package/hooks/dist/gsd-config-reload.js +18 -12
  285. package/hooks/dist/gsd-context-monitor.js +19 -10
  286. package/hooks/dist/gsd-cursor-post-tool.js +3 -1
  287. package/hooks/dist/gsd-cursor-pre-tool.js +3 -1
  288. package/hooks/dist/gsd-cursor-session-start.js +2 -1
  289. package/hooks/dist/gsd-cursor-stop.js +2 -1
  290. package/hooks/dist/gsd-cursor-subagent-start.js +28 -23
  291. package/hooks/dist/gsd-cursor-subagent-stop.js +3 -1
  292. package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
  293. package/hooks/dist/gsd-graphify-update.sh +22 -18
  294. package/hooks/dist/gsd-node-runner.sh +76 -0
  295. package/hooks/dist/gsd-phase-boundary.sh +1 -0
  296. package/hooks/dist/gsd-prompt-guard.js +16 -7
  297. package/hooks/dist/gsd-read-guard.js +16 -7
  298. package/hooks/dist/gsd-read-injection-scanner.js +17 -8
  299. package/hooks/dist/gsd-session-state.sh +1 -0
  300. package/hooks/dist/gsd-statusline.js +215 -26
  301. package/hooks/dist/gsd-validate-commit.sh +80 -6
  302. package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
  303. package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
  304. package/hooks/dist/gsd-workflow-guard.js +34 -16
  305. package/hooks/dist/gsd-worktree-path-guard.js +36 -21
  306. package/hooks/dist/gsd-write-guard.js +35 -25
  307. package/hooks/dist/lib/cli-exit.js +560 -0
  308. package/hooks/dist/lib/exit-code-registry.js +98 -0
  309. package/hooks/dist/lib/git-probe.js +84 -0
  310. package/hooks/dist/lib/hook-exit.js +81 -0
  311. package/hooks/dist/managed-hooks-registry.cjs +3 -0
  312. package/hooks/gsd-agent-isolation-guard.js +77 -38
  313. package/hooks/gsd-config-reload.js +18 -12
  314. package/hooks/gsd-context-monitor.js +19 -10
  315. package/hooks/gsd-cursor-post-tool.js +3 -1
  316. package/hooks/gsd-cursor-pre-tool.js +3 -1
  317. package/hooks/gsd-cursor-session-start.js +2 -1
  318. package/hooks/gsd-cursor-stop.js +2 -1
  319. package/hooks/gsd-cursor-subagent-start.js +28 -23
  320. package/hooks/gsd-cursor-subagent-stop.js +3 -1
  321. package/hooks/gsd-ensure-canonical-path.js +2 -1
  322. package/hooks/gsd-graphify-update.sh +22 -18
  323. package/hooks/gsd-node-runner.sh +76 -0
  324. package/hooks/gsd-phase-boundary.sh +1 -0
  325. package/hooks/gsd-prompt-guard.js +16 -7
  326. package/hooks/gsd-read-guard.js +16 -7
  327. package/hooks/gsd-read-injection-scanner.js +17 -8
  328. package/hooks/gsd-session-state.sh +1 -0
  329. package/hooks/gsd-statusline.js +215 -26
  330. package/hooks/gsd-validate-commit.sh +80 -6
  331. package/hooks/gsd-windsurf-pre-command.js +16 -11
  332. package/hooks/gsd-windsurf-pre-write.js +22 -13
  333. package/hooks/gsd-workflow-guard.js +34 -16
  334. package/hooks/gsd-worktree-path-guard.js +36 -21
  335. package/hooks/gsd-write-guard.js +35 -25
  336. package/hooks/lib/cli-exit.js +560 -0
  337. package/hooks/lib/exit-code-registry.js +98 -0
  338. package/hooks/lib/git-probe.js +84 -0
  339. package/hooks/lib/hook-exit.js +81 -0
  340. package/hooks/managed-hooks-registry.cjs +3 -0
  341. package/package.json +12 -7
  342. package/scripts/base64-scan.sh +74 -12
  343. package/scripts/build-hooks.js +5 -0
  344. package/scripts/check-glossary-refs.cjs +77 -15
  345. package/scripts/check-mutation-score-ratchet.cjs +156 -0
  346. package/scripts/ci-check-job-near-cap.cjs +49 -0
  347. package/scripts/ci-pr-mergeability.cjs +262 -0
  348. package/scripts/ci-test-scope.cjs +45 -12
  349. package/scripts/ci-timeout-report.cjs +230 -0
  350. package/scripts/docs-guard-registry.cjs +396 -0
  351. package/scripts/gen-capability-registry.cjs +8 -6
  352. package/scripts/gen-exit-code-docs.cjs +318 -0
  353. package/scripts/gen-exit-code-registry.cjs +891 -0
  354. package/scripts/gen-features.cjs +836 -0
  355. package/scripts/gen-hooks-cli-exit.cjs +239 -0
  356. package/scripts/gen-install-tree-fixtures.cjs +2 -2
  357. package/scripts/gen-loop-host-contract.cjs +134 -1
  358. package/scripts/gen-scripts-cli-exit.cjs +185 -0
  359. package/scripts/gen-state-md-docs.cjs +727 -0
  360. package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
  361. package/scripts/lib/ci-job-timing.cjs +72 -0
  362. package/scripts/lib/cli-exit.cjs +546 -44
  363. package/scripts/lib/drift-scan.cjs +32 -2
  364. package/scripts/lib/exit-code-registry.cjs +98 -0
  365. package/scripts/lib/ndjson-reporter.cjs +119 -0
  366. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  367. package/scripts/lint-docs-guard-registration.cjs +495 -0
  368. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
  369. package/scripts/lint-eslint-glob-coverage.allowlist.json +4 -0
  370. package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
  371. package/scripts/lint-health-diagnostic-rule-table.cjs +65 -8
  372. package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
  373. package/scripts/lint-phase-enumeration-drift.cjs +21 -8
  374. package/scripts/lint-planning-prompt-drift.cjs +38 -1
  375. package/scripts/lint-removed-but-needed.cjs +184 -16
  376. package/scripts/lint-seam-enforcement.cjs +182 -0
  377. package/scripts/lint-slug-derivation-drift.cjs +921 -0
  378. package/scripts/lint-source-test-name-collision.cjs +241 -0
  379. package/scripts/lint-state-write-path-drift.cjs +337 -432
  380. package/scripts/lint-test-file-count.allowlist.json +122 -4
  381. package/scripts/lint-test-file-count.cjs +25 -3
  382. package/scripts/lint-unreachable-guard-drift.cjs +51 -64
  383. package/scripts/lint-vendored-deps.cjs +208 -35
  384. package/scripts/mutation-matrix.cjs +599 -50
  385. package/scripts/prompt-injection-scan.sh +75 -14
  386. package/scripts/secret-scan.sh +75 -13
  387. package/scripts/select-docs-guards.cjs +56 -0
  388. package/scripts/sync-runtime-launcher.cjs +22 -3
  389. package/skills/gsd-discuss-phase/SKILL.md +1 -1
  390. package/skills/gsd-import/SKILL.md +1 -1
  391. package/skills/gsd-quick/SKILL.md +8 -4
  392. package/vscode/package.json +1 -1
  393. package/bin/lib/ui-safety-gate.cjs +0 -109
  394. package/scripts/lint-emitted-drift-ack.cjs +0 -344
  395. package/scripts/state-write-path-drift-baseline.json +0 -19
@@ -85,6 +85,14 @@ function getControllingTtyToken() {
85
85
  return probeControllingTtyToken();
86
86
  }
87
87
  function getWorkstreamSessionKey() {
88
+ // #3883 (ADR-3473 §8.3): DECLARED DIFFERENT from generateSlugInternal
89
+ // (core-utils.cts), the canonical slug formula — deliberately, not a
90
+ // consolidation gap. The text slugified here is never caller-supplied: it
91
+ // is always one of the fixed ASCII WORKSTREAM_SESSION_ENV_KEYS identifier
92
+ // names above (e.g. "GSD_SESSION_KEY"), which already agree with the
93
+ // canonical's output byte-for-byte on this restricted input domain (no
94
+ // unicode, no length anywhere near the 60-char truncation boundary) — so
95
+ // delegating would add a require() edge for zero behavioral change.
88
96
  for (const envKey of WORKSTREAM_SESSION_ENV_KEYS) {
89
97
  const raw = process.env[envKey];
90
98
  const token = sanitizeWorkstreamSessionToken(raw);
@@ -14,6 +14,9 @@ const node_fs_1 = __importDefault(require("node:fs"));
14
14
  const node_path_1 = __importDefault(require("node:path"));
15
15
  const security_cjs_1 = require("./security.cjs");
16
16
  const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
17
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
18
+ const cliExitModule = require("./cli-exit.cjs");
19
+ const { ExitError, runMain } = cliExitModule;
17
20
  const STATUS_REJECT_SET = new Set(['superseded', 'rejected', 'deprecated']);
18
21
  const CANONICAL_HEADERS = {
19
22
  status: ['status', 'state', 'lifecycle', 'stage'],
@@ -405,13 +408,16 @@ function main(argv) {
405
408
  process.stdout.write(JSON.stringify(parsed, null, 2));
406
409
  }
407
410
  if (require.main === module) {
408
- try {
409
- main(process.argv.slice(2));
410
- }
411
- catch (error) {
412
- process.stderr.write(`Error: ${error.message}\n`);
413
- process.exit(1);
414
- }
411
+ runMain(() => {
412
+ try {
413
+ main(process.argv.slice(2));
414
+ }
415
+ catch (err) {
416
+ // ExitError with a message so runMain's catch writes it verbatim
417
+ // (byte-identical to the prior `Error: ${message}\n` process.exit(1)).
418
+ throw new ExitError(1, `Error: ${err.message}`);
419
+ }
420
+ });
415
421
  }
416
422
  module.exports = {
417
423
  CANONICAL_HEADERS,
@@ -29,6 +29,20 @@ const model_catalog_cjs_1 = require("./model-catalog.cjs");
29
29
  // locally. Behavior is unchanged: scanTomlLines/stripBOM here are the exact
30
30
  // same lenient functions that used to live in this file — see
31
31
  // codex-agent-toml.cts's module header for the reader/writer reconciliation.
32
+ // #3897 CAUSE A fix: `deriveCodexSandboxMode` needs the resolved `tools:`
33
+ // frontmatter VALUE, not raw content — this module used to reach that via
34
+ // `runtime-artifact-conversion.cjs`'s general-purpose
35
+ // `extractFrontmatterAndBody`/`extractFrontmatterField`, but that module's
36
+ // dependency chain (`command-roster.cjs` -> `scripts/fix-slash-commands.cjs`,
37
+ // a dev-only repo script) does not resolve from an installed tree — any test
38
+ // exercising a synthetic install dir blew up at module load. `codex-agent-toml.cjs`
39
+ // is already imported here, is a genuine leaf (node builtins only, zero
40
+ // side effects), and now exports `extractToolsValue` — a single-purpose
41
+ // `tools:`-VALUE reader (inline AND YAML block-list form, #3897 list-form
42
+ // parse fix), not a general frontmatter parser — for exactly this caller,
43
+ // shared with `bin/install.js`'s emitter so the two sandbox-feeding paths
44
+ // can never silently disagree, and no fourth copy of frontmatter parsing is
45
+ // added.
32
46
  const codex_agent_toml_cjs_1 = require("./codex-agent-toml.cjs");
33
47
  /**
34
48
  * Frozen reason enum for {@link checkCodexModelPosture}. Per CONTRIBUTING's
@@ -305,6 +319,153 @@ function checkCodexModelPosture(runtime, projectRoot) {
305
319
  agent_runtime: resolvedRuntime,
306
320
  };
307
321
  }
322
+ // #3897 rung 3 — the canonical bundled `agents/*.md` source directory (the
323
+ // tool-contract source of truth), resolved the same install-relative way
324
+ // getAgentsDir resolves the claude case, WITHOUT that function's npm-global
325
+ // node_modules redirect: that redirect exists so a Codex/claude INSTALL is
326
+ // never validated against itself, but here we deliberately want the bundled
327
+ // copy every time — it is the only place the `tools:` contract a role
328
+ // derives its sandbox_mode from actually lives, in every install shape
329
+ // (repo-dev tree or npm-global package, where the package still ships its
330
+ // own agents/ alongside bin/ and gsd-core/).
331
+ function _canonicalAgentSourceDir() {
332
+ return node_path_1.default.join(__dirname, '..', '..', '..', 'agents');
333
+ }
334
+ /**
335
+ * Validate installed Codex `.toml` agents' `sandbox_mode` against what each
336
+ * role's own tool contract derives (#3897, ADR-3473 §8.3 criterion 3,
337
+ * HALT.md option 2). Sibling to {@link checkCodexModelPosture} — same shape,
338
+ * same short-circuits — but a different defect class: a TOML whose
339
+ * `sandbox_mode` disagrees with the derived expectation, not a bad `model`.
340
+ *
341
+ * `deriveCodexSandboxMode` (and the `CODEX_SANDBOX_HOLDS` hold list + its own
342
+ * self-invalidation check it embeds) is imported from `codex-agent-toml.cjs`
343
+ * — the shared, side-effect-free leaf both `bin/install.js`'s emitter and
344
+ * this posture check import from (CAUSE A fix, #3897: this function used to
345
+ * lazily `require('bin/install.js')` to reach the same derivation, but
346
+ * requiring `bin/install.js` runs its whole CLI top-level, including an
347
+ * ASCII banner print to stdout, which corrupted every stdout-JSON caller of
348
+ * this posture check, e.g. `gsd-tools validate agents`) — so the emitter and
349
+ * this posture check can never silently diverge on the same tool-contract
350
+ * predicate — the exact generative-fix-divergence shape this epic exists to
351
+ * close. `deriveCodexSandboxMode` itself is total (never throws, #3897
352
+ * CAUSE C fix) — a held role whose supplied content no longer derives
353
+ * broader than its pin simply returns `read-only` rather than failing,
354
+ * because the pin is then a no-op. The staleness invariant (S4/S5) this
355
+ * check's imports still carry is enforced separately, at the real `agents/`
356
+ * roster level, by `validateCodexSandboxHolds` and
357
+ * `tests/codex-config.test.cjs` T24/T25 — not by this per-call derivation.
358
+ *
359
+ * Read-only: detects, never repairs — same posture as {@link checkCodexModelPosture}.
360
+ *
361
+ * @param runtime - the active runtime name; defaults to GSD_RUNTIME env, then 'claude'
362
+ * @param projectRoot - canonical project root for local-install discovery
363
+ */
364
+ function checkCodexSandboxPosture(runtime, projectRoot) {
365
+ const resolvedRuntime = runtime ?? (process.env['GSD_RUNTIME'] || 'claude');
366
+ if (resolvedRuntime !== 'codex') {
367
+ return {
368
+ ok: true,
369
+ violations: [],
370
+ checked: [],
371
+ agents_dir: '',
372
+ agent_runtime: resolvedRuntime,
373
+ reason: POSTURE_REASON.NOT_CODEX,
374
+ };
375
+ }
376
+ const agentsDir = getAgentsDir(resolvedRuntime, projectRoot);
377
+ if (!node_fs_1.default.existsSync(agentsDir)) {
378
+ return {
379
+ ok: true,
380
+ violations: [],
381
+ checked: [],
382
+ agents_dir: agentsDir,
383
+ agent_runtime: resolvedRuntime,
384
+ reason: POSTURE_REASON.AGENTS_DIR_MISSING,
385
+ };
386
+ }
387
+ const canonicalAgentsDir = _canonicalAgentSourceDir();
388
+ // Same symlink guard as checkCodexModelPosture, same reason (never follow
389
+ // an agents-dir symlink into an arbitrary file's content).
390
+ const tomlFiles = node_fs_1.default
391
+ .readdirSync(agentsDir)
392
+ .filter((entry) => {
393
+ if (!entry.endsWith('.toml'))
394
+ return false;
395
+ try {
396
+ return node_fs_1.default.lstatSync(node_path_1.default.join(agentsDir, entry)).isFile();
397
+ }
398
+ catch {
399
+ return false;
400
+ }
401
+ })
402
+ .sort();
403
+ const checked = [];
404
+ const violations = [];
405
+ for (const entry of tomlFiles) {
406
+ const agentName = entry.slice(0, -'.toml'.length);
407
+ const filePath = node_path_1.default.join(agentsDir, entry);
408
+ checked.push(agentName);
409
+ let raw;
410
+ try {
411
+ raw = node_fs_1.default.readFileSync(filePath, 'utf8');
412
+ }
413
+ catch {
414
+ violations.push({ agent: agentName, file: filePath, reason: POSTURE_REASON.UNREADABLE });
415
+ continue;
416
+ }
417
+ // #3897 rung 4 (isolated correctness review, MINOR finding 2): route
418
+ // through the same block-aware `scanTomlLines` the sibling
419
+ // `checkCodexModelPosture` already uses, rather than a naive whole-file
420
+ // regex — a `sandbox_mode = "..."`-shaped line inside the
421
+ // `developer_instructions` block (prose) must never be read as a live
422
+ // value. A prior naive `raw.match(/^sandbox_mode.../m)` here produced a
423
+ // FALSE violation whenever an agent's own prompt prose happened to
424
+ // contain that shape.
425
+ const found = (0, codex_agent_toml_cjs_1.scanTomlLines)((0, codex_agent_toml_cjs_1.stripBOM)(raw)).sandboxMode;
426
+ if (found === null) {
427
+ // No sandbox_mode line at all (e.g. sandboxTier "none") is a deliberate,
428
+ // documented state elsewhere — not this check's business.
429
+ continue;
430
+ }
431
+ const canonicalFile = node_path_1.default.join(canonicalAgentsDir, `${agentName}.md`);
432
+ let canonicalContent;
433
+ try {
434
+ canonicalContent = node_fs_1.default.readFileSync(canonicalFile, 'utf8');
435
+ }
436
+ catch {
437
+ // No canonical role source (a custom/non-roster agent) — nothing to
438
+ // derive an expectation from, so this is not this check's business.
439
+ continue;
440
+ }
441
+ // `canonicalContent` is always a real string here (read via fs.readFileSync
442
+ // above), so extractToolsValue's `undefined` (non-string input) branch is
443
+ // unreachable on this path — the `?? ''` is a type-level formality, not a
444
+ // behavior change (F4, #3897 security review: extractToolsValue is TOTAL).
445
+ const toolsRaw = (0, codex_agent_toml_cjs_1.extractToolsValue)(canonicalContent) ?? '';
446
+ const expected = (0, codex_agent_toml_cjs_1.deriveCodexSandboxMode)(agentName, toolsRaw);
447
+ if (expected !== found) {
448
+ // #3897 rung 4 (isolated correctness review, MINOR finding 3):
449
+ // truncatePostureValue matches the sibling checkCodexModelPosture's
450
+ // convention (see its own `value: truncatePostureValue(model)` above)
451
+ // so an oversized or secret-shaped `sandbox_mode` value can never
452
+ // reach `validate agents --raw` output at full length.
453
+ violations.push({
454
+ agent: agentName,
455
+ file: filePath,
456
+ expected: truncatePostureValue(expected),
457
+ found: truncatePostureValue(found),
458
+ });
459
+ }
460
+ }
461
+ return {
462
+ ok: violations.length === 0,
463
+ violations,
464
+ checked,
465
+ agents_dir: agentsDir,
466
+ agent_runtime: resolvedRuntime,
467
+ };
468
+ }
308
469
  /**
309
470
  * Probe a single agents dir for `<name>` across runtime filename variants.
310
471
  * Mirrors {@link checkAgentsInstalled}'s probe (`.md`, `.agent.md`, `.toml`,
@@ -368,6 +529,7 @@ module.exports = {
368
529
  getAgentsDir,
369
530
  checkAgentsInstalled,
370
531
  checkCodexModelPosture,
532
+ checkCodexSandboxPosture,
371
533
  POSTURE_REASON,
372
534
  resolveAgentHint,
373
535
  };
@@ -52,9 +52,13 @@
52
52
  * renderCoverageMatrix(rows) -> string
53
53
  * DEFAULT_API_COVERAGE_TERMS
54
54
  *
55
- * CLI:
55
+ * CLI (ADR-3889 Phase 3, #3907):
56
56
  * echo "$SCOPE" | node gsd-core/bin/lib/api-coverage.cjs [--json]
57
- * exit 0 = integration detected, 1 = none, 2 = startup error
57
+ * exit 0 = integration detected, 1 = none (real input, examined, no signal),
58
+ * NO_INPUT (registry, src/cli-exit.cts) = stdin empty/whitespace-only,
59
+ * UNAVAILABLE (registry) = stdin read failed
60
+ * --json additionally prints the typed IR on stdout (unchanged for 0/1);
61
+ * NO_INPUT/UNAVAILABLE print {skipped:true, reason:"no_input"|"stdin_error"}
58
62
  */
59
63
  Object.defineProperty(exports, "__esModule", { value: true });
60
64
  exports.DEFAULT_API_COVERAGE_TERMS = void 0;
@@ -849,11 +853,24 @@ function renderCoverageMatrix(rows) {
849
853
  // ── CLI entry point ──────────────────────────────────────────────────────────
850
854
  // Reads phase-scope text from STDIN (not argv) to avoid OS ARG_MAX limits.
851
855
  // Invoked by workflow bash as: echo "$SCOPE" | node .../api-coverage.cjs [--json]
852
- // Exit 0 = integration detected, 1 = none, 2 = startup error. Mirrors
853
- // assumption-delta.cjs / ui-safety-gate.cjs.
856
+ //
857
+ // Exit codes (ADR-3889 Phase 3, #3907): 0 = integration detected, 1 = none
858
+ // (real input, examined, no signal), NO_INPUT (registry — src/cli-exit.cts) =
859
+ // stdin empty/whitespace-only, UNAVAILABLE (registry) = stdin read failed.
860
+ // The prior single "2 = startup error" arm let empty input flow into the
861
+ // detector and exit 1 — an unexamined input reported as an authoritative
862
+ // negative. Under --json, NO_INPUT/UNAVAILABLE emit `{skipped:true,
863
+ // reason:"no_input"|"stdin_error"}` — no `detected` key, so a stale
864
+ // `detected:false` can never sit beside `skipped:true`; the detected/no-signal
865
+ // `--json` payloads are byte-identical to before. Terminates via terminateNow
866
+ // (write-then-terminate, total by construction), not raw process.exit — these
867
+ // exits fire from inside a stdin event handler. Mirrors assumption-delta.cjs /
868
+ // ui-safety-gate.cjs.
854
869
  if (require.main === module) {
855
870
  const argv = process.argv.slice(2);
856
871
  const wantJson = argv.includes('--json');
872
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
873
+ const cliExit = require('./cli-exit.cjs');
857
874
  let termsOverride;
858
875
  const verbsIdx = argv.indexOf('--verbs');
859
876
  const verbsVal = verbsIdx !== -1 ? argv[verbsIdx + 1] : undefined;
@@ -877,14 +894,18 @@ if (require.main === module) {
877
894
  process.stdin.on('data', (chunk) => chunks.push(chunk));
878
895
  process.stdin.on('end', () => {
879
896
  const input = chunks.join('');
880
- const result = detectApiIntegration(input, termsOverride);
881
- if (wantJson) {
882
- process.stdout.write(JSON.stringify(result) + '\n');
897
+ // Whitespace-only counts as empty (ADR-3889 §1's NO_INPUT: "zero units
898
+ // were in scope, and that emptiness is known to be genuine"). Real input
899
+ // — including a lone NUL byte, which .trim() does not strip — always
900
+ // falls through to the detector.
901
+ if (input.trim().length === 0) {
902
+ cliExit.terminateNow('NO_INPUT', wantJson ? { skipped: true, reason: 'no_input' } : undefined);
883
903
  }
884
- process.exit(result.detected ? 0 : 1);
904
+ const result = detectApiIntegration(input, termsOverride);
905
+ cliExit.terminateNow(result.detected ? 'PASS' : 'FAIL', wantJson ? result : undefined);
885
906
  });
886
907
  process.stdin.on('error', (err) => {
887
908
  process.stderr.write(`ERROR: api-coverage.cjs stdin read failed: ${err.message}\n`);
888
- process.exit(2);
909
+ cliExit.terminateNow('UNAVAILABLE', wantJson ? { skipped: true, reason: 'stdin_error' } : undefined);
889
910
  });
890
911
  }
@@ -30,6 +30,8 @@ exports.CANONICAL_EXACT = new Set([
30
30
  'WINDOWS.md', // #3224: broken-windows ledger (src/broken-windows.cts, LEDGER_FILE_NAME)
31
31
  'STATE-ARCHIVE.md', // state.cts's cmdStatePrune writes this at the .planning/ root
32
32
  'milestone.lock', // #3311: milestone (phase + session) claim (src/milestone-lock.cts); persistent, unlike the transient STATE.md.lock/WAITING.json
33
+ 'state.json', // #3227: machine-readable state contract published at step boundaries (src/state-contract.cts)
34
+ 'skill-manifest.json', // init.cts routeSkillManifest --write (project-scoped planning root, #3964)
33
35
  ]);
34
36
  // Pattern-match canonical file names (regex tests on the basename)
35
37
  // Each pattern includes the name of the workflow that produces it as a comment.
@@ -21,17 +21,19 @@
21
21
  * tunable (config + the `terms` parameter) so teams can widen it.
22
22
  * - FENCED CODE BLOCKS ARE STRIPPED first (via the markdown-sectionizer seam)
23
23
  * so a trigger term that appears only inside a code snippet does not fire.
24
- * - Mirrors ui-safety-gate.cts: a pure function + a STDIN-reading CLI whose
25
- * exit codes mirror grep (0 = signal found, 1 = none, 2 = usage error).
24
+ * - Mirrors ui-safety-gate.cts: a pure function + a STDIN-reading CLI.
26
25
  *
27
26
  * Public API:
28
27
  * detectAssumptionDelta(text, terms?) -> { detected, signals, terms }
29
28
  * DEFAULT_ASSUMPTION_DELTA_TERMS
30
29
  *
31
- * CLI:
30
+ * CLI (ADR-3889 Phase 3, #3907):
32
31
  * echo "$PHASE_SECTION" | node gsd-core/bin/lib/assumption-delta.cjs [--json]
33
- * exit 0 = signal detected, 1 = none, 2 = startup error
34
- * --json additionally prints the typed IR on stdout
32
+ * exit 0 = signal detected, 1 = none (real input, examined, no signal),
33
+ * NO_INPUT (registry, src/cli-exit.cts) = stdin empty/whitespace-only,
34
+ * UNAVAILABLE (registry) = stdin read failed
35
+ * --json additionally prints the typed IR on stdout (unchanged for 0/1);
36
+ * NO_INPUT/UNAVAILABLE print {skipped:true, reason:"no_input"|"stdin_error"}
35
37
  */
36
38
  Object.defineProperty(exports, "__esModule", { value: true });
37
39
  exports.DEFAULT_ASSUMPTION_DELTA_TERMS = void 0;
@@ -192,10 +194,23 @@ function detectAssumptionDelta(text, terms) {
192
194
  // ── CLI entry point ──────────────────────────────────────────────────────────
193
195
  // Reads phase-section text from STDIN (not argv) to avoid OS ARG_MAX limits.
194
196
  // Invoked by workflow bash as: echo "$PHASE_SECTION" | node .../assumption-delta.cjs [--json]
195
- // Exit 0 = signal detected, 1 = none, 2 = startup error. Mirrors ui-safety-gate.
197
+ //
198
+ // Exit codes (ADR-3889 Phase 3, #3907): 0 = signal detected, 1 = none (real
199
+ // input, examined, no signal), NO_INPUT (registry — src/cli-exit.cts) = stdin
200
+ // empty/whitespace-only, UNAVAILABLE (registry) = stdin read failed. The prior
201
+ // single "2 = startup error" arm let empty input flow into the detector and
202
+ // exit 1 — an unexamined input reported as an authoritative negative. Under
203
+ // --json, NO_INPUT/UNAVAILABLE emit `{skipped:true, reason:"no_input"|
204
+ // "stdin_error"}` — no `detected` key, so a stale `detected:false` can never
205
+ // sit beside `skipped:true`; the detected/no-signal `--json` payloads are
206
+ // byte-identical to before. Terminates via terminateNow (write-then-terminate,
207
+ // total by construction), not raw process.exit — these exits fire from inside
208
+ // a stdin event handler. Mirrors ui-safety-gate.cjs / api-coverage.cjs.
196
209
  if (require.main === module) {
197
210
  const argv = process.argv.slice(2);
198
211
  const wantJson = argv.includes('--json');
212
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
213
+ const cliExit = require('./cli-exit.cjs');
199
214
  // --terms <csv>: config-tunable vocabulary override. Replaces the
200
215
  // pluralization cues (the primary trigger); optional/chosen keep defaults.
201
216
  // An EMPTY value ("") or a flag-shaped value restores the curated defaults
@@ -216,14 +231,18 @@ if (require.main === module) {
216
231
  process.stdin.on('data', (chunk) => chunks.push(chunk));
217
232
  process.stdin.on('end', () => {
218
233
  const input = chunks.join('');
219
- const result = detectAssumptionDelta(input, termsOverride);
220
- if (wantJson) {
221
- process.stdout.write(JSON.stringify(result) + '\n');
234
+ // Whitespace-only counts as empty (ADR-3889 §1's NO_INPUT: "zero units
235
+ // were in scope, and that emptiness is known to be genuine"). Real input
236
+ // — including a lone NUL byte, which .trim() does not strip — always
237
+ // falls through to the detector.
238
+ if (input.trim().length === 0) {
239
+ cliExit.terminateNow('NO_INPUT', wantJson ? { skipped: true, reason: 'no_input' } : undefined);
222
240
  }
223
- process.exit(result.detected ? 0 : 1);
241
+ const result = detectAssumptionDelta(input, termsOverride);
242
+ cliExit.terminateNow(result.detected ? 'PASS' : 'FAIL', wantJson ? result : undefined);
224
243
  });
225
244
  process.stdin.on('error', (err) => {
226
245
  process.stderr.write(`ERROR: assumption-delta.cjs stdin read failed: ${err.message}\n`);
227
- process.exit(2);
246
+ cliExit.terminateNow('UNAVAILABLE', wantJson ? { skipped: true, reason: 'stdin_error' } : undefined);
228
247
  });
229
248
  }