@opengsd/gsd-core 1.10.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 (544) 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 +12 -1
  5. package/agents/gsd-debugger.md +1 -1
  6. package/agents/gsd-doc-synthesizer.md +2 -4
  7. package/agents/gsd-dom-verifier.md +169 -0
  8. package/agents/gsd-eval-auditor.md +1 -1
  9. package/agents/gsd-executor.md +22 -14
  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 +5 -3
  13. package/agents/gsd-pattern-mapper.md +11 -0
  14. package/agents/gsd-phase-researcher.md +23 -2
  15. package/agents/gsd-plan-checker.md +50 -53
  16. package/agents/gsd-planner.md +50 -50
  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 +63 -4
  21. package/agents/gsd-ui-researcher.md +41 -3
  22. package/agents/gsd-user-profiler.md +3 -0
  23. package/agents/gsd-verifier.md +13 -4
  24. package/bin/install.js +1448 -1103
  25. package/commands/gsd/code-review.md +1 -1
  26. package/commands/gsd/discuss-phase.md +1 -1
  27. package/commands/gsd/execute-phase.md +1 -1
  28. package/commands/gsd/import.md +1 -1
  29. package/commands/gsd/map-codebase.md +1 -1
  30. package/commands/gsd/mempalace-capture.md +1 -1
  31. package/commands/gsd/mempalace-recall.md +1 -1
  32. package/commands/gsd/new-milestone.md +1 -1
  33. package/commands/gsd/quick.md +9 -5
  34. package/commands/gsd/review-backlog.md +2 -1
  35. package/commands/gsd/verify-work.md +1 -1
  36. package/gsd-core/bin/gsd-tools.cjs +1035 -138
  37. package/gsd-core/bin/lib/active-workstream-store.cjs +146 -22
  38. package/gsd-core/bin/lib/adr-parser.cjs +13 -7
  39. package/gsd-core/bin/lib/agent-install-check.cjs +392 -32
  40. package/gsd-core/bin/lib/api-coverage.cjs +33 -14
  41. package/gsd-core/bin/lib/artifacts.cjs +5 -0
  42. package/gsd-core/bin/lib/assumption-delta.cjs +32 -15
  43. package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
  44. package/gsd-core/bin/lib/audit.cjs +1026 -268
  45. package/gsd-core/bin/lib/broken-windows.cjs +306 -28
  46. package/gsd-core/bin/lib/capability-consent.cjs +149 -15
  47. package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
  48. package/gsd-core/bin/lib/capability-lock.cjs +10 -4
  49. package/gsd-core/bin/lib/capability-registry.cjs +845 -130
  50. package/gsd-core/bin/lib/capability-source.cjs +92 -0
  51. package/gsd-core/bin/lib/capability-state.cjs +18 -3
  52. package/gsd-core/bin/lib/capability-trust.cjs +444 -25
  53. package/gsd-core/bin/lib/capability-validator.cjs +700 -40
  54. package/gsd-core/bin/lib/capability-writer.cjs +3 -2
  55. package/gsd-core/bin/lib/check-command-router.cjs +216 -42
  56. package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
  57. package/gsd-core/bin/lib/cli-exit.cjs +496 -10
  58. package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
  59. package/gsd-core/bin/lib/codex-agent-toml.cjs +735 -0
  60. package/gsd-core/bin/lib/command-aliases.cjs +22 -0
  61. package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
  62. package/gsd-core/bin/lib/command-roster.cjs +44 -1
  63. package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
  64. package/gsd-core/bin/lib/commands.cjs +1172 -108
  65. package/gsd-core/bin/lib/commonjs-marker.cjs +12 -6
  66. package/gsd-core/bin/lib/complexity-trigger.cjs +1192 -0
  67. package/gsd-core/bin/lib/config-loader.cjs +187 -23
  68. package/gsd-core/bin/lib/config.cjs +102 -3
  69. package/gsd-core/bin/lib/configuration.cjs +129 -37
  70. package/gsd-core/bin/lib/core-utils.cjs +208 -33
  71. package/gsd-core/bin/lib/decisions.cjs +23 -0
  72. package/gsd-core/bin/lib/edge-probe.cjs +9 -1
  73. package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
  74. package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
  75. package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
  76. package/gsd-core/bin/lib/frontmatter.cjs +899 -229
  77. package/gsd-core/bin/lib/gap-checker.cjs +95 -10
  78. package/gsd-core/bin/lib/git-base-branch.cjs +276 -39
  79. package/gsd-core/bin/lib/gsd2-import.cjs +10 -1
  80. package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
  81. package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
  82. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +149 -0
  83. package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
  84. package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
  85. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
  86. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +268 -0
  87. package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
  88. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
  89. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +187 -0
  90. package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
  91. package/gsd-core/bin/lib/health-diagnostic.cjs +451 -0
  92. package/gsd-core/bin/lib/host-integration.cjs +39 -6
  93. package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
  94. package/gsd-core/bin/lib/init-command-router.cjs +118 -21
  95. package/gsd-core/bin/lib/init.cjs +439 -168
  96. package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
  97. package/gsd-core/bin/lib/install-engine.cjs +811 -259
  98. package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
  99. package/gsd-core/bin/lib/install-model-override-resolver.cjs +235 -0
  100. package/gsd-core/bin/lib/install-profiles.cjs +212 -61
  101. package/gsd-core/bin/lib/install-scope.cjs +270 -0
  102. package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
  103. package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
  104. package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
  105. package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
  106. package/gsd-core/bin/lib/installer-migrations.cjs +148 -38
  107. package/gsd-core/bin/lib/intel.cjs +101 -26
  108. package/gsd-core/bin/lib/io.cjs +170 -15
  109. package/gsd-core/bin/lib/learnings.cjs +85 -14
  110. package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
  111. package/gsd-core/bin/lib/markdown-sectionizer.cjs +2 -1
  112. package/gsd-core/bin/lib/markdown-table.cjs +183 -22
  113. package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
  114. package/gsd-core/bin/lib/milestone.cjs +842 -73
  115. package/gsd-core/bin/lib/model-catalog.cjs +232 -16
  116. package/gsd-core/bin/lib/model-resolver.cjs +193 -68
  117. package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
  118. package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
  119. package/gsd-core/bin/lib/pattern.cjs +122 -0
  120. package/gsd-core/bin/lib/phase-estimation.cjs +18 -9
  121. package/gsd-core/bin/lib/phase-id.cjs +514 -40
  122. package/gsd-core/bin/lib/phase-lifecycle.cjs +52 -19
  123. package/gsd-core/bin/lib/phase-locator.cjs +262 -34
  124. package/gsd-core/bin/lib/phase.cjs +1038 -214
  125. package/gsd-core/bin/lib/plan-dependency-graph.cjs +72 -1
  126. package/gsd-core/bin/lib/plan-document.cjs +263 -0
  127. package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
  128. package/gsd-core/bin/lib/plan-scan.cjs +98 -3
  129. package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
  130. package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
  131. package/gsd-core/bin/lib/planning-scope.cjs +31 -0
  132. package/gsd-core/bin/lib/planning-snapshot.cjs +894 -0
  133. package/gsd-core/bin/lib/planning-workspace.cjs +112 -6
  134. package/gsd-core/bin/lib/probe-core.cjs +5 -2
  135. package/gsd-core/bin/lib/profile-output.cjs +1 -1
  136. package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
  137. package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
  138. package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
  139. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +766 -0
  140. package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +11 -6
  141. package/gsd-core/bin/lib/review-lane-descriptor.cjs +22 -13
  142. package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
  143. package/gsd-core/bin/lib/review-lane-runner.cjs +421 -66
  144. package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
  145. package/gsd-core/bin/lib/roadmap-command-router.cjs +59 -11
  146. package/gsd-core/bin/lib/roadmap-parser.cjs +1006 -184
  147. package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
  148. package/gsd-core/bin/lib/roadmap.cjs +442 -96
  149. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +702 -52
  150. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
  151. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +459 -55
  152. package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
  153. package/gsd-core/bin/lib/runtime-homes.cjs +69 -3
  154. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +402 -58
  155. package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
  156. package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
  157. package/gsd-core/bin/lib/runtime-slash.cjs +96 -8
  158. package/gsd-core/bin/lib/security.cjs +104 -5
  159. package/gsd-core/bin/lib/shell-command-projection.cjs +342 -7
  160. package/gsd-core/bin/lib/smart-entry.cjs +133 -23
  161. package/gsd-core/bin/lib/spec-section.cjs +12 -7
  162. package/gsd-core/bin/lib/state-command-router.cjs +52 -19
  163. package/gsd-core/bin/lib/state-contract.cjs +359 -0
  164. package/gsd-core/bin/lib/state-document.cjs +338 -8
  165. package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
  166. package/gsd-core/bin/lib/state-transition.cjs +846 -176
  167. package/gsd-core/bin/lib/state.cjs +2589 -369
  168. package/gsd-core/bin/lib/surface.cjs +33 -11
  169. package/gsd-core/bin/lib/task-command-router.cjs +111 -1
  170. package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
  171. package/gsd-core/bin/lib/teams-status.cjs +4 -1
  172. package/gsd-core/bin/lib/text-lines.cjs +80 -0
  173. package/gsd-core/bin/lib/token-scanner.cjs +76 -0
  174. package/gsd-core/bin/lib/uat-predicate.cjs +67 -23
  175. package/gsd-core/bin/lib/uat.cjs +1761 -167
  176. package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
  177. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
  178. package/gsd-core/bin/lib/ui-safety-gate.cjs +51 -12
  179. package/gsd-core/bin/lib/unusable-input.cjs +37 -0
  180. package/gsd-core/bin/lib/update-context.cjs +8 -2
  181. package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
  182. package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
  183. package/gsd-core/bin/lib/validate.cjs +20 -6
  184. package/gsd-core/bin/lib/vendor/README.md +75 -0
  185. package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
  186. package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
  187. package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
  188. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  189. package/gsd-core/bin/lib/verification.cjs +272 -9
  190. package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
  191. package/gsd-core/bin/lib/verify.cjs +453 -918
  192. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +53 -32
  193. package/gsd-core/bin/lib/workstream-inventory.cjs +63 -10
  194. package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
  195. package/gsd-core/bin/lib/workstream.cjs +2 -2
  196. package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
  197. package/gsd-core/bin/lib/worktree-safety.cjs +341 -18
  198. package/gsd-core/bin/shared/config-defaults.manifest.json +8 -1
  199. package/gsd-core/bin/shared/config-schema.manifest.json +12 -1
  200. package/gsd-core/bin/shared/exit-codes.json +8 -0
  201. package/gsd-core/bin/shared/exit-codes.sh +20 -0
  202. package/gsd-core/bin/shared/model-catalog.json +8 -1
  203. package/gsd-core/references/agent-contracts.md +44 -26
  204. package/gsd-core/references/api-coverage.md +24 -2
  205. package/gsd-core/references/autonomous-smart-discuss.md +3 -3
  206. package/gsd-core/references/checkpoints.md +39 -21
  207. package/gsd-core/references/context-budget.md +1 -1
  208. package/gsd-core/references/decimal-phase-calculation.md +5 -5
  209. package/gsd-core/references/dispatch-isolation-gate.md +138 -0
  210. package/gsd-core/references/doc-conflict-engine.md +1 -1
  211. package/gsd-core/references/edge-probe.md +8 -0
  212. package/gsd-core/references/execute-mvp-tdd.md +4 -6
  213. package/gsd-core/references/execute-phase-between-wave-reset.md +15 -14
  214. package/gsd-core/references/execute-phase-context-guard.md +1 -1
  215. package/gsd-core/references/execute-phase-response-language.md +1 -1
  216. package/gsd-core/references/execute-phase-wave-guard.md +17 -11
  217. package/gsd-core/references/failing-direction.md +78 -0
  218. package/gsd-core/references/gate-prompts.md +1 -1
  219. package/gsd-core/references/git-integration.md +5 -5
  220. package/gsd-core/references/git-planning-commit.md +5 -4
  221. package/gsd-core/references/gsd-run-resolver.md +1 -1
  222. package/gsd-core/references/loop-hook-dispatch.md +61 -2
  223. package/gsd-core/references/model-profiles.md +12 -4
  224. package/gsd-core/references/mvp-concepts.md +9 -9
  225. package/gsd-core/references/nyquist-compliance.md +74 -0
  226. package/gsd-core/references/offer-next.md +3 -5
  227. package/gsd-core/references/phase-argument-parsing.md +3 -3
  228. package/gsd-core/references/planner-failing-direction.md +53 -0
  229. package/gsd-core/references/planner-guidance.md +3 -9
  230. package/gsd-core/references/planner-human-verify-mode.md +15 -1
  231. package/gsd-core/references/planner-preconditions.md +1 -1
  232. package/gsd-core/references/planner-reviews.md +1 -1
  233. package/gsd-core/references/planner-revision.md +1 -1
  234. package/gsd-core/references/planner-verify-command-grounding.md +17 -0
  235. package/gsd-core/references/planning-config.md +44 -13
  236. package/gsd-core/references/reviewer-instances.md +31 -0
  237. package/gsd-core/references/revision-loop.md +1 -1
  238. package/gsd-core/references/runtime-aware-dispatch.md +1 -1
  239. package/gsd-core/references/specless-probe-fallback.md +1 -1
  240. package/gsd-core/references/tdd.md +1 -3
  241. package/gsd-core/references/ui-brand.md +65 -21
  242. package/gsd-core/references/ui-consideration-probe.md +1 -1
  243. package/gsd-core/references/universal-anti-patterns.md +5 -5
  244. package/gsd-core/references/verifier-phase-gates.md +192 -0
  245. package/gsd-core/references/verify-command-path-resolvability.md +42 -0
  246. package/gsd-core/references/verify-mvp-mode.md +2 -2
  247. package/gsd-core/references/workstream-flag.md +33 -17
  248. package/gsd-core/templates/README.md +1 -1
  249. package/gsd-core/templates/SECURITY.md +3 -3
  250. package/gsd-core/templates/UI-SPEC.md +25 -3
  251. package/gsd-core/templates/VALIDATION.md +3 -3
  252. package/gsd-core/templates/discussion-log.md +1 -1
  253. package/gsd-core/templates/phase-prompt.md +5 -4
  254. package/gsd-core/templates/state.md +11 -4
  255. package/gsd-core/templates/verification-report.md +9 -1
  256. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  257. package/gsd-core/workflows/add-backlog.md +1 -1
  258. package/gsd-core/workflows/add-phase.md +3 -3
  259. package/gsd-core/workflows/add-tests.md +3 -8
  260. package/gsd-core/workflows/add-todo.md +1 -1
  261. package/gsd-core/workflows/ai-integration-phase.md +13 -20
  262. package/gsd-core/workflows/audit-fix.md +12 -3
  263. package/gsd-core/workflows/audit-milestone.md +9 -9
  264. package/gsd-core/workflows/audit-uat.md +17 -2
  265. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
  266. package/gsd-core/workflows/autonomous.md +11 -27
  267. package/gsd-core/workflows/check-todos.md +1 -1
  268. package/gsd-core/workflows/cleanup.md +64 -5
  269. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +14 -4
  270. package/gsd-core/workflows/code-review-fix.md +38 -11
  271. package/gsd-core/workflows/code-review.md +159 -52
  272. package/gsd-core/workflows/complete-milestone.md +151 -23
  273. package/gsd-core/workflows/debug.md +12 -8
  274. package/gsd-core/workflows/diagnose-issues.md +47 -15
  275. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  276. package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -8
  277. package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
  278. package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
  279. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
  280. package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
  281. package/gsd-core/workflows/discuss-phase.md +1 -1
  282. package/gsd-core/workflows/do.md +3 -6
  283. package/gsd-core/workflows/docs-update.md +5 -4
  284. package/gsd-core/workflows/edit-phase.md +27 -2
  285. package/gsd-core/workflows/eval-review.md +7 -14
  286. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
  287. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +142 -15
  288. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
  289. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
  290. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
  291. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +24 -4
  292. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
  293. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
  294. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
  295. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
  296. package/gsd-core/workflows/execute-phase.md +72 -100
  297. package/gsd-core/workflows/execute-plan.md +52 -15
  298. package/gsd-core/workflows/explore.md +131 -4
  299. package/gsd-core/workflows/extract-learnings.md +1 -1
  300. package/gsd-core/workflows/fast.md +10 -2
  301. package/gsd-core/workflows/forensics.md +1 -1
  302. package/gsd-core/workflows/graduation.md +5 -5
  303. package/gsd-core/workflows/health.md +76 -10
  304. package/gsd-core/workflows/import.md +18 -15
  305. package/gsd-core/workflows/inbox.md +4 -5
  306. package/gsd-core/workflows/ingest-docs.md +49 -16
  307. package/gsd-core/workflows/insert-phase.md +5 -5
  308. package/gsd-core/workflows/list-seeds.md +5 -3
  309. package/gsd-core/workflows/list-workspaces.md +1 -1
  310. package/gsd-core/workflows/manager.md +12 -23
  311. package/gsd-core/workflows/map-codebase.md +1 -1
  312. package/gsd-core/workflows/milestone-summary.md +1 -1
  313. package/gsd-core/workflows/mvp-phase.md +8 -5
  314. package/gsd-core/workflows/new-milestone.md +22 -29
  315. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  316. package/gsd-core/workflows/new-project.md +26 -40
  317. package/gsd-core/workflows/new-workspace.md +1 -1
  318. package/gsd-core/workflows/next.md +14 -2
  319. package/gsd-core/workflows/pause-work.md +1 -1
  320. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
  321. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
  322. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
  323. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
  324. package/gsd-core/workflows/plan-phase.md +162 -59
  325. package/gsd-core/workflows/plan-review-convergence.md +96 -11
  326. package/gsd-core/workflows/plant-seed.md +2 -2
  327. package/gsd-core/workflows/pr-branch.md +187 -51
  328. package/gsd-core/workflows/profile-user.md +16 -14
  329. package/gsd-core/workflows/progress.md +61 -18
  330. package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
  331. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +5 -7
  332. package/gsd-core/workflows/quick/steps/quick-verification.md +28 -9
  333. package/gsd-core/workflows/quick/steps/research-phase.md +4 -6
  334. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
  335. package/gsd-core/workflows/quick.md +55 -44
  336. package/gsd-core/workflows/remove-phase.md +4 -4
  337. package/gsd-core/workflows/remove-workspace.md +2 -2
  338. package/gsd-core/workflows/resume-project.md +8 -12
  339. package/gsd-core/workflows/review.md +219 -20
  340. package/gsd-core/workflows/scan.md +1 -1
  341. package/gsd-core/workflows/secure-phase.md +3 -3
  342. package/gsd-core/workflows/session-report.md +2 -1
  343. package/gsd-core/workflows/settings-advanced.md +7 -9
  344. package/gsd-core/workflows/settings-integrations.md +64 -31
  345. package/gsd-core/workflows/settings.md +69 -7
  346. package/gsd-core/workflows/ship.md +116 -50
  347. package/gsd-core/workflows/sketch-wrap-up.md +11 -17
  348. package/gsd-core/workflows/sketch.md +12 -18
  349. package/gsd-core/workflows/smart-entry.md +3 -5
  350. package/gsd-core/workflows/spec-phase.md +53 -13
  351. package/gsd-core/workflows/spike-wrap-up.md +7 -11
  352. package/gsd-core/workflows/spike.md +20 -31
  353. package/gsd-core/workflows/stats.md +2 -2
  354. package/gsd-core/workflows/sync-skills.md +64 -9
  355. package/gsd-core/workflows/thread.md +11 -7
  356. package/gsd-core/workflows/transition.md +49 -14
  357. package/gsd-core/workflows/ui-phase.md +15 -21
  358. package/gsd-core/workflows/ui-review.md +8 -12
  359. package/gsd-core/workflows/ultraplan-phase.md +5 -13
  360. package/gsd-core/workflows/undo.md +8 -16
  361. package/gsd-core/workflows/update.md +7 -11
  362. package/gsd-core/workflows/validate-phase.md +3 -3
  363. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
  364. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
  365. package/gsd-core/workflows/verify-work.md +66 -25
  366. package/hooks/dist/gsd-agent-isolation-guard.js +158 -30
  367. package/hooks/dist/gsd-check-update-worker.js +56 -13
  368. package/hooks/dist/gsd-check-update.js +19 -1
  369. package/hooks/dist/gsd-config-reload.js +18 -12
  370. package/hooks/dist/gsd-context-monitor.js +19 -10
  371. package/hooks/dist/gsd-cursor-post-tool.js +3 -1
  372. package/hooks/dist/gsd-cursor-pre-tool.js +2 -3
  373. package/hooks/dist/gsd-cursor-session-start.js +2 -1
  374. package/hooks/dist/gsd-cursor-stop.js +2 -1
  375. package/hooks/dist/gsd-cursor-subagent-start.js +83 -3
  376. package/hooks/dist/gsd-cursor-subagent-stop.js +6 -3
  377. package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
  378. package/hooks/dist/gsd-graphify-update.sh +22 -18
  379. package/hooks/dist/gsd-node-runner.sh +76 -0
  380. package/hooks/dist/gsd-phase-boundary.sh +1 -0
  381. package/hooks/dist/gsd-prompt-guard.js +37 -27
  382. package/hooks/dist/gsd-read-guard.js +16 -7
  383. package/hooks/dist/gsd-read-injection-scanner.js +55 -32
  384. package/hooks/dist/gsd-session-state.sh +1 -0
  385. package/hooks/dist/gsd-statusline.js +231 -24
  386. package/hooks/dist/gsd-update-banner.js +22 -1
  387. package/hooks/dist/gsd-validate-commit.sh +80 -6
  388. package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
  389. package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
  390. package/hooks/dist/gsd-workflow-guard.js +162 -46
  391. package/hooks/dist/gsd-worktree-path-guard.js +36 -21
  392. package/hooks/dist/gsd-write-guard.js +35 -25
  393. package/hooks/dist/lib/cli-exit.js +560 -0
  394. package/hooks/dist/lib/exit-code-registry.js +98 -0
  395. package/hooks/dist/lib/git-cmd.js +92 -59
  396. package/hooks/dist/lib/git-probe.js +84 -0
  397. package/hooks/dist/lib/hook-exit.js +81 -0
  398. package/hooks/dist/lib/injection-patterns.js +45 -0
  399. package/hooks/dist/lib/isolation-deny-reason.js +39 -0
  400. package/hooks/dist/lib/isolation-sentinel.js +9 -0
  401. package/hooks/dist/managed-hooks-registry.cjs +3 -0
  402. package/hooks/gsd-agent-isolation-guard.js +158 -30
  403. package/hooks/gsd-check-update-worker.js +56 -13
  404. package/hooks/gsd-check-update.js +19 -1
  405. package/hooks/gsd-config-reload.js +18 -12
  406. package/hooks/gsd-context-monitor.js +19 -10
  407. package/hooks/gsd-cursor-post-tool.js +3 -1
  408. package/hooks/gsd-cursor-pre-tool.js +2 -3
  409. package/hooks/gsd-cursor-session-start.js +2 -1
  410. package/hooks/gsd-cursor-stop.js +2 -1
  411. package/hooks/gsd-cursor-subagent-start.js +83 -3
  412. package/hooks/gsd-cursor-subagent-stop.js +6 -3
  413. package/hooks/gsd-ensure-canonical-path.js +2 -1
  414. package/hooks/gsd-graphify-update.sh +22 -18
  415. package/hooks/gsd-node-runner.sh +76 -0
  416. package/hooks/gsd-phase-boundary.sh +1 -0
  417. package/hooks/gsd-prompt-guard.js +37 -27
  418. package/hooks/gsd-read-guard.js +16 -7
  419. package/hooks/gsd-read-injection-scanner.js +55 -32
  420. package/hooks/gsd-session-state.sh +1 -0
  421. package/hooks/gsd-statusline.js +231 -24
  422. package/hooks/gsd-update-banner.js +22 -1
  423. package/hooks/gsd-validate-commit.sh +80 -6
  424. package/hooks/gsd-windsurf-pre-command.js +16 -11
  425. package/hooks/gsd-windsurf-pre-write.js +22 -13
  426. package/hooks/gsd-workflow-guard.js +162 -46
  427. package/hooks/gsd-worktree-path-guard.js +36 -21
  428. package/hooks/gsd-write-guard.js +35 -25
  429. package/hooks/lib/cli-exit.js +560 -0
  430. package/hooks/lib/exit-code-registry.js +98 -0
  431. package/hooks/lib/git-cmd.js +92 -59
  432. package/hooks/lib/git-probe.js +84 -0
  433. package/hooks/lib/hook-exit.js +81 -0
  434. package/hooks/lib/injection-patterns.js +45 -0
  435. package/hooks/lib/isolation-deny-reason.js +39 -0
  436. package/hooks/lib/isolation-sentinel.js +9 -0
  437. package/hooks/managed-hooks-registry.cjs +3 -0
  438. package/package.json +28 -11
  439. package/pi/gsd.cjs +19 -5
  440. package/scripts/base64-scan.sh +74 -12
  441. package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
  442. package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
  443. package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
  444. package/scripts/build-hooks.js +5 -0
  445. package/scripts/changeset/lint.cjs +60 -5
  446. package/scripts/check-alias-drift.cjs +7 -43
  447. package/scripts/check-contract-drift.cjs +297 -0
  448. package/scripts/check-glossary-refs.cjs +77 -15
  449. package/scripts/check-mutation-score-ratchet.cjs +156 -0
  450. package/scripts/ci-check-job-near-cap.cjs +49 -0
  451. package/scripts/ci-pr-mergeability.cjs +262 -0
  452. package/scripts/ci-test-scope.cjs +64 -14
  453. package/scripts/ci-timeout-report.cjs +230 -0
  454. package/scripts/command-contract-helpers.cjs +903 -1
  455. package/scripts/docs-guard-registry.cjs +396 -0
  456. package/scripts/gen-adr-index.cjs +728 -38
  457. package/scripts/gen-capability-registry.cjs +11 -21
  458. package/scripts/gen-context-index.cjs +2 -11
  459. package/scripts/gen-exit-code-docs.cjs +318 -0
  460. package/scripts/gen-exit-code-registry.cjs +891 -0
  461. package/scripts/gen-features.cjs +836 -0
  462. package/scripts/gen-health-docs.cjs +390 -0
  463. package/scripts/gen-hooks-cli-exit.cjs +239 -0
  464. package/scripts/gen-install-tree-fixtures.cjs +2 -2
  465. package/scripts/gen-inventory-manifest.cjs +50 -4
  466. package/scripts/gen-loop-host-contract.cjs +138 -25
  467. package/scripts/gen-registry.cjs +3 -14
  468. package/scripts/gen-scripts-cli-exit.cjs +185 -0
  469. package/scripts/gen-state-md-docs.cjs +727 -0
  470. package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
  471. package/scripts/lib/alias-drift-families.cjs +46 -0
  472. package/scripts/lib/ci-job-timing.cjs +72 -0
  473. package/scripts/lib/cli-exit.cjs +546 -44
  474. package/scripts/lib/drift-scan.cjs +308 -0
  475. package/scripts/lib/exit-code-registry.cjs +98 -0
  476. package/scripts/lib/ndjson-reporter.cjs +119 -0
  477. package/scripts/lint-allow-test-rule-refs.allowlist.json +1 -26
  478. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
  479. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
  480. package/scripts/lint-canary-version-leak.cjs +73 -0
  481. package/scripts/lint-command-contract.cjs +96 -13
  482. package/scripts/lint-completion-predicate-drift.cjs +933 -0
  483. package/scripts/lint-completion-ratio-drift.cjs +214 -0
  484. package/scripts/lint-default-flip-documentation.cjs +193 -0
  485. package/scripts/lint-docs-guard-registration.cjs +495 -0
  486. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
  487. package/scripts/lint-eslint-glob-coverage.allowlist.json +38 -0
  488. package/scripts/lint-eslint-glob-coverage.cjs +340 -0
  489. package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
  490. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
  491. package/scripts/lint-health-diagnostic-rule-table.cjs +461 -0
  492. package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
  493. package/scripts/lint-milestone-window-drift.cjs +468 -0
  494. package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
  495. package/scripts/lint-phase-enumeration-drift.cjs +492 -0
  496. package/scripts/lint-plan-count-drift.cjs +318 -0
  497. package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
  498. package/scripts/lint-planning-prompt-drift.cjs +471 -0
  499. package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
  500. package/scripts/lint-regression-test-names.cjs +15 -13
  501. package/scripts/lint-removed-but-needed.cjs +488 -0
  502. package/scripts/lint-seam-enforcement.cjs +182 -0
  503. package/scripts/lint-slug-derivation-drift.cjs +921 -0
  504. package/scripts/lint-source-test-name-collision.cjs +241 -0
  505. package/scripts/lint-state-field-drift.cjs +805 -0
  506. package/scripts/lint-state-write-path-drift.cjs +950 -0
  507. package/scripts/lint-test-file-count.allowlist.json +137 -8
  508. package/scripts/lint-test-file-count.cjs +25 -3
  509. package/scripts/lint-unreachable-guard-drift.cjs +830 -0
  510. package/scripts/lint-vendored-deps.cjs +297 -0
  511. package/scripts/mutation-matrix.cjs +599 -50
  512. package/scripts/pr-changed-files.cjs +63 -0
  513. package/scripts/pr-template-policy.cjs +14 -4
  514. package/scripts/prompt-injection-scan.sh +100 -14
  515. package/scripts/require-issue-link-policy.cjs +192 -0
  516. package/scripts/secret-scan.sh +75 -13
  517. package/scripts/select-docs-guards.cjs +56 -0
  518. package/scripts/sync-runtime-launcher.cjs +24 -7
  519. package/skills/gsd-autonomous/SKILL.md +0 -1
  520. package/skills/gsd-code-review/SKILL.md +1 -1
  521. package/skills/gsd-discuss-phase/SKILL.md +1 -1
  522. package/skills/gsd-execute-phase/SKILL.md +1 -2
  523. package/skills/gsd-import/SKILL.md +1 -1
  524. package/skills/gsd-map-codebase/SKILL.md +1 -1
  525. package/skills/gsd-mempalace-capture/SKILL.md +1 -1
  526. package/skills/gsd-mempalace-recall/SKILL.md +1 -1
  527. package/skills/gsd-new-milestone/SKILL.md +1 -1
  528. package/skills/gsd-next/SKILL.md +0 -1
  529. package/skills/gsd-plan-phase/SKILL.md +0 -1
  530. package/skills/gsd-progress/SKILL.md +0 -1
  531. package/skills/gsd-quick/SKILL.md +9 -5
  532. package/skills/gsd-review-backlog/SKILL.md +2 -1
  533. package/skills/gsd-stats/SKILL.md +0 -1
  534. package/skills/gsd-verify-work/SKILL.md +1 -1
  535. package/vscode/package.json +1 -1
  536. package/bin/lib/ui-safety-gate.cjs +0 -107
  537. package/gsd-core/workflows/discovery-phase.md +0 -298
  538. package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
  539. package/gsd-core/workflows/verify-phase.md +0 -574
  540. package/scripts/affected-tests-lib.cjs +0 -554
  541. package/scripts/lint-allow-test-rule-refs.cjs +0 -162
  542. package/scripts/lint-emitted-drift-ack.cjs +0 -344
  543. package/scripts/run-affected-tests.cjs +0 -7
  544. package/scripts/run-tests.cjs +0 -1051
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Anti-divergence drift guard for the completion-RATIO seam
6
+ * (epic #3180, ADR-3180 "Planning Semantic Model Single Owner").
7
+ *
8
+ * `src/phase-lifecycle.cts`'s `clampPercent(completed, total)` /
9
+ * `clampPercentFromFraction(fraction)` are the SINGLE canonical owner of
10
+ * "turn a completed/total pair into an integer completion percentage,
11
+ * clamped to 100". Until just before this guard was added, the identical
12
+ * expression `total > 0 ? Math.min(100, Math.round((completed / total) * 100)) : 0`
13
+ * was hand-inlined at six call sites across five modules while the owner sat
14
+ * exported and unused by them — the exact ADR-3180 divergence class, in a
15
+ * derivation the epic had not previously named. Those six sites have been
16
+ * migrated onto the owner; this guard is what stops a seventh copy.
17
+ *
18
+ * Per ADR-3180 Decision 4(a) this guard discovers call sites by SCANNING THE
19
+ * WHOLE `src/` TREE, not by consulting an allowlist of known files — an
20
+ * allowlist only measures re-derivations in files someone remembered to
21
+ * list, and a new call site added anywhere else would sail through silently.
22
+ *
23
+ * DETECTION. A line is a re-derivation when ALL THREE hold, on that ONE
24
+ * source line:
25
+ * (a) it calls one of the `Math.round(`/`Math.floor(`/`Math.trunc(`/
26
+ * `Math.ceil(` rounding family — MATH_ROUND_FAMILY_RE;
27
+ * (b) it SCALES by 100 — a `*` followed by optional whitespace then `100`
28
+ * at a word boundary — SCALE_100_RE;
29
+ * (c) it contains a DIVISION — an identifier/closing-bracket, optional
30
+ * whitespace, `/`, optional whitespace, an identifier/opening-paren —
31
+ * DIVISION_RE — AND that division's index in the line is EARLIER than
32
+ * the index of the `* 100` scale from (b).
33
+ *
34
+ * Clause (c)'s ORDERING requirement is the whole precision of this guard.
35
+ * `(a / b) * 100` — divide FIRST, scale SECOND — is a percentage: the
36
+ * completed/total-derived shape this guard exists to catch. `Math.round(n *
37
+ * 100) / 100` — scale FIRST, divide SECOND — is a completely unrelated
38
+ * idiom (2-decimal-place rounding of an already-fractional value) that
39
+ * happens to share both a rounding call and a `* 100` token; it appears in
40
+ * this repo at `src/eval.cts` and `src/commands.cts` and MUST stay
41
+ * unflagged. Comparing leftmost-match indices (rather than merely testing
42
+ * "does a division exist anywhere on the line") is what tells the two
43
+ * idioms apart: this guard finds the EARLIEST division and the EARLIEST
44
+ * `* 100` scale on the line and requires divIdx < scaleIdx, so a line with a
45
+ * scale-then-divide shape (divIdx > scaleIdx, or no division at all) never
46
+ * matches, regardless of what else is on the line.
47
+ *
48
+ * `Math.floor(Math.random() * 100)` carries (a) and (b) but no division
49
+ * anywhere on the line (DIVISION_RE finds nothing, divIdx === -1) and is
50
+ * correctly excluded by clause (c) alone.
51
+ *
52
+ * `src/context-utilization.cts`'s `Math.min(Math.round(ratio * 100), 100)`
53
+ * is OUT OF SCOPE BY DOMAIN, not by exemption: it scales an
54
+ * ALREADY-COMPUTED fraction (`ratio`, a context-window utilization figure —
55
+ * unrelated to `.planning/` phase/plan completion) and there is no division
56
+ * anywhere on that line either, so clause (c) excludes it the same way as
57
+ * the `Math.random()` case above; it needs no FUNCTION_SCOPED_EXEMPTIONS
58
+ * entry because it was never going to match.
59
+ *
60
+ * Every regex below is small, bounded, and has no nested/overlapping
61
+ * quantifiers — each character class is followed by a fixed literal or a
62
+ * single `\s*` run bounded by the next required literal, so there is
63
+ * nothing for a backtracking engine to explore more than linearly.
64
+ * `npm run lint:ci` runs CodeQL js/redos over this repo; mirrors the
65
+ * ReDoS discipline of `lint-plan-count-drift.cjs` / `lint-milestone-window-drift.cjs`.
66
+ *
67
+ * The tree-walk / root-confinement / sanitizer machinery is SHARED with the
68
+ * sibling drift guards via `scripts/lib/drift-scan.cjs` (ADR-3180 Decision 4)
69
+ * — see that module for the `isInsideRoot` case-sensitivity note and the
70
+ * `walk` symlink-confinement rationale. This guard's detection shape needs
71
+ * no regex-LITERAL extraction (unlike the milestone-window guard), so it
72
+ * does not use `readRegexLiteralAt`; the reported fragment is simply the
73
+ * trimmed source line, bounded to MAX_REGEX_LITERAL_LEN characters.
74
+ *
75
+ * KNOWN, ACCEPTED limits of a per-line textual scan (same tradeoff the
76
+ * sibling drift guards document): a re-derivation whose division and
77
+ * `Math.round`/scale are split across two DIFFERENT lines with no single
78
+ * line carrying all three tokens is not caught by this narrow shape, nor is
79
+ * one routed through a helper that itself performs the division one call
80
+ * away from the rounding. That is left to code review, not this regex.
81
+ */
82
+
83
+ const path = require('node:path');
84
+ const driftScan = require('./lib/drift-scan.cjs');
85
+ const { MAX_REGEX_LITERAL_LEN, sanitizeForReport, scanTree } = driftScan;
86
+
87
+ // (a) The `Math.round`/`Math.floor`/`Math.trunc`/`Math.ceil` rounding family,
88
+ // called with an open paren. `\b` before `Math` keeps this from matching
89
+ // inside a longer identifier (e.g. `fooMath.round(` never occurs in this
90
+ // codebase, but the boundary costs nothing and documents intent).
91
+ const MATH_ROUND_FAMILY_RE = /\bMath\.(?:round|floor|trunc|ceil)\(/;
92
+
93
+ // (b) A `* 100` scale — a `*` operator, optional whitespace, then the
94
+ // literal digits `100` at a word boundary (so `*1000` or `*100.5` do not
95
+ // match a bare `100` inside a longer number).
96
+ const SCALE_100_RE = /\*\s*100\b/;
97
+
98
+ // (c) A division: an identifier character/closing-bracket (the end of the
99
+ // numerator expression), optional whitespace, `/`, optional whitespace, an
100
+ // identifier character/opening-paren (the start of the denominator
101
+ // expression). Deliberately does not try to distinguish this from a regex
102
+ // literal or a `//` comment — the detection window is a Math.round-family
103
+ // call on the same line, which neither idiom co-occurs with in practice, and
104
+ // keeping the class small is what keeps the regex non-backtracking.
105
+ const DIVISION_RE = /[A-Za-z0-9_$)\]]\s*\/\s*[A-Za-z0-9_$(]/;
106
+
107
+ // Authored TypeScript source only (the generated bin/lib/*.cjs mirror it).
108
+ const SCAN_DIRS = ['src'];
109
+ const SCAN_EXT = new Set(['.cts', '.ts', '.mts']);
110
+
111
+ // The canonical owner defines the ratio-to-percent grammar. It is NOT
112
+ // exempt as a whole file (ADR-3180 Decision 4(a) forbids bare file
113
+ // allowlists) — it is scanned like every other file in SCAN_DIRS, and only
114
+ // the two named functions below are exempt, each for a documented reason.
115
+ // An unrelated re-derivation added elsewhere in this same file (including a
116
+ // future one) is still caught.
117
+ const OWNER_FILE = path.join('src', 'phase-lifecycle.cts');
118
+
119
+ // Per ADR-3180 Decision 4(a): function-scoped, not a bare file allowlist.
120
+ // - clampPercentFromFraction: `Math.min(100, Math.round(fraction * 100))`
121
+ // IS the canonical fraction-to-percent kernel this guard exists to
122
+ // protect, not a copy of it — every other caller in the tree is
123
+ // expected to CALL this function rather than re-express its body.
124
+ // - clampPercent: the canonical count-shaped entry point; it delegates to
125
+ // `clampPercentFromFraction(completed / total)` rather than computing
126
+ // `Math.round(...)` itself, so it is exempted for the same reason even
127
+ // though its own line does not currently carry a Math.round-family call.
128
+ const FUNCTION_SCOPED_EXEMPTIONS = new Map([[OWNER_FILE, new Set(['clampPercent', 'clampPercentFromFraction'])]]);
129
+
130
+ // Optional `export ` modifier, matching the sibling guards' convention —
131
+ // only a column-0 top-level `function` declaration updates the
132
+ // current-function tracker.
133
+ const TOP_LEVEL_FUNCTION_RE = /^(?:export\s+)?function\s+([A-Za-z0-9_]+)\s*\(/;
134
+
135
+ /**
136
+ * Pure: find every unsanctioned completion-ratio re-derivation in `text`.
137
+ * `relPath` is the repo-relative path, used both to report file:line and to
138
+ * apply the narrow, function-scoped owner exemptions above.
139
+ * Returns [{ line, found }].
140
+ */
141
+ function findCompletionRatioDrift(text, relPath) {
142
+ const out = [];
143
+ const lines = text.split('\n');
144
+ const exemptFunctions = FUNCTION_SCOPED_EXEMPTIONS.get(relPath) || null;
145
+ let currentFunction = null;
146
+ for (let i = 0; i < lines.length; i++) {
147
+ const line = lines[i];
148
+ const fnMatch = TOP_LEVEL_FUNCTION_RE.exec(line);
149
+ if (fnMatch) currentFunction = fnMatch[1];
150
+
151
+ if (!MATH_ROUND_FAMILY_RE.test(line)) continue;
152
+ const scaleIdx = line.search(SCALE_100_RE);
153
+ if (scaleIdx === -1) continue;
154
+ const divIdx = line.search(DIVISION_RE);
155
+ if (divIdx === -1 || divIdx >= scaleIdx) continue;
156
+
157
+ if (exemptFunctions && exemptFunctions.has(currentFunction)) continue;
158
+
159
+ out.push({ line: i + 1, found: line.trim().slice(0, MAX_REGEX_LITERAL_LEN) });
160
+ }
161
+ return out;
162
+ }
163
+
164
+ /**
165
+ * Scan the authored source tree and return every unsanctioned re-derivation,
166
+ * each annotated with the repo-relative file path.
167
+ */
168
+ function scanRepo(root) {
169
+ return scanTree({
170
+ root,
171
+ scanDirs: SCAN_DIRS,
172
+ scanExt: SCAN_EXT,
173
+ onFile(rel, text) {
174
+ // `rel` is already the REAL (canonical) path (scanTree resolves
175
+ // symlinks before calling onFile), so this — and
176
+ // FUNCTION_SCOPED_EXEMPTIONS above, also keyed on `rel` — match
177
+ // consistently regardless of which symlink reached the file.
178
+ return findCompletionRatioDrift(text, rel).map((d) => ({ file: rel, ...d }));
179
+ },
180
+ });
181
+ }
182
+
183
+ function main() {
184
+ const root = path.join(__dirname, '..');
185
+ const violations = scanRepo(root);
186
+ if (violations.length === 0) {
187
+ process.stdout.write('ok completion-ratio-drift: no unsanctioned completed/total percent re-derivations outside phase-lifecycle.cts\n');
188
+ return;
189
+ }
190
+ process.stderr.write('completion-ratio-drift: independent re-derivation(s) of completed/total percent found.\n');
191
+ process.stderr.write('Use src/phase-lifecycle.cjs `clampPercent(completed, total)` (or `clampPercentFromFraction(fraction)`\n');
192
+ process.stderr.write('when you already hold a fraction) instead of re-deriving Math.round((completed / total) * 100):\n');
193
+ for (const d of violations) {
194
+ // `d.file` is exactly as attacker-controlled as `d.found`: a repo can
195
+ // legally track a filename containing control bytes / bidi overrides,
196
+ // and it is a fork-PR-authored value reaching a CI log the same way the
197
+ // matched line text does — sanitize it at the same reporting boundary.
198
+ process.stderr.write(` ${sanitizeForReport(d.file)}:${d.line} ${sanitizeForReport(d.found)}\n`);
199
+ }
200
+ process.exitCode = 1;
201
+ }
202
+
203
+ if (require.main === module) main();
204
+
205
+ module.exports = {
206
+ findCompletionRatioDrift,
207
+ scanRepo,
208
+ MATH_ROUND_FAMILY_RE,
209
+ SCALE_100_RE,
210
+ DIVISION_RE,
211
+ OWNER_FILE,
212
+ FUNCTION_SCOPED_EXEMPTIONS,
213
+ MAX_REGEX_LITERAL_LEN,
214
+ };
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * lint-default-flip-documentation.cjs — DEFECT.DEFAULT-FLIP-DOCUMENTATION
6
+ * (CONTEXT.md).
7
+ *
8
+ * ## Why
9
+ *
10
+ * A PR flips a config default but doesn't call out the migration semantics
11
+ * (when the new default takes effect; existing configs vs new configs; what
12
+ * the opt-back-in looks like — #3309, the v2 default flip from mid-flight to
13
+ * end-of-phase).
14
+ *
15
+ * ## Scope (deliberately narrower than the full DEFECT.detect clause)
16
+ *
17
+ * The DEFECT text names two surfaces: `CONFIG_DEFAULTS` and
18
+ * `buildNewProjectConfig`. This check covers ONLY the single-source-of-truth
19
+ * defaults manifest, `gsd-core/bin/shared/config-defaults.manifest.json`
20
+ * (what `CONFIG_DEFAULTS` in `src/configuration.cts` / `src/config.cts`
21
+ * actually loads at runtime) — because it is pure JSON, a resolved
22
+ * key→value-map diff between base and head is trivially reliable: no line
23
+ * movement, reordering, or refactor can ever produce a false "value changed"
24
+ * verdict, only an actual value change can.
25
+ *
26
+ * `buildNewProjectConfig`'s `hardcoded` object literal in `src/config.cts`
27
+ * is DELIBERATELY OUT OF SCOPE here. It mixes literal values with
28
+ * environment-derived branches (`hasBraveSearch`, etc.) and spreads of
29
+ * `CONFIG_DEFAULTS.*` — there is no reliable way to compute its *resolved*
30
+ * value map from source text alone without executing the compiled module at
31
+ * both refs, and a line/AST-level diff of that literal would inherit exactly
32
+ * the false-positive risk (a harmless refactor that moves or restructures
33
+ * the literal reads as a "flip") this check exists to avoid. Per the audit's
34
+ * own risk callout, a noisy check here is worse than no check — the
35
+ * `buildNewProjectConfig` half of the DEFECT stays prose-only.
36
+ *
37
+ * ## What this checks
38
+ *
39
+ * If any *value* differs between the base and head resolved manifest
40
+ * key→value maps (additions/removals alone don't count as a "flip" — the
41
+ * symptom is specifically about an EXISTING default changing), fail unless
42
+ * the PR body contains a `## Breaking Changes` (or `# Breaking Changes`)
43
+ * heading.
44
+ *
45
+ * Needs a PR event payload (`GITHUB_EVENT_PATH`) to read the PR body — this
46
+ * is a dedicated-workflow check (like `lint-canary-version-leak.cjs`), not a
47
+ * `lint:ci` member, since a local/push run has no PR body to check against.
48
+ */
49
+
50
+ const fs = require('node:fs');
51
+ const path = require('node:path');
52
+ const cp = require('node:child_process');
53
+ const { ExitError, runMain } = require('./lib/cli-exit.cjs');
54
+
55
+ const ROOT = path.join(__dirname, '..');
56
+ const MANIFEST_PATH = path.join('gsd-core', 'bin', 'shared', 'config-defaults.manifest.json');
57
+ const BREAKING_CHANGES_RE = /^#{1,6}\s*Breaking Changes\b/im;
58
+
59
+ /**
60
+ * Pure: flatten a nested plain-object JSON value into dot-path
61
+ * `{ "a.b.c": value }` leaves. Arrays and primitives are leaves (compared by
62
+ * JSON.stringify equality, never recursed into) so array reordering reads as
63
+ * one value change, not N.
64
+ * @param {unknown} value
65
+ * @param {string} prefix
66
+ * @param {Record<string, unknown>} out
67
+ * @returns {Record<string, unknown>}
68
+ */
69
+ function flatten(value, prefix = '', out = {}) {
70
+ if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
71
+ for (const [key, v] of Object.entries(value)) {
72
+ flatten(v, prefix ? `${prefix}.${key}` : key, out);
73
+ }
74
+ } else {
75
+ out[prefix] = value;
76
+ }
77
+ return out;
78
+ }
79
+
80
+ /**
81
+ * Pure: given two resolved (already-flattened) key→value maps, return the
82
+ * keys present in BOTH whose value differs. Additions/removals are NOT
83
+ * "flips" — a brand-new default has no prior behavior to contradict.
84
+ * @param {Record<string, unknown>} baseMap
85
+ * @param {Record<string, unknown>} headMap
86
+ * @returns {{ key: string, from: unknown, to: unknown }[]}
87
+ */
88
+ function findDefaultValueChanges(baseMap, headMap) {
89
+ const changes = [];
90
+ for (const key of Object.keys(baseMap)) {
91
+ if (!Object.prototype.hasOwnProperty.call(headMap, key)) continue;
92
+ if (JSON.stringify(baseMap[key]) !== JSON.stringify(headMap[key])) {
93
+ changes.push({ key, from: baseMap[key], to: headMap[key] });
94
+ }
95
+ }
96
+ return changes;
97
+ }
98
+
99
+ /**
100
+ * Pure verdict: given the detected default-value changes and the PR body,
101
+ * decide pass/fail.
102
+ * @param {{ key: string, from: unknown, to: unknown }[]} changes
103
+ * @param {string} prBody
104
+ * @returns {{ ok: boolean, changes: object[] }}
105
+ */
106
+ function evaluateDefaultFlipDoc(changes, prBody) {
107
+ if (changes.length === 0) return { ok: true, changes: [] };
108
+ if (BREAKING_CHANGES_RE.test(prBody || '')) return { ok: true, changes };
109
+ return { ok: false, changes };
110
+ }
111
+
112
+ /**
113
+ * Read and JSON.parse the manifest at a given git ref. Returns `{}` when the
114
+ * file doesn't exist at that ref (new file, or ref predates it) — that is
115
+ * not a "flip", it's an addition, and is silently excluded by
116
+ * findDefaultValueChanges's both-sides-present requirement anyway.
117
+ * @param {string} root
118
+ * @param {string} ref
119
+ * @returns {Record<string, unknown>}
120
+ */
121
+ function readManifestAtRef(root, ref) {
122
+ let raw;
123
+ try {
124
+ raw = cp.execFileSync('git', ['show', `${ref}:${MANIFEST_PATH.split(path.sep).join('/')}`], {
125
+ cwd: root,
126
+ encoding: 'utf8',
127
+ timeout: 15000,
128
+ });
129
+ } catch {
130
+ return {};
131
+ }
132
+ try {
133
+ return JSON.parse(raw);
134
+ } catch (e) {
135
+ throw new ExitError(2, `lint-default-flip-documentation: ${ref}:${MANIFEST_PATH} is not valid JSON: ${e.message}`);
136
+ }
137
+ }
138
+
139
+ function readPrBody() {
140
+ const eventPath = process.env.GITHUB_EVENT_PATH;
141
+ if (!eventPath || !fs.existsSync(eventPath)) return null;
142
+ try {
143
+ const event = JSON.parse(fs.readFileSync(eventPath, 'utf8'));
144
+ return typeof event.pull_request?.body === 'string' ? event.pull_request.body : '';
145
+ } catch {
146
+ return null;
147
+ }
148
+ }
149
+
150
+ function main() {
151
+ const prBody = readPrBody();
152
+ if (prBody === null) {
153
+ console.log('lint-default-flip-documentation: no PR event payload (not a pull_request run), skipping');
154
+ return;
155
+ }
156
+
157
+ const baseRef = `origin/${process.env.GITHUB_BASE_REF || 'next'}`; // #2988
158
+ const baseMap = flatten(readManifestAtRef(ROOT, baseRef));
159
+ const headMap = flatten(readManifestAtRef(ROOT, 'HEAD'));
160
+ const changes = findDefaultValueChanges(baseMap, headMap);
161
+ const verdict = evaluateDefaultFlipDoc(changes, prBody);
162
+
163
+ if (!verdict.ok) {
164
+ const detail = verdict.changes
165
+ .map((c) => ` ${c.key}: ${JSON.stringify(c.from)} → ${JSON.stringify(c.to)}`)
166
+ .join('\n');
167
+ throw new ExitError(
168
+ 1,
169
+ 'lint-default-flip-documentation: this PR changes an existing default value in\n'
170
+ + 'config-defaults.manifest.json (DEFECT.DEFAULT-FLIP-DOCUMENTATION) but the PR body has no\n'
171
+ + '`## Breaking Changes` section. Add one covering: (a) when the new default takes effect\n'
172
+ + '(config-set, fresh project, regenerated config), (b) the opt-back-in command\n'
173
+ + '(`gsd config-set <key> <old-value>`), (c) effect on in-flight artifacts. Changed default(s):\n'
174
+ + detail,
175
+ );
176
+ }
177
+ console.log(
178
+ changes.length === 0
179
+ ? 'ok lint-default-flip-documentation: no default value changed'
180
+ : `ok lint-default-flip-documentation: ${changes.length} default value change(s), PR body documents Breaking Changes`,
181
+ );
182
+ }
183
+
184
+ module.exports = {
185
+ flatten,
186
+ findDefaultValueChanges,
187
+ evaluateDefaultFlipDoc,
188
+ readManifestAtRef,
189
+ MANIFEST_PATH,
190
+ BREAKING_CHANGES_RE,
191
+ };
192
+
193
+ if (require.main === module) runMain(main);