@opengsd/gsd-core 1.11.0 → 1.13.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 (498) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.opencode/plugins/gsd-core.js +12 -0
  4. package/agents/gsd-code-fixer.md +1 -1
  5. package/agents/gsd-debug-session-manager.md +1 -1
  6. package/agents/gsd-debugger.md +1 -1
  7. package/agents/gsd-dom-verifier.md +169 -0
  8. package/agents/gsd-eval-auditor.md +1 -1
  9. package/agents/gsd-executor.md +78 -42
  10. package/agents/gsd-framework-selector.md +1 -3
  11. package/agents/gsd-intel-updater.md +1 -1
  12. package/agents/gsd-mempalace-curator.md +0 -1
  13. package/agents/gsd-pattern-mapper.md +11 -0
  14. package/agents/gsd-phase-researcher.md +3 -1
  15. package/agents/gsd-plan-checker.md +91 -112
  16. package/agents/gsd-planner.md +20 -4
  17. package/agents/gsd-project-researcher.md +1 -1
  18. package/agents/gsd-research-synthesizer.md +2 -2
  19. package/agents/gsd-roadmapper.md +15 -11
  20. package/agents/gsd-ui-checker.md +82 -7
  21. package/agents/gsd-ui-researcher.md +70 -3
  22. package/agents/gsd-verifier.md +24 -2
  23. package/bin/install.js +847 -200
  24. package/commands/gsd/discuss-phase.md +1 -1
  25. package/commands/gsd/execute-phase.md +1 -1
  26. package/commands/gsd/import.md +1 -1
  27. package/commands/gsd/ns-workflow.md +2 -1
  28. package/commands/gsd/phase.md +1 -1
  29. package/commands/gsd/quick-batch.md +105 -0
  30. package/commands/gsd/quick.md +8 -4
  31. package/commands/gsd/surface.md +18 -8
  32. package/gsd-core/bin/gsd-tools.cjs +761 -100
  33. package/gsd-core/bin/lib/active-workstream-store.cjs +8 -0
  34. package/gsd-core/bin/lib/adr-parser.cjs +13 -7
  35. package/gsd-core/bin/lib/agent-install-check.cjs +162 -0
  36. package/gsd-core/bin/lib/api-coverage.cjs +30 -9
  37. package/gsd-core/bin/lib/artifacts.cjs +2 -0
  38. package/gsd-core/bin/lib/assumption-delta.cjs +30 -11
  39. package/gsd-core/bin/lib/audit.cjs +163 -41
  40. package/gsd-core/bin/lib/broken-windows.cjs +306 -28
  41. package/gsd-core/bin/lib/capability-activation.cjs +27 -0
  42. package/gsd-core/bin/lib/capability-lock.cjs +10 -4
  43. package/gsd-core/bin/lib/capability-registry.cjs +785 -144
  44. package/gsd-core/bin/lib/capability-state.cjs +25 -4
  45. package/gsd-core/bin/lib/capability-validator.cjs +321 -18
  46. package/gsd-core/bin/lib/capability-writer.cjs +14 -4
  47. package/gsd-core/bin/lib/check-command-router.cjs +229 -6
  48. package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
  49. package/gsd-core/bin/lib/cli-exit.cjs +496 -10
  50. package/gsd-core/bin/lib/clusters.cjs +1 -0
  51. package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
  52. package/gsd-core/bin/lib/codex-agent-toml.cjs +410 -4
  53. package/gsd-core/bin/lib/command-aliases.cjs +16 -0
  54. package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
  55. package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
  56. package/gsd-core/bin/lib/commands.cjs +877 -54
  57. package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
  58. package/gsd-core/bin/lib/config-loader.cjs +121 -29
  59. package/gsd-core/bin/lib/config.cjs +92 -2
  60. package/gsd-core/bin/lib/configuration.cjs +129 -37
  61. package/gsd-core/bin/lib/core-utils.cjs +118 -14
  62. package/gsd-core/bin/lib/decisions.cjs +213 -1
  63. package/gsd-core/bin/lib/edge-probe.cjs +23 -2
  64. package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
  65. package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
  66. package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
  67. package/gsd-core/bin/lib/frontmatter.cjs +975 -326
  68. package/gsd-core/bin/lib/gap-checker.cjs +41 -8
  69. package/gsd-core/bin/lib/git-base-branch.cjs +182 -39
  70. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
  71. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
  72. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +60 -14
  73. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +75 -22
  74. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +22 -8
  75. package/gsd-core/bin/lib/health-diagnostic.cjs +23 -3
  76. package/gsd-core/bin/lib/host-integration.cjs +96 -11
  77. package/gsd-core/bin/lib/init-command-router.cjs +132 -21
  78. package/gsd-core/bin/lib/init.cjs +252 -56
  79. package/gsd-core/bin/lib/install-engine.cjs +252 -15
  80. package/gsd-core/bin/lib/install-model-override-resolver.cjs +78 -1
  81. package/gsd-core/bin/lib/install-profiles.cjs +100 -18
  82. package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
  83. package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
  84. package/gsd-core/bin/lib/installer-migrations.cjs +10 -7
  85. package/gsd-core/bin/lib/intel.cjs +101 -26
  86. package/gsd-core/bin/lib/io.cjs +195 -15
  87. package/gsd-core/bin/lib/learnings.cjs +85 -14
  88. package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
  89. package/gsd-core/bin/lib/loop-resolver.cjs +14 -8
  90. package/gsd-core/bin/lib/markdown-table.cjs +175 -4
  91. package/gsd-core/bin/lib/milestone.cjs +112 -7
  92. package/gsd-core/bin/lib/model-catalog.cjs +177 -19
  93. package/gsd-core/bin/lib/model-resolver.cjs +10 -28
  94. package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
  95. package/gsd-core/bin/lib/phase-command-router.cjs +13 -6
  96. package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
  97. package/gsd-core/bin/lib/phase-id.cjs +321 -13
  98. package/gsd-core/bin/lib/phase-lifecycle.cjs +24 -16
  99. package/gsd-core/bin/lib/phase-locator.cjs +138 -17
  100. package/gsd-core/bin/lib/phase.cjs +1175 -115
  101. package/gsd-core/bin/lib/plan-document.cjs +273 -0
  102. package/gsd-core/bin/lib/plan-scan.cjs +13 -2
  103. package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
  104. package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
  105. package/gsd-core/bin/lib/planning-snapshot.cjs +165 -34
  106. package/gsd-core/bin/lib/planning-workspace.cjs +159 -28
  107. package/gsd-core/bin/lib/probe-core.cjs +4 -1
  108. package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
  109. package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
  110. package/gsd-core/bin/lib/quick-batch-command-router.cjs +285 -0
  111. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
  112. package/gsd-core/bin/lib/quick-batch.cjs +840 -0
  113. package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
  114. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +71 -45
  115. package/gsd-core/bin/lib/review-lane-descriptor.cjs +62 -14
  116. package/gsd-core/bin/lib/review-lane-invocation.cjs +73 -1
  117. package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
  118. package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
  119. package/gsd-core/bin/lib/roadmap-parser.cjs +577 -41
  120. package/gsd-core/bin/lib/roadmap.cjs +248 -64
  121. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +329 -41
  122. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +16 -17
  123. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +320 -109
  124. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +487 -83
  125. package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
  126. package/gsd-core/bin/lib/runtime-slash.cjs +72 -2
  127. package/gsd-core/bin/lib/shell-command-projection.cjs +75 -8
  128. package/gsd-core/bin/lib/smart-entry.cjs +19 -31
  129. package/gsd-core/bin/lib/spec-section.cjs +12 -7
  130. package/gsd-core/bin/lib/state-command-router.cjs +47 -18
  131. package/gsd-core/bin/lib/state-contract.cjs +359 -0
  132. package/gsd-core/bin/lib/state-document.cjs +216 -5
  133. package/gsd-core/bin/lib/state-md-schema.cjs +231 -0
  134. package/gsd-core/bin/lib/state-transition.cjs +850 -145
  135. package/gsd-core/bin/lib/state.cjs +1629 -287
  136. package/gsd-core/bin/lib/surface.cjs +33 -10
  137. package/gsd-core/bin/lib/task-command-router.cjs +111 -1
  138. package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
  139. package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
  140. package/gsd-core/bin/lib/teams-status.cjs +4 -1
  141. package/gsd-core/bin/lib/uat-predicate.cjs +58 -20
  142. package/gsd-core/bin/lib/uat.cjs +2542 -387
  143. package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
  144. package/gsd-core/bin/lib/ui-safety-gate.cjs +37 -7
  145. package/gsd-core/bin/lib/unusable-input.cjs +13 -0
  146. package/gsd-core/bin/lib/update-context.cjs +6 -2
  147. package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
  148. package/gsd-core/bin/lib/validate.cjs +230 -12
  149. package/gsd-core/bin/lib/vendor/README.md +43 -5
  150. package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
  151. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  152. package/gsd-core/bin/lib/verification.cjs +287 -13
  153. package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
  154. package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
  155. package/gsd-core/bin/lib/verify.cjs +441 -56
  156. package/gsd-core/bin/lib/workstream-inventory.cjs +20 -2
  157. package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
  158. package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
  159. package/gsd-core/bin/lib/worktree-safety.cjs +185 -21
  160. package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
  161. package/gsd-core/bin/shared/config-schema.manifest.json +13 -0
  162. package/gsd-core/bin/shared/exit-codes.json +8 -0
  163. package/gsd-core/bin/shared/exit-codes.sh +20 -0
  164. package/gsd-core/bin/shared/model-catalog.json +8 -1
  165. package/gsd-core/bin/verify-reapply-patches.cjs +70 -3
  166. package/gsd-core/references/agent-contracts.md +6 -5
  167. package/gsd-core/references/api-coverage.md +24 -2
  168. package/gsd-core/references/autonomous-smart-discuss.md +3 -3
  169. package/gsd-core/references/checkpoints.md +37 -19
  170. package/gsd-core/references/decimal-phase-calculation.md +5 -5
  171. package/gsd-core/references/edge-probe.md +17 -5
  172. package/gsd-core/references/execute-mvp-tdd.md +18 -18
  173. package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
  174. package/gsd-core/references/execute-phase-response-language.md +6 -0
  175. package/gsd-core/references/execute-phase-wave-guard.md +11 -9
  176. package/gsd-core/references/executor-examples.md +42 -0
  177. package/gsd-core/references/failing-direction.md +78 -0
  178. package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
  179. package/gsd-core/references/gate-prompts.md +1 -1
  180. package/gsd-core/references/git-integration.md +5 -5
  181. package/gsd-core/references/git-planning-commit.md +3 -3
  182. package/gsd-core/references/gsd-run-resolver.md +1 -1
  183. package/gsd-core/references/loop-hook-dispatch.md +22 -0
  184. package/gsd-core/references/model-profiles.md +1 -1
  185. package/gsd-core/references/mvp-concepts.md +2 -2
  186. package/gsd-core/references/nyquist-compliance.md +74 -0
  187. package/gsd-core/references/offer-next.md +3 -5
  188. package/gsd-core/references/phase-argument-parsing.md +3 -3
  189. package/gsd-core/references/plan-checker-examples.md +41 -0
  190. package/gsd-core/references/planner-antipatterns.md +25 -0
  191. package/gsd-core/references/planner-chunked.md +5 -1
  192. package/gsd-core/references/planner-coupling.md +42 -0
  193. package/gsd-core/references/planner-failing-direction.md +53 -0
  194. package/gsd-core/references/planner-human-verify-mode.md +15 -1
  195. package/gsd-core/references/planner-quick-batch.md +71 -0
  196. package/gsd-core/references/planner-reviews.md +47 -0
  197. package/gsd-core/references/planner-revision.md +76 -3
  198. package/gsd-core/references/planner-verify-command-grounding.md +17 -0
  199. package/gsd-core/references/planning-config.md +39 -9
  200. package/gsd-core/references/response-language-directive.md +9 -0
  201. package/gsd-core/references/reviewer-instances.md +31 -0
  202. package/gsd-core/references/revision-loop.md +118 -11
  203. package/gsd-core/references/runtime-aware-dispatch.md +1 -1
  204. package/gsd-core/references/tdd.md +15 -12
  205. package/gsd-core/references/ui-brand.md +65 -21
  206. package/gsd-core/references/ui-consideration-probe.md +1 -1
  207. package/gsd-core/references/universal-anti-patterns.md +2 -2
  208. package/gsd-core/references/verifier-evidence-gate.md +160 -0
  209. package/gsd-core/references/verify-command-path-resolvability.md +42 -0
  210. package/gsd-core/references/verify-mvp-mode.md +1 -1
  211. package/gsd-core/references/workstream-flag.md +11 -11
  212. package/gsd-core/templates/README.md +1 -1
  213. package/gsd-core/templates/SECURITY.md +3 -3
  214. package/gsd-core/templates/UI-SPEC.md +25 -3
  215. package/gsd-core/templates/VALIDATION.md +3 -3
  216. package/gsd-core/templates/phase-prompt.md +7 -0
  217. package/gsd-core/templates/state.md +7 -0
  218. package/gsd-core/templates/verification-report.md +5 -0
  219. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  220. package/gsd-core/workflows/add-backlog.md +3 -1
  221. package/gsd-core/workflows/add-phase.md +5 -3
  222. package/gsd-core/workflows/add-tests.md +4 -9
  223. package/gsd-core/workflows/add-todo.md +2 -2
  224. package/gsd-core/workflows/ai-integration-phase.md +5 -10
  225. package/gsd-core/workflows/analyze-dependencies.md +2 -0
  226. package/gsd-core/workflows/audit-fix.md +14 -3
  227. package/gsd-core/workflows/audit-milestone.md +11 -9
  228. package/gsd-core/workflows/audit-uat.md +19 -2
  229. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
  230. package/gsd-core/workflows/autonomous.md +12 -26
  231. package/gsd-core/workflows/check-todos.md +2 -2
  232. package/gsd-core/workflows/cleanup.md +3 -3
  233. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +16 -14
  234. package/gsd-core/workflows/code-review-fix.md +3 -1
  235. package/gsd-core/workflows/code-review.md +192 -69
  236. package/gsd-core/workflows/complete-milestone.md +28 -14
  237. package/gsd-core/workflows/debug.md +6 -4
  238. package/gsd-core/workflows/diagnose-issues.md +17 -7
  239. package/gsd-core/workflows/discuss-phase/modes/advisor.md +3 -1
  240. package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
  241. package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
  242. package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
  243. package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
  244. package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -7
  245. package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
  246. package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
  247. package/gsd-core/workflows/discuss-phase/modes/text.md +3 -1
  248. package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
  249. package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
  250. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
  251. package/gsd-core/workflows/discuss-phase-assumptions.md +3 -3
  252. package/gsd-core/workflows/discuss-phase-power.md +2 -0
  253. package/gsd-core/workflows/discuss-phase.md +2 -2
  254. package/gsd-core/workflows/do.md +46 -19
  255. package/gsd-core/workflows/docs-update.md +6 -5
  256. package/gsd-core/workflows/edit-phase.md +3 -1
  257. package/gsd-core/workflows/eval-review.md +5 -10
  258. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +3 -1
  259. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +129 -11
  260. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
  261. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
  262. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  263. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +29 -5
  264. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
  265. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
  266. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +4 -2
  267. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
  268. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
  269. package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
  270. package/gsd-core/workflows/execute-phase.md +68 -66
  271. package/gsd-core/workflows/execute-plan.md +25 -20
  272. package/gsd-core/workflows/explore.md +3 -1
  273. package/gsd-core/workflows/extract-learnings.md +3 -1
  274. package/gsd-core/workflows/fast.md +8 -2
  275. package/gsd-core/workflows/forensics.md +3 -1
  276. package/gsd-core/workflows/graduation.md +6 -6
  277. package/gsd-core/workflows/health.md +4 -7
  278. package/gsd-core/workflows/help/modes/brief.md +2 -0
  279. package/gsd-core/workflows/help/modes/default.md +2 -0
  280. package/gsd-core/workflows/help/modes/full.md +12 -0
  281. package/gsd-core/workflows/help/modes/topic.md +2 -0
  282. package/gsd-core/workflows/help.md +2 -0
  283. package/gsd-core/workflows/import.md +17 -14
  284. package/gsd-core/workflows/inbox.md +5 -6
  285. package/gsd-core/workflows/ingest-docs.md +45 -12
  286. package/gsd-core/workflows/insert-phase.md +7 -5
  287. package/gsd-core/workflows/list-phase-assumptions.md +2 -0
  288. package/gsd-core/workflows/list-seeds.md +7 -3
  289. package/gsd-core/workflows/list-workspaces.md +3 -1
  290. package/gsd-core/workflows/manager.md +15 -26
  291. package/gsd-core/workflows/map-codebase.md +3 -1
  292. package/gsd-core/workflows/milestone-summary.md +3 -1
  293. package/gsd-core/workflows/mvp-phase.md +3 -3
  294. package/gsd-core/workflows/new-milestone.md +10 -22
  295. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  296. package/gsd-core/workflows/new-project.md +17 -29
  297. package/gsd-core/workflows/new-workspace.md +2 -2
  298. package/gsd-core/workflows/next.md +4 -2
  299. package/gsd-core/workflows/node-repair.md +2 -0
  300. package/gsd-core/workflows/note.md +2 -0
  301. package/gsd-core/workflows/onboard.md +1 -1
  302. package/gsd-core/workflows/pause-work.md +20 -5
  303. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
  304. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
  305. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +4 -4
  306. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +12 -3
  307. package/gsd-core/workflows/plan-phase.md +251 -54
  308. package/gsd-core/workflows/plan-review-convergence.md +148 -19
  309. package/gsd-core/workflows/plant-seed.md +3 -3
  310. package/gsd-core/workflows/pr-branch.md +195 -51
  311. package/gsd-core/workflows/profile-user.md +17 -15
  312. package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
  313. package/gsd-core/workflows/progress.md +52 -15
  314. package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
  315. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +38 -5
  316. package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
  317. package/gsd-core/workflows/quick/steps/research-phase.md +5 -7
  318. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
  319. package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
  320. package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
  321. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
  322. package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
  323. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
  324. package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
  325. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
  326. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
  327. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
  328. package/gsd-core/workflows/quick-batch.md +203 -0
  329. package/gsd-core/workflows/quick.md +33 -32
  330. package/gsd-core/workflows/reapply-patches.md +2 -0
  331. package/gsd-core/workflows/remove-phase.md +6 -4
  332. package/gsd-core/workflows/remove-workspace.md +3 -3
  333. package/gsd-core/workflows/resume-project.md +14 -14
  334. package/gsd-core/workflows/review.md +404 -21
  335. package/gsd-core/workflows/scan.md +3 -1
  336. package/gsd-core/workflows/section-manifest.json +12 -0
  337. package/gsd-core/workflows/secure-phase.md +3 -3
  338. package/gsd-core/workflows/session-report.md +2 -0
  339. package/gsd-core/workflows/settings-advanced.md +9 -9
  340. package/gsd-core/workflows/settings-integrations.md +66 -32
  341. package/gsd-core/workflows/settings.md +4 -6
  342. package/gsd-core/workflows/ship.md +22 -16
  343. package/gsd-core/workflows/sketch-wrap-up.md +13 -17
  344. package/gsd-core/workflows/sketch.md +13 -19
  345. package/gsd-core/workflows/smart-entry.md +4 -6
  346. package/gsd-core/workflows/spec-phase.md +31 -4
  347. package/gsd-core/workflows/spike-wrap-up.md +9 -11
  348. package/gsd-core/workflows/spike.md +21 -32
  349. package/gsd-core/workflows/stats.md +4 -2
  350. package/gsd-core/workflows/sync-skills.md +13 -5
  351. package/gsd-core/workflows/thread.md +13 -7
  352. package/gsd-core/workflows/transition.md +7 -5
  353. package/gsd-core/workflows/ui-phase.md +36 -21
  354. package/gsd-core/workflows/ui-review.md +7 -11
  355. package/gsd-core/workflows/ultraplan-phase.md +7 -13
  356. package/gsd-core/workflows/undo.md +9 -17
  357. package/gsd-core/workflows/update.md +47 -48
  358. package/gsd-core/workflows/validate-phase.md +3 -3
  359. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
  360. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
  361. package/gsd-core/workflows/verify-work.md +106 -21
  362. package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
  363. package/hooks/dist/gsd-check-update-worker.js +19 -2
  364. package/hooks/dist/gsd-config-reload.js +18 -12
  365. package/hooks/dist/gsd-context-monitor.js +302 -22
  366. package/hooks/dist/gsd-cursor-post-tool.js +3 -1
  367. package/hooks/dist/gsd-cursor-pre-tool.js +3 -1
  368. package/hooks/dist/gsd-cursor-session-start.js +2 -1
  369. package/hooks/dist/gsd-cursor-stop.js +2 -1
  370. package/hooks/dist/gsd-cursor-subagent-start.js +28 -23
  371. package/hooks/dist/gsd-cursor-subagent-stop.js +3 -1
  372. package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
  373. package/hooks/dist/gsd-graphify-update.sh +22 -18
  374. package/hooks/dist/gsd-node-runner.sh +77 -0
  375. package/hooks/dist/gsd-phase-boundary.sh +1 -0
  376. package/hooks/dist/gsd-prompt-guard.js +46 -12
  377. package/hooks/dist/gsd-read-guard.js +18 -7
  378. package/hooks/dist/gsd-read-injection-scanner.js +22 -13
  379. package/hooks/dist/gsd-secret-read-guard.js +1079 -0
  380. package/hooks/dist/gsd-session-state.sh +1 -0
  381. package/hooks/dist/gsd-statusline.js +222 -29
  382. package/hooks/dist/gsd-validate-commit.sh +523 -12
  383. package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
  384. package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
  385. package/hooks/dist/gsd-workflow-guard.js +36 -17
  386. package/hooks/dist/gsd-worktree-path-guard.js +36 -21
  387. package/hooks/dist/gsd-write-guard.js +35 -25
  388. package/hooks/dist/lib/cli-exit.js +560 -0
  389. package/hooks/dist/lib/exit-code-registry.js +98 -0
  390. package/hooks/dist/lib/git-cmd.js +210 -1
  391. package/hooks/dist/lib/git-probe.js +84 -0
  392. package/hooks/dist/lib/hook-exit.js +81 -0
  393. package/hooks/dist/lib/injection-patterns.js +36 -6
  394. package/hooks/dist/managed-hooks-registry.cjs +4 -0
  395. package/hooks/gsd-agent-isolation-guard.js +77 -38
  396. package/hooks/gsd-check-update-worker.js +19 -2
  397. package/hooks/gsd-config-reload.js +18 -12
  398. package/hooks/gsd-context-monitor.js +302 -22
  399. package/hooks/gsd-cursor-post-tool.js +3 -1
  400. package/hooks/gsd-cursor-pre-tool.js +3 -1
  401. package/hooks/gsd-cursor-session-start.js +2 -1
  402. package/hooks/gsd-cursor-stop.js +2 -1
  403. package/hooks/gsd-cursor-subagent-start.js +28 -23
  404. package/hooks/gsd-cursor-subagent-stop.js +3 -1
  405. package/hooks/gsd-ensure-canonical-path.js +2 -1
  406. package/hooks/gsd-graphify-update.sh +22 -18
  407. package/hooks/gsd-node-runner.sh +77 -0
  408. package/hooks/gsd-phase-boundary.sh +1 -0
  409. package/hooks/gsd-prompt-guard.js +46 -12
  410. package/hooks/gsd-read-guard.js +18 -7
  411. package/hooks/gsd-read-injection-scanner.js +22 -13
  412. package/hooks/gsd-secret-read-guard.js +1079 -0
  413. package/hooks/gsd-session-state.sh +1 -0
  414. package/hooks/gsd-statusline.js +222 -29
  415. package/hooks/gsd-validate-commit.sh +523 -12
  416. package/hooks/gsd-windsurf-pre-command.js +16 -11
  417. package/hooks/gsd-windsurf-pre-write.js +22 -13
  418. package/hooks/gsd-workflow-guard.js +36 -17
  419. package/hooks/gsd-worktree-path-guard.js +36 -21
  420. package/hooks/gsd-write-guard.js +35 -25
  421. package/hooks/hooks.json +6 -0
  422. package/hooks/lib/cli-exit.js +560 -0
  423. package/hooks/lib/exit-code-registry.js +98 -0
  424. package/hooks/lib/git-cmd.js +210 -1
  425. package/hooks/lib/git-probe.js +84 -0
  426. package/hooks/lib/hook-exit.js +81 -0
  427. package/hooks/lib/injection-patterns.js +36 -6
  428. package/hooks/managed-hooks-registry.cjs +4 -0
  429. package/package.json +14 -9
  430. package/scripts/base64-scan.sh +74 -12
  431. package/scripts/build-hooks.js +12 -0
  432. package/scripts/check-glossary-refs.cjs +77 -15
  433. package/scripts/check-mutation-score-ratchet.cjs +156 -0
  434. package/scripts/ci-check-job-near-cap.cjs +49 -0
  435. package/scripts/ci-pr-mergeability.cjs +262 -0
  436. package/scripts/ci-test-scope.cjs +52 -12
  437. package/scripts/ci-timeout-report.cjs +230 -0
  438. package/scripts/docs-guard-registry.cjs +406 -0
  439. package/scripts/gen-capability-registry.cjs +8 -6
  440. package/scripts/gen-exit-code-docs.cjs +318 -0
  441. package/scripts/gen-exit-code-registry.cjs +891 -0
  442. package/scripts/gen-features.cjs +836 -0
  443. package/scripts/gen-hooks-cli-exit.cjs +239 -0
  444. package/scripts/gen-install-tree-fixtures.cjs +2 -2
  445. package/scripts/gen-loop-host-contract.cjs +189 -4
  446. package/scripts/gen-scripts-cli-exit.cjs +185 -0
  447. package/scripts/gen-state-md-docs.cjs +727 -0
  448. package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
  449. package/scripts/lib/ci-job-timing.cjs +72 -0
  450. package/scripts/lib/cli-exit.cjs +546 -44
  451. package/scripts/lib/drift-scan.cjs +32 -2
  452. package/scripts/lib/exit-code-registry.cjs +98 -0
  453. package/scripts/lib/ndjson-reporter.cjs +119 -0
  454. package/scripts/lib/shellcheck-fetch.cjs +247 -0
  455. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
  456. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  457. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  458. package/scripts/lint-docs-guard-registration.cjs +495 -0
  459. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +198 -0
  460. package/scripts/lint-eslint-glob-coverage.allowlist.json +4 -0
  461. package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
  462. package/scripts/lint-health-diagnostic-rule-table.cjs +65 -8
  463. package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
  464. package/scripts/lint-phase-enumeration-drift.cjs +45 -14
  465. package/scripts/lint-phase-id-drift.cjs +133 -8
  466. package/scripts/lint-planning-prompt-drift.cjs +38 -1
  467. package/scripts/lint-portable-grep.cjs +176 -0
  468. package/scripts/lint-removed-but-needed.cjs +184 -16
  469. package/scripts/lint-response-language-coverage.cjs +524 -0
  470. package/scripts/lint-seam-enforcement.cjs +182 -0
  471. package/scripts/lint-slug-derivation-drift.cjs +921 -0
  472. package/scripts/lint-source-test-name-collision.cjs +241 -0
  473. package/scripts/lint-state-write-path-drift.cjs +337 -432
  474. package/scripts/lint-test-file-count.allowlist.json +124 -4
  475. package/scripts/lint-test-file-count.cjs +25 -3
  476. package/scripts/lint-unreachable-guard-drift.cjs +51 -64
  477. package/scripts/lint-vendored-deps.cjs +208 -35
  478. package/scripts/lint-workflow-shellcheck-baseline.json +1027 -0
  479. package/scripts/lint-workflow-shellcheck.cjs +614 -0
  480. package/scripts/mutation-matrix.cjs +599 -50
  481. package/scripts/npm-audit-baseline.cjs +376 -0
  482. package/scripts/prompt-injection-scan.sh +83 -14
  483. package/scripts/require-issue-link-policy.cjs +16 -1
  484. package/scripts/secret-scan.sh +75 -13
  485. package/scripts/select-docs-guards.cjs +56 -0
  486. package/scripts/sync-runtime-launcher.cjs +22 -3
  487. package/skills/gsd-discuss-phase/SKILL.md +1 -1
  488. package/skills/gsd-execute-phase/SKILL.md +1 -1
  489. package/skills/gsd-import/SKILL.md +1 -1
  490. package/skills/gsd-ns-workflow/SKILL.md +1 -0
  491. package/skills/gsd-phase/SKILL.md +1 -1
  492. package/skills/gsd-quick/SKILL.md +8 -4
  493. package/skills/gsd-quick-batch/SKILL.md +105 -0
  494. package/skills/gsd-surface/SKILL.md +18 -8
  495. package/vscode/package.json +1 -1
  496. package/bin/lib/ui-safety-gate.cjs +0 -109
  497. package/scripts/lint-emitted-drift-ack.cjs +0 -344
  498. package/scripts/state-write-path-drift-baseline.json +0 -19
@@ -9,6 +9,11 @@
9
9
  */
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
11
  exports.KNOWN_STATUS_PATTERNS = exports.KNOWN_TEMPLATE_DEFAULTS = void 0;
12
+ exports.toFiniteNumber = toFiniteNumber;
13
+ exports.isUnfilledFieldValue = isUnfilledFieldValue;
14
+ exports.leadingCalendarDate = leadingCalendarDate;
15
+ exports.isRealCalendarDate = isRealCalendarDate;
16
+ exports.stateFieldContinuation = stateFieldContinuation;
12
17
  exports.stateExtractField = stateExtractField;
13
18
  exports.stateFieldValue = stateFieldValue;
14
19
  exports.stateCurrentPositionSlice = stateCurrentPositionSlice;
@@ -28,6 +33,15 @@ const pattern_cjs_1 = require("./pattern.cjs");
28
33
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- planning-scope.cjs is an export= CommonJS module
29
34
  const planningScopeMod = require("./planning-scope.cjs");
30
35
  const { SCOPE } = planningScopeMod;
36
+ /**
37
+ * Coerce an arbitrary frontmatter scalar to a finite number, or `null` if it
38
+ * is not one. Exported per ADR-3473 §8.6: `state-transition.cts`'s
39
+ * progress-ratchet unmeasured-scan check ("is this derived total a real
40
+ * measurement?") must ask through the SAME coercion this module already uses
41
+ * for `existingProgressExceedsDerived`, rather than growing a second private
42
+ * copy. This matters because frontmatter scalars arrive as STRINGS
43
+ * (`"0"`, not `0`) — a raw `=== 0` test is wrong at both call sites.
44
+ */
31
45
  function toFiniteNumber(value) {
32
46
  const number = Number(value);
33
47
  return Number.isFinite(number) ? number : null;
@@ -208,6 +222,178 @@ function locateFieldRow(content, fieldName) {
208
222
  }
209
223
  return null;
210
224
  }
225
+ /**
226
+ * True only when y/m/d name a date that actually exists on the calendar.
227
+ *
228
+ * `Date.parse` validates shape but not value: it rolls an out-of-range day
229
+ * FORWARD rather than rejecting it (`2026-02-30` -> `2026-03-02`,
230
+ * `2026-04-31` -> `2026-05-01`). Shape-only validation would therefore
231
+ * propagate a different, wrong instant instead of failing safe — precisely
232
+ * what ADR-227 ("validate shape AND value; on failure of either layer coerce
233
+ * to the contract's safe default, never propagate") exists to prevent. A
234
+ * round-trip through Date.UTC detects the rollover: any component the
235
+ * constructor normalised comes back changed.
236
+ *
237
+ * #3696: this predicate previously lived privately inside `smart-entry.cts`,
238
+ * where it gated `parseActivityTimestamp`. `state validate` needed the same
239
+ * answer to assert the `last_activity` invariant (S008), and a second copy is
240
+ * the "generative fix divergence" class outright — two surfaces that disagree
241
+ * about whether a STATE.md is usable is the defect #3696 opens with, so a
242
+ * parity test over two copies would be codifying the bug rather than fixing
243
+ * it. It moves here because this module is already the designated owner of
244
+ * STATE.md field semantics (ADR-3180 §7.7) and `smart-entry.cts` imports no
245
+ * peer that would make the reverse direction a cycle.
246
+ */
247
+ /**
248
+ * True when a field carries no value a writer ever supplied: absent, blank, or
249
+ * still holding the shipped template's bracket placeholder.
250
+ *
251
+ * `templates/state.md:35` ships `Last activity: [YYYY-MM-DD] — [What happened]`,
252
+ * so EVERY freshly-initialized project has this exact string until something
253
+ * records activity. #3696's first cut only spared the ABSENT form, which made
254
+ * S008 fire on the shipped template itself — caught by the pre-existing
255
+ * "template-equivalent phase identities remain clean without disk drift" test,
256
+ * which is precisely what it is there for.
257
+ *
258
+ * The placeholder test is anchored at the START rather than "contains a bracket
259
+ * anywhere", so a real description that happens to cite one — `2026-08-19 — fixed
260
+ * [#123] parsing` — is still a filled-in value. That keeps the rule from
261
+ * silently swallowing genuine drift.
262
+ *
263
+ * Distinct from `isStateTemplateDefault`, which answers a different question
264
+ * ("may a later handler overwrite this?") and deliberately returns true for a
265
+ * bare ISO date — a perfectly valid value here.
266
+ */
267
+ function isUnfilledFieldValue(value) {
268
+ if (value === null || value === undefined)
269
+ return true;
270
+ const trimmed = value.trim();
271
+ return trimmed === '' || trimmed.startsWith('[');
272
+ }
273
+ /**
274
+ * The `YYYY-MM-DD` prefix of `value`, but only when it names a date that
275
+ * actually exists. `null` for anything else — no leading date token at all, or
276
+ * a token that is shape-valid and calendar-impossible.
277
+ *
278
+ * #3696 review: this is deliberately a LEADING-TOKEN test, not the fully
279
+ * anchored prose grammar `parseProseLastActivityField` uses. That function
280
+ * requires the whole value to be `date` or `date <separator> description`, and
281
+ * returns `{date: <the entire raw string>}` when it does not match — a shape
282
+ * that reads like success. Asserting the S008 invariant through it therefore
283
+ * rejected values the real reader accepts: `smart-entry`'s
284
+ * `parseActivityTimestamp` needs only a leading date and reconstructs the
285
+ * instant even when the suffix carries no dash, so
286
+ * `Last activity: 2026-08-24 Shipped feature X` parses fine there while S008
287
+ * called it unreadable. That is the same two-surfaces-disagree defect #3696
288
+ * exists to close, merely pointing the other way.
289
+ *
290
+ * So the invariant asserted is the one the readers actually share: a leading
291
+ * ISO date token that is a real calendar date.
292
+ */
293
+ function leadingCalendarDate(value) {
294
+ if (!value)
295
+ return null;
296
+ const match = /^(\d{4})-(\d{2})-(\d{2})(?![\d-])/.exec(value.trim());
297
+ if (!match)
298
+ return null;
299
+ return isRealCalendarDate(Number(match[1]), Number(match[2]), Number(match[3]))
300
+ ? `${match[1]}-${match[2]}-${match[3]}`
301
+ : null;
302
+ }
303
+ function isRealCalendarDate(year, month, day) {
304
+ if (month < 1 || month > 12 || day < 1 || day > 31)
305
+ return false;
306
+ const probe = new Date(Date.UTC(year, month - 1, day));
307
+ return (probe.getUTCFullYear() === year &&
308
+ probe.getUTCMonth() === month - 1 &&
309
+ probe.getUTCDate() === day);
310
+ }
311
+ /**
312
+ * Markdown structure that can legitimately follow a single-line field. A line
313
+ * matching any of these is the NEXT construct, never a continuation of the
314
+ * field above it.
315
+ *
316
+ * BREADTH IS THE POINT, and the failure direction is deliberate: a missed
317
+ * truncation costs a diagnostic nobody sees, while a false S009 reports drift on
318
+ * a well-formed STATE.md — a gate that fires on valid documents is worse than no
319
+ * gate. When a shape is ambiguous, it belongs here.
320
+ *
321
+ * #3696 review round 2 added the last three arms after all three were shown to
322
+ * produce false S009 fires on well-formed content: an indented code block, an
323
+ * HTML block, and a setext underline (`===`, which the `[-*_]{3,}` rule does not
324
+ * cover — it only knows `-`, `*` and `_`).
325
+ */
326
+ const MD_STRUCTURE_LINE_RE = /^(?:#{1,6}\s|\||>|```|~~~|[-*_]{3,}\s*$|=+\s*$|[-*+]\s|\d+[.)]\s|\[[^\]]+\]:|<|(?: {4}|\t))/;
327
+ /**
328
+ * A setext heading's underline — `===` or `---` on its own line. The line ABOVE
329
+ * one of these is a heading TITLE, which is indistinguishable from prose on its
330
+ * own, so the scan must look ahead by one line rather than consume it. Without
331
+ * this, `Last activity: …\nMy Heading\n===` reported "My Heading ===" as dropped
332
+ * continuation text (#3696 review round 2).
333
+ */
334
+ const SETEXT_UNDERLINE_RE = /^(?:=+|-+)\s*$/;
335
+ const STATE_SIBLING_FIELD_LINE_RE = /^\*{0,2}[A-Za-z][A-Za-z0-9 _-]*\*{0,2}:{1,2}\*{0,2}(?:\s|$)/;
336
+ /**
337
+ * Return the prose that FOLLOWS a single-line field but plainly belongs to it —
338
+ * i.e. the remainder `stateExtractField` silently drops when a writer emits a
339
+ * value long enough to wrap.
340
+ *
341
+ * `stateExtractField`'s `(.+)` is newline-excluding, so
342
+ *
343
+ * Last activity: 2026-08-19 — Project initialized from ingest; PROJECT.md,
344
+ * REQUIREMENTS.md, ROADMAP.md written
345
+ *
346
+ * yields only the first line and the rest is lost with no diagnostic (#3696).
347
+ * `templates/state.md` prescribes a single-line field, so the DOCUMENT is what
348
+ * is wrong here, not the reader — this function exists so `state validate` can
349
+ * SAY so, not so the reader can start guessing at a multi-line grammar the
350
+ * template does not sanction.
351
+ *
352
+ * That is also why the fix is not in `stateExtractField` itself: it has 20
353
+ * direct callers and a CRITICAL blast radius (ADR-3180 §7.7, Rejected #1), and
354
+ * joining continuations there would apply to every field — `Status:` would
355
+ * swallow the line beneath it.
356
+ *
357
+ * Returns `null` when the field is absent, is a pipe-table row (a table cell
358
+ * cannot wrap), or is followed by end-of-file, a blank line, Markdown
359
+ * structure, or a sibling field.
360
+ */
361
+ function stateFieldContinuation(content, fieldName) {
362
+ const escaped = (0, pattern_cjs_1.escapeRegex)(fieldName);
363
+ // Same two single-line grammars stateExtractField uses, in the same order, so
364
+ // this locates exactly the line whose value it returned. The pipe-table rung
365
+ // is deliberately absent: a `| Field | value |` row is bounded by its closing
366
+ // pipe and cannot wrap.
367
+ const match = new RegExp(`\\*\\*${escaped}:\\*\\*[ \\t]*(.+)`, 'i').exec(content) ??
368
+ new RegExp(`^${escaped}:[ \\t]*(.+)`, 'im').exec(content);
369
+ if (!match)
370
+ return null;
371
+ // `(.+)` stops at the line terminator, so the field's line ends where the
372
+ // match does. JS `.` excludes \r as well as \n, so on a CRLF document the \r
373
+ // sits just AFTER the match rather than inside it — hence the strip below
374
+ // before testing for the newline.
375
+ const afterValue = match.index + match[0].length;
376
+ const rest = content.slice(afterValue).replace(/^\r/, '');
377
+ if (!rest.startsWith('\n'))
378
+ return null; // end of file: nothing follows
379
+ const lines = rest.slice(1).split('\n').map((line) => line.replace(/\r$/, ''));
380
+ const continuation = [];
381
+ for (let i = 0; i < lines.length; i++) {
382
+ const line = lines[i];
383
+ if (!line.trim())
384
+ break;
385
+ if (MD_STRUCTURE_LINE_RE.test(line))
386
+ break;
387
+ if (STATE_SIBLING_FIELD_LINE_RE.test(line))
388
+ break;
389
+ // Look ahead one line: a setext underline below makes THIS line a heading
390
+ // title, so stop before consuming it rather than after.
391
+ if (i + 1 < lines.length && SETEXT_UNDERLINE_RE.test(lines[i + 1]))
392
+ break;
393
+ continuation.push(line.trim());
394
+ }
395
+ return continuation.length ? continuation.join(' ') : null;
396
+ }
211
397
  function stateExtractField(content, fieldName) {
212
398
  const escaped = (0, pattern_cjs_1.escapeRegex)(fieldName);
213
399
  // Bold inline format: **FieldName:** value
@@ -326,17 +512,42 @@ function stateCurrentPositionSlice(body) {
326
512
  const section = (0, markdown_sectionizer_cjs_1.collectSection)(body, isCurrentPosition, { levelBounded: true });
327
513
  return section ? section.body : null;
328
514
  }
515
+ /**
516
+ * Join a matched `**Field:**`/`Field:` label prefix to its new value, inserting a
517
+ * single space when the prefix does not already end in same-line whitespace.
518
+ *
519
+ * On an empty field the same-line `[ \t]*` gap (below) captures nothing, so the
520
+ * bare `prefix + value` would glue the value to the label (`**Status:**value`);
521
+ * this inserts the missing separator. A non-empty field whose label-to-value
522
+ * separator is ordinary space/tab keeps that separator in the prefix, so `[ \t]$`
523
+ * is true and the output stays byte-identical to prior behaviour. The one
524
+ * exception is a non-empty field written with NO separator at all (a hand-edited
525
+ * `**Status:**value`): the narrowed gap captures nothing, `[ \t]$` is false, and a
526
+ * single space is inserted — an intentional normalization, not byte-identical, and
527
+ * with no GSD-template trigger. An empty new value inserts no separator, avoiding a
528
+ * dangling trailing space. See #4010.
529
+ */
530
+ function joinFieldReplacement(prefix, newValue) {
531
+ const value = `${newValue}`;
532
+ const needsSeparator = value.length > 0 && !/[ \t]$/.test(prefix);
533
+ return `${prefix}${needsSeparator ? ' ' : ''}${value}`;
534
+ }
329
535
  function stateReplaceField(content, fieldName, newValue) {
330
536
  const escaped = (0, pattern_cjs_1.escapeRegex)(fieldName);
331
537
  // Bold inline format: **FieldName:** value
332
- const boldPattern = new RegExp(`(\\*\\*${escaped}:\\*\\*\\s*)(.*)`, 'i');
538
+ // The label-to-value gap is same-line whitespace only (`[ \t]*`, mirroring the
539
+ // read side at stateExtractField). `\s*` here matched `\n`, so on an empty field
540
+ // `(.*)` captured the following line and the rebuild discarded it — the #4010
541
+ // data-loss. ADR-3180 §7.7 makes stateExtractField the same-line-confined owner;
542
+ // this aligns the writer to it.
543
+ const boldPattern = new RegExp(`(\\*\\*${escaped}:\\*\\*[ \\t]*)(.*)`, 'i');
333
544
  if (boldPattern.test(content)) {
334
- return content.replace(boldPattern, (_match, prefix) => `${prefix}${newValue}`);
545
+ return content.replace(boldPattern, (_match, prefix) => joinFieldReplacement(prefix, newValue));
335
546
  }
336
- // Plain line-start format: FieldName: value
337
- const plainPattern = new RegExp(`(^${escaped}:\\s*)(.*)`, 'im');
547
+ // Plain line-start format: FieldName: value (same same-line confinement as above)
548
+ const plainPattern = new RegExp(`(^${escaped}:[ \\t]*)(.*)`, 'im');
338
549
  if (plainPattern.test(content)) {
339
- return content.replace(plainPattern, (_match, prefix) => `${prefix}${newValue}`);
550
+ return content.replace(plainPattern, (_match, prefix) => joinFieldReplacement(prefix, newValue));
340
551
  }
341
552
  // Pipe-table format: | FieldName | value |
342
553
  // Preserve the surrounding pipe/whitespace structure; only swap the value cell.
@@ -0,0 +1,231 @@
1
+ "use strict";
2
+ /**
3
+ * STATE.md Field Schema — the one declaration (ADR-3473 §8.8, issue #3873).
4
+ *
5
+ * Phase 3 substrate. Before this module, "which STATE.md keys exist and what
6
+ * they carry" was declared in THREE hand-maintained places that were already
7
+ * observed to disagree (see the `last_activity` docstring below):
8
+ *
9
+ * - `FIELD_CLASSIFICATION` (`src/state-transition.cts`) — source/preservation/
10
+ * guard/mergeStrategy per frontmatter key (ADR-1769 §4 / ADR-3408).
11
+ * - `FRONTMATTER_BODY_SOURCE` (`src/state-transition.cts`) — which BODY field
12
+ * a frontmatter key derives from.
13
+ * - `FRONTMATTER_KEY_TO_BODY_LABEL` (`src/state.cts`) — the Title-Case label
14
+ * a report speaks a preserved field in (ADR-3408 §8.4/§8.5).
15
+ *
16
+ * This module is the single row-per-key declaration those three now PROJECT
17
+ * from at load time (`state-transition.cts` / `state.cts`), rather than
18
+ * hand-maintaining a fourth copy of the same knowledge. Every exported shape
19
+ * of the three original tables is unchanged — same keys, same key ORDER, same
20
+ * frozen/null-prototype-ness — so every existing consumer (the preservation
21
+ * dispatch loop, `getFieldClassification`, `getPreserveWhenUnchangedFields`,
22
+ * `bodyLabelFor`, and issue #3872's `declaredLeavesOf`) keeps working without
23
+ * an edit. See `.gsd/phase/feat-3873-state-md-schema/40-design.md`.
24
+ *
25
+ * LEAF MODULE, DELIBERATELY. This file imports from neither `state-transition.cts`
26
+ * nor `state.cts` — both of those import THIS module, and either importing
27
+ * back would be the exact CJS require-cycle `src/health-diagnostic-types.cts`'s
28
+ * own docstring describes breaking for the health-diagnostic rule tables
29
+ * (`module.exports` read before it is assigned, so a destructured value comes
30
+ * back `undefined`). `FieldSource` / `FieldPreservation` / `FieldGuard` /
31
+ * `FieldMergeStrategy` therefore live HERE now and are re-exported (by the same
32
+ * name, so no importer of `state-transition.cts` needs to change) from
33
+ * `state-transition.cts`.
34
+ *
35
+ * ADR-457 build-at-publish: source in `src/state-md-schema.cts`, compiled to
36
+ * `gsd-core/bin/lib/state-md-schema.cjs` (gitignored).
37
+ *
38
+ * Design: .gsd/phase/feat-3873-state-md-schema/40-design.md
39
+ * Test matrix: .gsd/phase/feat-3873-state-md-schema/50-test-matrix.md
40
+ */
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.STATE_FIELD_SCHEMA = exports.STATUS_LIFECYCLE_ENUM = void 0;
43
+ /**
44
+ * The seven CANONICAL values `normalizeStateStatus` (`src/state-document.cts`)
45
+ * maps recognized raw status prose TO — the function's default fallback plus
46
+ * each branch's literal output, in the order the function tests them. This is
47
+ * NOT the raw body prose vocabulary `CONTEXT.md`'s "STATE.md Status Lifecycle
48
+ * (ADR-2207)" entry documents (`Ready to plan` → `All phases complete` →
49
+ * `<version> milestone complete` → `Awaiting next milestone`, plus the
50
+ * handler-authored strings in `KNOWN_TEMPLATE_DEFAULTS['Status']`) — that is
51
+ * free-form prose `normalizeStateStatus` READS.
52
+ *
53
+ * CORRECTED (#3873 phase-3 test-matrix row 26 — verified by executing
54
+ * `normalizeStateStatus`, not by reading this docstring's prior claim):
55
+ * this is NOT a closed set the `status` frontmatter key is restricted to at
56
+ * runtime. `normalizeStateStatus` is deliberately LENIENT: its fallback is
57
+ * `normalizedStatus = status || 'unknown'`, and when none of its
58
+ * substring-match branches recognize the raw input, that fallback — the
59
+ * caller's raw, UNRECOGNIZED prose — is returned unchanged. A status value
60
+ * outside this seven-member set is not rejected, coerced, or normalized; it
61
+ * passes straight through into the frontmatter. `STATUS_LIFECYCLE_ENUM` is
62
+ * therefore the set of values the normalizer maps recognized input ONTO, not
63
+ * a runtime-enforced closed vocabulary for the field.
64
+ */
65
+ exports.STATUS_LIFECYCLE_ENUM = Object.freeze([
66
+ 'unknown',
67
+ 'paused',
68
+ 'executing',
69
+ 'planning',
70
+ 'discussing',
71
+ 'verifying',
72
+ 'completed',
73
+ ]);
74
+ // ─── The one declaration ────────────────────────────────────────────────────
75
+ //
76
+ // Row order below is `FIELD_CLASSIFICATION`'s (`src/state-transition.cts`,
77
+ // pre-#3873) ORIGINAL literal order, verified by direct read and preserved
78
+ // deliberately: the `FIELD_CLASSIFICATION` projection built from this table
79
+ // (`state-transition.cts`) walks `Object.keys(STATE_FIELD_SCHEMA)` directly,
80
+ // so this row order IS that projection's key order, and key order is
81
+ // observable (the preservation dispatch loop iterates it). The two other
82
+ // projections (`FRONTMATTER_BODY_SOURCE`, `FRONTMATTER_KEY_TO_BODY_LABEL`) do
83
+ // NOT reuse this same order — their pre-#3873 literals were independently
84
+ // hand-written and already disagreed with each other and with this order (see
85
+ // each projection's own ordering constant in its home module) — so each
86
+ // projection module declares its OWN explicit key-order list rather than
87
+ // re-deriving order from this table's iteration, which would silently change
88
+ // two of the three tables' observable order out from under every consumer.
89
+ exports.STATE_FIELD_SCHEMA = Object.freeze(Object.assign(Object.create(null), {
90
+ // Schema
91
+ gsd_state_version: {
92
+ type: 'string', cardinality: 'one', source: 'free', preservation: 'derive', emitted: 'always',
93
+ },
94
+ // Milestone (external — from ROADMAP.md)
95
+ milestone: {
96
+ type: 'string', cardinality: 'optional', source: 'external', preservation: 'preserve-if-placeholder', emitted: 'when-present',
97
+ },
98
+ milestone_name: {
99
+ type: 'string', cardinality: 'optional', source: 'external', preservation: 'preserve-if-placeholder', emitted: 'when-present',
100
+ },
101
+ // Phase / plan position (body-derived)
102
+ current_phase: {
103
+ type: 'string', cardinality: 'optional', source: 'body', preservation: 'preserve-when-unchanged',
104
+ bodySource: Object.freeze(['Current Phase']), bodyLabel: 'Current Phase', emitted: 'when-present',
105
+ },
106
+ current_phase_name: {
107
+ type: 'string', cardinality: 'optional', source: 'curated', preservation: 'preserve-when-unchanged',
108
+ bodySource: Object.freeze(['Current Phase Name']), bodyLabel: 'Current Phase Name', emitted: 'when-present',
109
+ },
110
+ current_plan: {
111
+ type: 'string', cardinality: 'optional', source: 'body', preservation: 'preserve-when-unchanged',
112
+ bodySource: Object.freeze(['Current Plan']), bodyLabel: 'Current Plan',
113
+ // #3873 phase-3 test-matrix row 25 (verified by executing
114
+ // `advancePlanCore`, `src/state-transition.cts:1306`, not by reading
115
+ // its docstring): TODAY the `Current Plan` body field parses in
116
+ // exactly ONE shape — a bare number `N`, paired with a separate
117
+ // `Total Plans in Phase` field. The hybrid compound `N of M` written
118
+ // directly into `Current Plan` (no `Total Plans in Phase` sibling)
119
+ // does NOT parse: `legacyTotal` is absent, `planField` reads the
120
+ // DIFFERENT `Plan` field (also absent), so the function falls to its
121
+ // NaN/NaN error branch. Feeding `Current Plan: 2 of 5` WITH a
122
+ // `Total Plans in Phase` sibling present does not change this — it
123
+ // "succeeds" only because `parseInt("2 of 5", 10)` truncates to `2`
124
+ // and the sibling supplies the total; the `of 5` half is silently
125
+ // discarded, which is `parseInt` coincidence, not shape recognition.
126
+ // `Plan: N of M` (a DIFFERENT field name) DOES parse the hybrid shape,
127
+ // but `buildStateFrontmatter` never reads `Plan` into `current_plan`
128
+ // (verified: it calls `stateExtractField(bodyContent, 'Current Plan')`
129
+ // only), so that shape is out of scope for this row regardless.
130
+ //
131
+ // #3784/#3791 WIDENED THIS ROW. The paragraph above describes the
132
+ // pre-#3791 parser and is kept as the record of what the shape was
133
+ // before, because row 25 exists to stop exactly that reading from
134
+ // being re-asserted by accident.
135
+ //
136
+ // `advancePlanCore` now accepts the hybrid shape, so the declared set
137
+ // is `['N', 'N of M']`. Two properties of the widening matter to a
138
+ // future reader:
139
+ //
140
+ // - It is ANCHORED. The parser matches `/^(\d+)\s+of\s+(\d+)\s*$/`
141
+ // against the whole value, so `4 — blocked on review of 2 PRs`
142
+ // is REJECTED rather than yielding a total of 2 out of prose.
143
+ // Declaring `'N of M'` is a claim about that grammar, not about
144
+ // "contains the word of".
145
+ // - `'N/M'` stays UNDECLARED and must keep failing. Row 23 probes
146
+ // the undeclared remainder of `SHAPE_EXAMPLES`, so it needs at
147
+ // least one member outside the declared set to stay non-vacuous.
148
+ //
149
+ // Widening this row without widening the parser (or the reverse) goes
150
+ // RED on rows 23/24/25. That coupling is the forcing function, and it
151
+ // is the reason this row is data rather than a predicate: §8.8's
152
+ // "parsers are checked, not generated".
153
+ acceptedShapes: Object.freeze(['N', 'N of M']),
154
+ emitted: 'when-present',
155
+ },
156
+ // Status / lifecycle (body-derived; #1230 delta heuristic applies)
157
+ // guard: the 'unknown' sentinel is the ONLY true executor-side guard in
158
+ // this table (stopped_at's `## Session` scoping is caller-side delta
159
+ // extraction, not an executor condition) — ADR-3408 Decision 1.
160
+ status: {
161
+ type: 'string', enum: exports.STATUS_LIFECYCLE_ENUM, cardinality: 'one', source: 'body', preservation: 'preserve-when-unchanged',
162
+ guard: 'non-sentinel-unknown', bodySource: Object.freeze(['Status']), bodyLabel: 'Status', emitted: 'always',
163
+ },
164
+ stopped_at: {
165
+ type: 'string', cardinality: 'optional', source: 'body', preservation: 'preserve-when-unchanged',
166
+ bodySource: Object.freeze(['Stopped At', 'Stopped at']), bodyLabel: 'Stopped At', emitted: 'when-present',
167
+ },
168
+ paused_at: {
169
+ type: 'string', cardinality: 'optional', source: 'body', preservation: 'preserve-when-unchanged',
170
+ bodySource: Object.freeze(['Paused At']), bodyLabel: 'Paused At', emitted: 'when-present',
171
+ },
172
+ // Activity log
173
+ last_updated: {
174
+ type: 'string', cardinality: 'one', source: 'free', preservation: 'derive', emitted: 'always',
175
+ }, // realClock.nowIso()
176
+ // #3873: THE LIVE DISAGREEMENT. Pre-schema, `FRONTMATTER_BODY_SOURCE`
177
+ // carried this key (`last_activity: ['Last Activity', 'Last activity']`)
178
+ // while `FRONTMATTER_KEY_TO_BODY_LABEL` did NOT — same field, two
179
+ // tables, two different answers to "does this key have a reportable
180
+ // body label". Resolved by DECLARATION, not by picking whichever table
181
+ // "looks right": `bodySource` is present below (this key IS derived from
182
+ // a body field and `buildStateFrontmatter` — `src/state.cts` — reads it
183
+ // via that exact two-case-variant fallback), and `bodyLabel` is
184
+ // deliberately ABSENT, because that is what ships TODAY —
185
+ // `last_activity`'s `preservation` is `'derive'`, never
186
+ // `'preserve-when-unchanged'`, so it can never reach `bodyLabelFor`'s
187
+ // (`src/state.cts`) `STATE_BODY_LABEL_UNWIRED_ROW` throw in the first
188
+ // place; the absent label is inert, not a latent bug. Pinned by
189
+ // `tests/state.test.cjs`'s pre-existing
190
+ // `lastActivityLabelResolutionMatchesShippedBehavior`. Do NOT "tidy"
191
+ // this by adding a label — that would be shipping a policy change
192
+ // disguised as a consolidation, exactly the #3427 failure this epic is
193
+ // named after.
194
+ last_activity: {
195
+ type: 'string', cardinality: 'optional', source: 'body', preservation: 'derive',
196
+ bodySource: Object.freeze(['Last Activity', 'Last activity']), emitted: 'when-present',
197
+ }, // always refresh on transition
198
+ last_activity_desc: {
199
+ type: 'string', cardinality: 'optional', source: 'body', preservation: 'preserve-when-unchanged',
200
+ bodySource: Object.freeze(['Last Activity Description']), bodyLabel: 'Last Activity Description', emitted: 'when-present',
201
+ },
202
+ // Commit provenance (#2573) — ambient git read, recomputed on every write,
203
+ // exactly like last_updated. Never preserved: a stale stamp would claim
204
+ // STATE.md was written against a commit it wasn't.
205
+ state_head: {
206
+ type: 'string', cardinality: 'optional', source: 'free', preservation: 'derive', emitted: 'when-present',
207
+ }, // #2573
208
+ // Progress block (disk-derived, except the curated progress ratchet)
209
+ // mergeStrategy: 'progress-ratchet' — completed_plans/completed_phases
210
+ // only ever ratchet UP toward the derived value (#2969); everything
211
+ // else in the merge is either always-derived (#2440) or always-curated.
212
+ progress: {
213
+ type: 'object', cardinality: 'optional', source: 'curated', preservation: 'preserve-always',
214
+ mergeStrategy: 'progress-ratchet', emitted: 'when-present',
215
+ }, // #3242, #1446
216
+ 'progress.total_phases': {
217
+ type: 'number', cardinality: 'optional', source: 'disk', preservation: 'derive', emitted: 'when-present',
218
+ },
219
+ 'progress.completed_phases': {
220
+ type: 'number', cardinality: 'optional', source: 'disk', preservation: 'derive', emitted: 'when-present',
221
+ },
222
+ 'progress.total_plans': {
223
+ type: 'number', cardinality: 'optional', source: 'disk', preservation: 'derive', emitted: 'when-present',
224
+ },
225
+ 'progress.completed_plans': {
226
+ type: 'number', cardinality: 'optional', source: 'disk', preservation: 'derive', emitted: 'when-present',
227
+ },
228
+ 'progress.percent': {
229
+ type: 'number', cardinality: 'optional', source: 'disk', preservation: 'derive', emitted: 'when-present',
230
+ },
231
+ }));