@opengsd/gsd-core 1.12.0 → 1.14.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 (455) 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-advisor-researcher.compact.md +85 -0
  5. package/agents/gsd-ai-researcher.compact.md +96 -0
  6. package/agents/gsd-assumptions-analyzer.compact.md +81 -0
  7. package/agents/gsd-code-fixer.compact.md +458 -0
  8. package/agents/gsd-code-fixer.md +5 -5
  9. package/agents/gsd-code-reviewer.compact.md +269 -0
  10. package/agents/gsd-code-reviewer.md +15 -3
  11. package/agents/gsd-codebase-mapper.compact.md +760 -0
  12. package/agents/gsd-debug-session-manager.compact.md +345 -0
  13. package/agents/gsd-doc-classifier.compact.md +192 -0
  14. package/agents/gsd-doc-synthesizer.compact.md +200 -0
  15. package/agents/gsd-doc-verifier.compact.md +143 -0
  16. package/agents/gsd-doc-writer.compact.md +440 -0
  17. package/agents/gsd-dom-verifier.compact.md +138 -0
  18. package/agents/gsd-domain-researcher.compact.md +141 -0
  19. package/agents/gsd-eval-auditor.compact.md +160 -0
  20. package/agents/gsd-eval-planner.compact.md +137 -0
  21. package/agents/gsd-executor.md +63 -35
  22. package/agents/gsd-framework-selector.compact.md +82 -0
  23. package/agents/gsd-integration-checker.compact.md +245 -0
  24. package/agents/gsd-intel-updater.compact.md +226 -0
  25. package/agents/gsd-mempalace-curator.compact.md +45 -0
  26. package/agents/gsd-nyquist-auditor.compact.md +179 -0
  27. package/agents/gsd-pattern-mapper.compact.md +275 -0
  28. package/agents/gsd-plan-checker.md +76 -57
  29. package/agents/gsd-planner.md +14 -0
  30. package/agents/gsd-project-researcher.compact.md +587 -0
  31. package/agents/gsd-research-synthesizer.compact.md +212 -0
  32. package/agents/gsd-roadmapper.compact.md +454 -0
  33. package/agents/gsd-roadmapper.md +13 -0
  34. package/agents/gsd-security-auditor.compact.md +162 -0
  35. package/agents/gsd-ui-auditor.compact.md +404 -0
  36. package/agents/gsd-ui-checker.compact.md +277 -0
  37. package/agents/gsd-ui-checker.md +19 -3
  38. package/agents/gsd-ui-researcher.compact.md +282 -0
  39. package/agents/gsd-ui-researcher.md +29 -0
  40. package/agents/gsd-user-profiler.compact.md +108 -0
  41. package/agents/gsd-verifier.md +23 -1
  42. package/bin/install.js +444 -134
  43. package/commands/gsd/cleanup.md +1 -0
  44. package/commands/gsd/code-review.md +2 -1
  45. package/commands/gsd/complete-milestone.md +1 -0
  46. package/commands/gsd/config.md +1 -0
  47. package/commands/gsd/debug.md +1 -0
  48. package/commands/gsd/execute-phase.md +1 -1
  49. package/commands/gsd/graphify.md +1 -0
  50. package/commands/gsd/health.md +1 -0
  51. package/commands/gsd/mempalace-capture.md +1 -0
  52. package/commands/gsd/mempalace-recall.md +1 -0
  53. package/commands/gsd/new-milestone.md +1 -0
  54. package/commands/gsd/new-project.md +1 -0
  55. package/commands/gsd/next.md +1 -0
  56. package/commands/gsd/ns-workflow.md +2 -1
  57. package/commands/gsd/pause-work.md +1 -0
  58. package/commands/gsd/phase.md +2 -1
  59. package/commands/gsd/pr-branch.md +1 -0
  60. package/commands/gsd/quick-batch.md +105 -0
  61. package/commands/gsd/resume-work.md +1 -0
  62. package/commands/gsd/review-backlog.md +1 -0
  63. package/commands/gsd/settings.md +2 -1
  64. package/commands/gsd/stats.md +1 -0
  65. package/commands/gsd/surface.md +18 -8
  66. package/commands/gsd/thread.md +1 -0
  67. package/commands/gsd/workspace.md +1 -0
  68. package/commands/gsd/workstreams.md +1 -0
  69. package/gsd-core/bin/check-latest-version.cjs +8 -3
  70. package/gsd-core/bin/gsd-tools.cjs +532 -174
  71. package/gsd-core/bin/lib/adr-parser.cjs +1 -1
  72. package/gsd-core/bin/lib/artifacts.cjs +2 -1
  73. package/gsd-core/bin/lib/audit.cjs +39 -22
  74. package/gsd-core/bin/lib/broken-windows.cjs +168 -49
  75. package/gsd-core/bin/lib/capability-activation.cjs +27 -0
  76. package/gsd-core/bin/lib/capability-lifecycle.cjs +10 -6
  77. package/gsd-core/bin/lib/capability-loader.cjs +135 -1
  78. package/gsd-core/bin/lib/capability-registry.cjs +528 -116
  79. package/gsd-core/bin/lib/capability-source.cjs +19 -2
  80. package/gsd-core/bin/lib/capability-state.cjs +7 -1
  81. package/gsd-core/bin/lib/capability-validator.cjs +134 -5
  82. package/gsd-core/bin/lib/capability-writer.cjs +14 -4
  83. package/gsd-core/bin/lib/check-command-router.cjs +198 -38
  84. package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
  85. package/gsd-core/bin/lib/clusters.cjs +1 -0
  86. package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
  87. package/gsd-core/bin/lib/command-aliases.cjs +16 -0
  88. package/gsd-core/bin/lib/commands.cjs +981 -79
  89. package/gsd-core/bin/lib/config-loader.cjs +4 -0
  90. package/gsd-core/bin/lib/config.cjs +153 -38
  91. package/gsd-core/bin/lib/core-utils.cjs +34 -7
  92. package/gsd-core/bin/lib/coverage.cjs +1 -1
  93. package/gsd-core/bin/lib/decisions.cjs +343 -28
  94. package/gsd-core/bin/lib/edge-probe.cjs +14 -1
  95. package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
  96. package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
  97. package/gsd-core/bin/lib/frontmatter.cjs +137 -23
  98. package/gsd-core/bin/lib/gap-checker.cjs +22 -13
  99. package/gsd-core/bin/lib/git-base-branch.cjs +10 -2
  100. package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
  101. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
  102. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +54 -11
  103. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +87 -23
  104. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
  105. package/gsd-core/bin/lib/host-integration.cjs +57 -5
  106. package/gsd-core/bin/lib/init-command-router.cjs +14 -0
  107. package/gsd-core/bin/lib/init.cjs +539 -60
  108. package/gsd-core/bin/lib/install-engine.cjs +199 -14
  109. package/gsd-core/bin/lib/install-model-override-resolver.cjs +45 -0
  110. package/gsd-core/bin/lib/install-profiles.cjs +36 -14
  111. package/gsd-core/bin/lib/installer-migration-report.cjs +1 -0
  112. package/gsd-core/bin/lib/installer-migrations.cjs +33 -4
  113. package/gsd-core/bin/lib/io.cjs +35 -0
  114. package/gsd-core/bin/lib/loop-resolver.cjs +64 -39
  115. package/gsd-core/bin/lib/markdown-table.cjs +123 -0
  116. package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
  117. package/gsd-core/bin/lib/milestone.cjs +41 -10
  118. package/gsd-core/bin/lib/model-resolver.cjs +101 -10
  119. package/gsd-core/bin/lib/phase-command-router.cjs +20 -7
  120. package/gsd-core/bin/lib/phase-id.cjs +412 -31
  121. package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
  122. package/gsd-core/bin/lib/phase.cjs +941 -98
  123. package/gsd-core/bin/lib/plan-document.cjs +10 -0
  124. package/gsd-core/bin/lib/planning-inspect.cjs +34 -18
  125. package/gsd-core/bin/lib/planning-snapshot.cjs +206 -30
  126. package/gsd-core/bin/lib/planning-workspace.cjs +153 -29
  127. package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
  128. package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
  129. package/gsd-core/bin/lib/quick-batch-command-router.cjs +285 -0
  130. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
  131. package/gsd-core/bin/lib/quick-batch.cjs +840 -0
  132. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +61 -2
  133. package/gsd-core/bin/lib/research-store.cjs +11 -12
  134. package/gsd-core/bin/lib/review-lane-descriptor.cjs +53 -5
  135. package/gsd-core/bin/lib/review-lane-invocation.cjs +96 -1
  136. package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
  137. package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
  138. package/gsd-core/bin/lib/roadmap-parser.cjs +555 -41
  139. package/gsd-core/bin/lib/roadmap.cjs +292 -69
  140. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +260 -43
  141. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +28 -20
  142. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +294 -108
  143. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +408 -47
  144. package/gsd-core/bin/lib/security.cjs +126 -7
  145. package/gsd-core/bin/lib/shell-command-projection.cjs +4 -0
  146. package/gsd-core/bin/lib/smart-entry.cjs +7 -9
  147. package/gsd-core/bin/lib/state-document.cjs +159 -32
  148. package/gsd-core/bin/lib/state-md-schema.cjs +44 -27
  149. package/gsd-core/bin/lib/state-transition.cjs +465 -62
  150. package/gsd-core/bin/lib/state.cjs +906 -151
  151. package/gsd-core/bin/lib/surface.cjs +83 -10
  152. package/gsd-core/bin/lib/task-command-router.cjs +12 -6
  153. package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
  154. package/gsd-core/bin/lib/uat.cjs +1420 -516
  155. package/gsd-core/bin/lib/update-context.cjs +36 -26
  156. package/gsd-core/bin/lib/validate.cjs +230 -12
  157. package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
  158. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  159. package/gsd-core/bin/lib/verification.cjs +316 -23
  160. package/gsd-core/bin/lib/verify-command-grounding.cjs +1 -1
  161. package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
  162. package/gsd-core/bin/lib/verify.cjs +531 -36
  163. package/gsd-core/bin/lib/workstream-inventory.cjs +21 -2
  164. package/gsd-core/bin/lib/worktree-safety.cjs +21 -7
  165. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  166. package/gsd-core/bin/shared/config-schema.manifest.json +13 -0
  167. package/gsd-core/bin/verify-reapply-patches.cjs +507 -81
  168. package/gsd-core/references/agent-contracts.md +3 -3
  169. package/gsd-core/references/compact-content-gate.md +66 -0
  170. package/gsd-core/references/edge-probe.md +17 -13
  171. package/gsd-core/references/execute-mvp-tdd.md +18 -16
  172. package/gsd-core/references/execute-phase-response-language.md +6 -0
  173. package/gsd-core/references/executor-examples.md +42 -0
  174. package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
  175. package/gsd-core/references/loop-hook-dispatch.md +18 -0
  176. package/gsd-core/references/model-profiles.md +12 -3
  177. package/gsd-core/references/mvp-concepts.md +2 -2
  178. package/gsd-core/references/plan-checker-examples.md +41 -0
  179. package/gsd-core/references/planner-antipatterns.md +25 -0
  180. package/gsd-core/references/planner-chunked.md +5 -1
  181. package/gsd-core/references/planner-coupling.md +42 -0
  182. package/gsd-core/references/planner-quick-batch.md +71 -0
  183. package/gsd-core/references/planner-reviews.md +47 -0
  184. package/gsd-core/references/planner-revision.md +75 -2
  185. package/gsd-core/references/planning-config.md +5 -1
  186. package/gsd-core/references/response-language-directive.md +9 -0
  187. package/gsd-core/references/revision-loop.md +118 -11
  188. package/gsd-core/references/tdd.md +17 -9
  189. package/gsd-core/references/thinking-models-planning.md +18 -2
  190. package/gsd-core/references/verification-patterns.md +17 -4
  191. package/gsd-core/references/verifier-evidence-gate.md +160 -0
  192. package/gsd-core/references/worktree-path-safety.md +112 -2
  193. package/gsd-core/templates/README.md +7 -1
  194. package/gsd-core/templates/phase-prompt.md +4 -0
  195. package/gsd-core/templates/state.md +6 -3
  196. package/gsd-core/templates/summary.compact.md +212 -0
  197. package/gsd-core/templates/user-setup.compact.md +199 -0
  198. package/gsd-core/templates/user-setup.md +0 -9
  199. package/gsd-core/templates/verification-report.md +5 -0
  200. package/gsd-core/workflows/add-backlog.md +2 -0
  201. package/gsd-core/workflows/add-phase.md +2 -0
  202. package/gsd-core/workflows/add-tests.md +1 -1
  203. package/gsd-core/workflows/add-todo.md +4 -3
  204. package/gsd-core/workflows/ai-integration-phase.md +1 -1
  205. package/gsd-core/workflows/analyze-dependencies.md +2 -0
  206. package/gsd-core/workflows/audit-fix.md +2 -0
  207. package/gsd-core/workflows/audit-milestone.md +2 -0
  208. package/gsd-core/workflows/audit-uat.md +2 -0
  209. package/gsd-core/workflows/autonomous.md +15 -10
  210. package/gsd-core/workflows/check-todos.md +5 -3
  211. package/gsd-core/workflows/cleanup.md +4 -2
  212. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +22 -13
  213. package/gsd-core/workflows/code-review-fix.md +5 -3
  214. package/gsd-core/workflows/code-review.md +211 -43
  215. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
  216. package/gsd-core/workflows/complete-milestone.md +40 -254
  217. package/gsd-core/workflows/debug.md +1 -1
  218. package/gsd-core/workflows/diagnose-issues.md +5 -1
  219. package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -0
  220. package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
  221. package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
  222. package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
  223. package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
  224. package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -0
  225. package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
  226. package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
  227. package/gsd-core/workflows/discuss-phase/modes/text.md +2 -0
  228. package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
  229. package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
  230. package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
  231. package/gsd-core/workflows/discuss-phase-power.md +2 -0
  232. package/gsd-core/workflows/discuss-phase.md +1 -1
  233. package/gsd-core/workflows/do.md +43 -13
  234. package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
  235. package/gsd-core/workflows/docs-update.md +15 -156
  236. package/gsd-core/workflows/edit-phase.md +2 -0
  237. package/gsd-core/workflows/eval-review.md +1 -1
  238. package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
  239. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +20 -3
  240. package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
  241. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +24 -3
  242. package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
  243. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +8 -2
  244. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -0
  245. package/gsd-core/workflows/execute-phase/steps/sequential-root-pin.md +35 -0
  246. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
  247. package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
  248. package/gsd-core/workflows/execute-phase.md +78 -159
  249. package/gsd-core/workflows/execute-plan.md +28 -15
  250. package/gsd-core/workflows/explore.md +2 -0
  251. package/gsd-core/workflows/extract-learnings.md +2 -0
  252. package/gsd-core/workflows/fast.md +6 -0
  253. package/gsd-core/workflows/forensics.md +2 -0
  254. package/gsd-core/workflows/graduation.md +1 -1
  255. package/gsd-core/workflows/health.md +1 -1
  256. package/gsd-core/workflows/help/modes/brief.md +2 -0
  257. package/gsd-core/workflows/help/modes/default.md +2 -0
  258. package/gsd-core/workflows/help/modes/full.compact.md +398 -0
  259. package/gsd-core/workflows/help/modes/full.md +12 -0
  260. package/gsd-core/workflows/help/modes/topic.md +2 -0
  261. package/gsd-core/workflows/help.md +3 -1
  262. package/gsd-core/workflows/import.md +3 -3
  263. package/gsd-core/workflows/inbox.md +1 -1
  264. package/gsd-core/workflows/ingest-docs.md +1 -1
  265. package/gsd-core/workflows/insert-phase.md +2 -0
  266. package/gsd-core/workflows/list-phase-assumptions.md +2 -0
  267. package/gsd-core/workflows/list-seeds.md +2 -0
  268. package/gsd-core/workflows/list-workspaces.md +2 -0
  269. package/gsd-core/workflows/manager.md +3 -3
  270. package/gsd-core/workflows/map-codebase.md +52 -3
  271. package/gsd-core/workflows/milestone-summary.md +2 -0
  272. package/gsd-core/workflows/mvp-phase.md +1 -1
  273. package/gsd-core/workflows/new-milestone.md +55 -13
  274. package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
  275. package/gsd-core/workflows/new-project.md +37 -205
  276. package/gsd-core/workflows/new-workspace.md +1 -1
  277. package/gsd-core/workflows/next.md +2 -0
  278. package/gsd-core/workflows/node-repair.md +2 -0
  279. package/gsd-core/workflows/note.md +2 -0
  280. package/gsd-core/workflows/onboard.md +1 -1
  281. package/gsd-core/workflows/pause-work.md +19 -4
  282. package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
  283. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
  284. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -0
  285. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +9 -0
  286. package/gsd-core/workflows/plan-phase.md +144 -185
  287. package/gsd-core/workflows/plan-review-convergence.md +102 -10
  288. package/gsd-core/workflows/plant-seed.md +1 -1
  289. package/gsd-core/workflows/pr-branch.md +30 -10
  290. package/gsd-core/workflows/profile-user.md +1 -1
  291. package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
  292. package/gsd-core/workflows/progress.md +25 -3
  293. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +37 -2
  294. package/gsd-core/workflows/quick/steps/research-phase.md +3 -3
  295. package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
  296. package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
  297. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
  298. package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
  299. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
  300. package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
  301. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
  302. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
  303. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
  304. package/gsd-core/workflows/quick-batch.md +203 -0
  305. package/gsd-core/workflows/quick.md +21 -4
  306. package/gsd-core/workflows/reapply-patches.md +79 -3
  307. package/gsd-core/workflows/remove-phase.md +2 -0
  308. package/gsd-core/workflows/remove-workspace.md +1 -1
  309. package/gsd-core/workflows/resume-project.md +6 -2
  310. package/gsd-core/workflows/review.md +215 -10
  311. package/gsd-core/workflows/scan.md +2 -0
  312. package/gsd-core/workflows/section-manifest.json +12 -0
  313. package/gsd-core/workflows/secure-phase.md +1 -1
  314. package/gsd-core/workflows/session-report.md +2 -0
  315. package/gsd-core/workflows/settings-advanced.md +2 -0
  316. package/gsd-core/workflows/settings-integrations.md +9 -8
  317. package/gsd-core/workflows/settings.md +19 -6
  318. package/gsd-core/workflows/ship.md +10 -10
  319. package/gsd-core/workflows/sketch-wrap-up.md +2 -0
  320. package/gsd-core/workflows/sketch.md +1 -1
  321. package/gsd-core/workflows/smart-entry.md +1 -1
  322. package/gsd-core/workflows/spec-phase.md +24 -19
  323. package/gsd-core/workflows/spike-wrap-up.md +2 -0
  324. package/gsd-core/workflows/spike.md +1 -1
  325. package/gsd-core/workflows/stats.md +2 -0
  326. package/gsd-core/workflows/sync-skills.md +12 -4
  327. package/gsd-core/workflows/thread.md +2 -0
  328. package/gsd-core/workflows/transition.md +2 -0
  329. package/gsd-core/workflows/ui-phase.md +26 -5
  330. package/gsd-core/workflows/ui-review.md +1 -1
  331. package/gsd-core/workflows/ultraplan-phase.md +2 -0
  332. package/gsd-core/workflows/undo.md +1 -1
  333. package/gsd-core/workflows/update.md +48 -43
  334. package/gsd-core/workflows/validate-phase.md +1 -1
  335. package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
  336. package/gsd-core/workflows/verify-work.md +68 -182
  337. package/hooks/dist/gsd-agent-isolation-guard.js +42 -16
  338. package/hooks/dist/gsd-check-update-worker.js +19 -2
  339. package/hooks/dist/gsd-context-monitor.js +371 -27
  340. package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
  341. package/hooks/dist/gsd-node-runner.sh +1 -0
  342. package/hooks/dist/gsd-prompt-guard.js +30 -5
  343. package/hooks/dist/gsd-read-guard.js +2 -0
  344. package/hooks/dist/gsd-read-injection-scanner.js +5 -5
  345. package/hooks/dist/gsd-secret-read-guard.js +1105 -0
  346. package/hooks/dist/gsd-statusline.js +18 -10
  347. package/hooks/dist/gsd-validate-commit.sh +474 -7
  348. package/hooks/dist/gsd-workflow-guard.js +2 -1
  349. package/hooks/dist/gsd-worktree-path-guard.js +25 -14
  350. package/hooks/dist/gsd-write-guard.js +46 -1
  351. package/hooks/dist/lib/dispatch-identity.js +187 -0
  352. package/hooks/dist/lib/filename-classification.js +64 -0
  353. package/hooks/dist/lib/git-cmd.js +210 -1
  354. package/hooks/dist/lib/injection-patterns.js +36 -6
  355. package/hooks/dist/lib/isolation-deny-reason.js +53 -1
  356. package/hooks/dist/lib/isolation-sentinel.js +58 -19
  357. package/hooks/dist/managed-hooks-registry.cjs +1 -0
  358. package/hooks/gsd-agent-isolation-guard.js +42 -16
  359. package/hooks/gsd-check-update-worker.js +19 -2
  360. package/hooks/gsd-context-monitor.js +371 -27
  361. package/hooks/gsd-cursor-subagent-start.js +34 -14
  362. package/hooks/gsd-node-runner.sh +1 -0
  363. package/hooks/gsd-prompt-guard.js +30 -5
  364. package/hooks/gsd-read-guard.js +2 -0
  365. package/hooks/gsd-read-injection-scanner.js +5 -5
  366. package/hooks/gsd-secret-read-guard.js +1105 -0
  367. package/hooks/gsd-statusline.js +18 -10
  368. package/hooks/gsd-validate-commit.sh +474 -7
  369. package/hooks/gsd-workflow-guard.js +2 -1
  370. package/hooks/gsd-worktree-path-guard.js +25 -14
  371. package/hooks/gsd-write-guard.js +46 -1
  372. package/hooks/hooks.json +6 -0
  373. package/hooks/lib/dispatch-identity.js +187 -0
  374. package/hooks/lib/filename-classification.js +64 -0
  375. package/hooks/lib/git-cmd.js +210 -1
  376. package/hooks/lib/injection-patterns.js +36 -6
  377. package/hooks/lib/isolation-deny-reason.js +53 -1
  378. package/hooks/lib/isolation-sentinel.js +58 -19
  379. package/hooks/managed-hooks-registry.cjs +1 -0
  380. package/package.json +13 -9
  381. package/scripts/benchmark-compact-content-variants.cjs +298 -0
  382. package/scripts/benchmark-compact-content.cjs +368 -0
  383. package/scripts/build-hooks.js +11 -4
  384. package/scripts/check-contract-drift.cjs +4 -1
  385. package/scripts/check-env.cjs +36 -8
  386. package/scripts/check-glossary-refs.cjs +25 -21
  387. package/scripts/ci-next-health.cjs +271 -0
  388. package/scripts/ci-prepare-test-scope.cjs +7 -7
  389. package/scripts/ci-test-scope.cjs +133 -20
  390. package/scripts/ci-timeout-report.cjs +1 -1
  391. package/scripts/diff-touches-shipped-paths.cjs +1 -1
  392. package/scripts/docs-guard-registry.cjs +17 -2
  393. package/scripts/gen-adr-index.cjs +8 -2
  394. package/scripts/gen-inventory-manifest.cjs +12 -0
  395. package/scripts/gen-loop-host-contract.cjs +67 -15
  396. package/scripts/gen-platform-conformance-tier.cjs +557 -0
  397. package/scripts/lib/drift-scan.cjs +1 -1
  398. package/scripts/lib/macos-conformance-tier.generated.cjs +210 -0
  399. package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
  400. package/scripts/lib/platform-conformance-tier.generated.cjs +276 -0
  401. package/scripts/lib/shellcheck-fetch.cjs +247 -0
  402. package/scripts/lib/suite-detection.cjs +32 -0
  403. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
  404. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  405. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  406. package/scripts/lint-allowed-tools-parity.cjs +221 -0
  407. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +24 -2
  408. package/scripts/lint-phase-enumeration-drift.cjs +24 -6
  409. package/scripts/lint-phase-id-drift.cjs +465 -15
  410. package/scripts/lint-portable-grep.cjs +176 -0
  411. package/scripts/lint-response-language-coverage.cjs +530 -0
  412. package/scripts/lint-source-test-name-collision.cjs +1 -1
  413. package/scripts/lint-test-file-count.allowlist.json +4 -1
  414. package/scripts/lint-vendored-deps.cjs +128 -17
  415. package/scripts/lint-workflow-shellcheck-baseline.json +1112 -0
  416. package/scripts/lint-workflow-shellcheck.cjs +614 -0
  417. package/scripts/npm-audit-baseline.cjs +376 -0
  418. package/scripts/prompt-injection-scan.sh +22 -0
  419. package/scripts/require-issue-link-policy.cjs +16 -1
  420. package/scripts/workflow-size.cjs +139 -0
  421. package/skills/gsd-cleanup/SKILL.md +1 -0
  422. package/skills/gsd-code-review/SKILL.md +2 -1
  423. package/skills/gsd-complete-milestone/SKILL.md +1 -0
  424. package/skills/gsd-config/SKILL.md +1 -0
  425. package/skills/gsd-debug/SKILL.md +1 -0
  426. package/skills/gsd-execute-phase/SKILL.md +1 -1
  427. package/skills/gsd-graphify/SKILL.md +1 -0
  428. package/skills/gsd-health/SKILL.md +1 -0
  429. package/skills/gsd-mempalace-capture/SKILL.md +1 -0
  430. package/skills/gsd-mempalace-recall/SKILL.md +1 -0
  431. package/skills/gsd-new-milestone/SKILL.md +1 -0
  432. package/skills/gsd-new-project/SKILL.md +1 -0
  433. package/skills/gsd-next/SKILL.md +1 -0
  434. package/skills/gsd-ns-workflow/SKILL.md +1 -0
  435. package/skills/gsd-pause-work/SKILL.md +1 -0
  436. package/skills/gsd-phase/SKILL.md +2 -1
  437. package/skills/gsd-pr-branch/SKILL.md +1 -0
  438. package/skills/gsd-quick-batch/SKILL.md +105 -0
  439. package/skills/gsd-resume-work/SKILL.md +1 -0
  440. package/skills/gsd-review-backlog/SKILL.md +1 -0
  441. package/skills/gsd-settings/SKILL.md +2 -1
  442. package/skills/gsd-stats/SKILL.md +1 -0
  443. package/skills/gsd-surface/SKILL.md +18 -8
  444. package/skills/gsd-thread/SKILL.md +1 -0
  445. package/skills/gsd-workspace/SKILL.md +1 -0
  446. package/skills/gsd-workstreams/SKILL.md +1 -0
  447. package/vscode/package.json +1 -1
  448. package/gsd-core/templates/claude-md.md +0 -145
  449. package/gsd-core/templates/codebase/concerns.md +0 -310
  450. package/gsd-core/templates/codebase/conventions.md +0 -307
  451. package/gsd-core/templates/codebase/integrations.md +0 -280
  452. package/gsd-core/templates/codebase/structure.md +0 -285
  453. package/gsd-core/templates/codebase/testing.md +0 -480
  454. package/gsd-core/templates/debug-subagent-prompt.md +0 -91
  455. package/gsd-core/templates/discovery.md +0 -146
@@ -135,6 +135,7 @@ const CONFIG_DEFAULTS = {
135
135
  firecrawl: _getConfigDefault('firecrawl'),
136
136
  exa_search: _getConfigDefault('exa_search'),
137
137
  text_mode: _getNestedConfigDefault('workflow', 'text_mode'),
138
+ compact_content: _getNestedConfigDefault('workflow', 'compact_content'),
138
139
  sub_repos: _getNestedConfigDefault('planning', 'sub_repos'),
139
140
  pr_strict: _getNestedConfigDefault('planning', 'pr_strict'),
140
141
  resolve_model_ids: _getConfigDefault('resolve_model_ids'),
@@ -831,6 +832,7 @@ function loadConfigResolved(cwd, options = {}) {
831
832
  branching_strategy: get('branching_strategy', { section: 'git', field: 'branching_strategy' }) ?? defaults.branching_strategy,
832
833
  base_branch: get('base_branch', { section: 'git', field: 'base_branch' }),
833
834
  protected_branches: getNested('git', 'protected_branches'),
835
+ allow_default_branch_commits: getNested('git', 'allow_default_branch_commits'),
834
836
  phase_branch_template: get('phase_branch_template', { section: 'git', field: 'phase_branch_template' }) ?? defaults.phase_branch_template,
835
837
  milestone_branch_template: get('milestone_branch_template', { section: 'git', field: 'milestone_branch_template' }) ?? defaults.milestone_branch_template,
836
838
  quick_branch_template: get('quick_branch_template', { section: 'git', field: 'quick_branch_template' }) ?? defaults.quick_branch_template,
@@ -844,6 +846,7 @@ function loadConfigResolved(cwd, options = {}) {
844
846
  firecrawl: get('firecrawl') ?? defaults.firecrawl,
845
847
  exa_search: get('exa_search') ?? defaults.exa_search,
846
848
  mvp_mode: get('mvp_mode', { section: 'workflow', field: 'mvp_mode' }) ?? false,
849
+ tdd_mode: getNested('workflow', 'tdd_mode') ?? false,
847
850
  text_mode: get('text_mode', { section: 'workflow', field: 'text_mode' }) ?? defaults.text_mode,
848
851
  auto_advance: get('auto_advance', { section: 'workflow', field: 'auto_advance' }) ?? false,
849
852
  _auto_chain_active: get('_auto_chain_active', { section: 'workflow', field: '_auto_chain_active' }) ?? false,
@@ -856,6 +859,7 @@ function loadConfigResolved(cwd, options = {}) {
856
859
  project_code: get('project_code') ?? defaults.project_code,
857
860
  subagent_timeout: get('subagent_timeout', { section: 'workflow', field: 'subagent_timeout' }) ?? defaults.subagent_timeout,
858
861
  model_overrides: (parsed['model_overrides']) || null,
862
+ agent_tools: (parsed['agent_tools']) || null,
859
863
  models: (parsed['models']) || null,
860
864
  granularity: parsed['granularity'] !== undefined ? parsed['granularity'] : null,
861
865
  granularities: (parsed['granularities']) || null,
@@ -81,6 +81,11 @@ const SCHEMA_DEFAULTS = {
81
81
  // #1689: per-plan agent_hint executor routing — default-on. A no-op for plans
82
82
  // without an agent_hint field, so existing dispatch is byte-identical.
83
83
  'workflow.agent_hint_routing': true,
84
+ // #4401: Compact Content mode gate — derived from the defaults manifest via
85
+ // CONFIG_DEFAULTS (added in config-loader.cts) so the manifest stays the
86
+ // single source of truth, matching workflow.smart_zone_tokens /
87
+ // planning.pr_strict / workflow.inline_plan_threshold below.
88
+ 'workflow.compact_content': CONFIG_DEFAULTS.compact_content,
84
89
  // Derived from the defaults manifest rather than restated, so the manifest
85
90
  // stays the single source of truth for the smart-zone budget (#2630).
86
91
  'workflow.smart_zone_tokens': CONFIG_DEFAULTS.smart_zone_tokens,
@@ -93,6 +98,27 @@ const SCHEMA_DEFAULTS = {
93
98
  // effective default existed only as the workflow's shell fallback and the
94
99
  // docs disagreed (settings-advanced said 3). Manifest stays the one owner.
95
100
  'workflow.inline_plan_threshold': CONFIG_DEFAULTS.inline_plan_threshold,
101
+ // #4285 review: an absent threshold resolved to "Key not found" while the
102
+ // hook silently used 35/25 — the query surface disagreeing with the reader.
103
+ //
104
+ // Restated here rather than derived: `CONFIG_DEFAULTS` is re-exported with a
105
+ // FLATTENED shape that drops the manifest's nested blocks, so
106
+ // `CONFIG_DEFAULTS.hooks` is undefined at runtime and the manifest cannot
107
+ // feed these two rows the way `workflow.smart_zone_tokens` above is fed.
108
+ //
109
+ // Not added to `buildNewProjectConfig` either, and that one is deliberate
110
+ // rather than incidental: it writes a `hooks` object into every NEW project's
111
+ // config.json, which would freeze today's fire-points as an explicit
112
+ // per-project override everywhere — the opposite of this PR's premise that an
113
+ // absent key tracks the shipped default. (The manifest alone would NOT have
114
+ // that effect; `buildNewProjectConfig` builds its own literal. Correcting an
115
+ // earlier version of this comment that ran the two together.)
116
+ //
117
+ // That leaves ONE copy of 35/25 outside the hook — these two rows — and
118
+ // `tests/config.test.cjs` pins them against the hook's exported
119
+ // WARNING_THRESHOLD/CRITICAL_THRESHOLD so the copies cannot drift.
120
+ 'hooks.context_warning_threshold': 35,
121
+ 'hooks.context_critical_threshold': 25,
96
122
  };
97
123
  /**
98
124
  * Resolve a schema-level default for an absent key (#2256). Checks the legacy
@@ -308,6 +334,7 @@ function buildNewProjectConfig(userChoices) {
308
334
  human_verify_mode: 'end-of-phase',
309
335
  context_guard_mode: 'warn',
310
336
  text_mode: false,
337
+ compact_content: false,
311
338
  research_before_questions: false,
312
339
  discuss_mode: 'discuss',
313
340
  skip_discuss: false,
@@ -564,19 +591,35 @@ function _unsetNestedValue(config, keyPath) {
564
591
  * Does not call `output()`, so can be used as one step in a command without triggering `exit(0)` in
565
592
  * the happy path. But note that `error()` will still `exit(1)` out of the process.
566
593
  */
594
+ /**
595
+ * Loads `.planning/config.json` as a plain object, or `{}` if the file does
596
+ * not exist. A parse failure calls `error()` (process-exiting) rather than
597
+ * throwing, matching every caller's existing behavior.
598
+ *
599
+ * Single source for this load+parse step — `setConfigValue`,
600
+ * `unsetConfigValue`, `setConfigValues`, `previewConfigValue`, and
601
+ * `previewUnsetConfigValue` all delegate here instead of each repeating the
602
+ * same try/catch (CLAUDE.md's "Generative Fix Divergence" known-defect
603
+ * pattern: independently-guessed copies of the same logic can silently
604
+ * drift apart).
605
+ */
606
+ function loadConfigJson(cwd) {
607
+ const configPath = node_path_1.default.join(planningDir(cwd), 'config.json');
608
+ let config = {};
609
+ try {
610
+ if (node_fs_1.default.existsSync(configPath)) {
611
+ config = JSON.parse(node_fs_1.default.readFileSync(configPath, 'utf-8'));
612
+ }
613
+ }
614
+ catch (err) {
615
+ error('Failed to read config.json: ' + err.message, ERROR_REASON.CONFIG_PARSE_FAILED);
616
+ }
617
+ return config;
618
+ }
567
619
  function unsetConfigValue(cwd, keyPath) {
568
620
  const configPath = node_path_1.default.join(planningDir(cwd), 'config.json');
569
621
  return withPlanningLock(cwd, () => {
570
- // Load existing config or start with empty object
571
- let config = {};
572
- try {
573
- if (node_fs_1.default.existsSync(configPath)) {
574
- config = JSON.parse(node_fs_1.default.readFileSync(configPath, 'utf-8'));
575
- }
576
- }
577
- catch (err) {
578
- error('Failed to read config.json: ' + err.message, ERROR_REASON.CONFIG_PARSE_FAILED);
579
- }
622
+ const config = loadConfigJson(cwd);
580
623
  const { previousValue, existed } = _unsetNestedValue(config, keyPath);
581
624
  // Write back
582
625
  try {
@@ -598,16 +641,7 @@ function unsetConfigValue(cwd, keyPath) {
598
641
  function setConfigValue(cwd, keyPath, parsedValue) {
599
642
  const configPath = node_path_1.default.join(planningDir(cwd), 'config.json');
600
643
  return withPlanningLock(cwd, () => {
601
- // Load existing config or start with empty object
602
- let config = {};
603
- try {
604
- if (node_fs_1.default.existsSync(configPath)) {
605
- config = JSON.parse(node_fs_1.default.readFileSync(configPath, 'utf-8'));
606
- }
607
- }
608
- catch (err) {
609
- error('Failed to read config.json: ' + err.message, ERROR_REASON.CONFIG_PARSE_FAILED);
610
- }
644
+ const config = loadConfigJson(cwd);
611
645
  const previousValue = _setNestedValue(config, keyPath, parsedValue);
612
646
  // Write back
613
647
  try {
@@ -619,6 +653,29 @@ function setConfigValue(cwd, keyPath, parsedValue) {
619
653
  }
620
654
  });
621
655
  }
656
+ /**
657
+ * #4444: read-only preview counterpart to `setConfigValue` — loads config
658
+ * exactly like the real setter and reuses `_setNestedValue` (the SAME
659
+ * traversal/creation logic, including its prototype-pollution guards) on a
660
+ * throwaway in-memory copy that is NEVER written back to disk. This is what
661
+ * makes the dry-run preview provably identical to what the real write would
662
+ * compute, rather than a second, hand-maintained traversal that could drift
663
+ * from the real one.
664
+ */
665
+ function previewConfigValue(cwd, keyPath, parsedValue) {
666
+ const config = loadConfigJson(cwd);
667
+ const previousValue = _setNestedValue(config, keyPath, parsedValue);
668
+ return { key: keyPath, value: parsedValue, previousValue };
669
+ }
670
+ /**
671
+ * #4444: read-only preview counterpart to `unsetConfigValue` — same pattern
672
+ * as `previewConfigValue`, reusing `_unsetNestedValue` on a throwaway copy.
673
+ */
674
+ function previewUnsetConfigValue(cwd, keyPath) {
675
+ const config = loadConfigJson(cwd);
676
+ const { previousValue, existed } = _unsetNestedValue(config, keyPath);
677
+ return { key: keyPath, value: null, previousValue, existed };
678
+ }
622
679
  /**
623
680
  * Batched sibling of setConfigValue: apply multiple key-path writes in a
624
681
  * single load → set-all → write cycle inside ONE withPlanningLock call.
@@ -635,16 +692,7 @@ function setConfigValues(cwd, entries) {
635
692
  }
636
693
  const configPath = node_path_1.default.join(planningDir(cwd), 'config.json');
637
694
  return withPlanningLock(cwd, () => {
638
- // Load existing config or start with empty object
639
- let config = {};
640
- try {
641
- if (node_fs_1.default.existsSync(configPath)) {
642
- config = JSON.parse(node_fs_1.default.readFileSync(configPath, 'utf-8'));
643
- }
644
- }
645
- catch (err) {
646
- error('Failed to read config.json: ' + err.message, ERROR_REASON.CONFIG_PARSE_FAILED);
647
- }
695
+ const config = loadConfigJson(cwd);
648
696
  const results = [];
649
697
  for (const entry of entries) {
650
698
  const previousValue = _setNestedValue(config, entry.keyPath, entry.value);
@@ -676,14 +724,8 @@ function assertEnumValue(parsedValue, rawVal, allowed, label) {
676
724
  error(`Invalid ${label} '${rawVal}'. Valid values: ${allowed.join(', ')}`);
677
725
  }
678
726
  }
679
- /**
680
- * Command to set a value in the config file, allowing nested values via dot notation (e.g.,
681
- * "workflow.research").
682
- *
683
- * Note that this exits the process (via `output()`) even in the happy path; use `setConfigValue()`
684
- * directly if you need to avoid this.
685
- */
686
- function cmdConfigSet(cwd, keyPath, value, raw) {
727
+ function cmdConfigSet(cwd, keyPath, value, raw, options = {}) {
728
+ const dryRun = options.dryRun === true;
687
729
  if (!keyPath) {
688
730
  error('Usage: config-set <key.path> <value>', ERROR_REASON.USAGE);
689
731
  }
@@ -733,6 +775,18 @@ function cmdConfigSet(cwd, keyPath, value, raw) {
733
775
  // present, truthy-adjacent value that consumers must special-case — worst for
734
776
  // secret keys where a leftover value can be passed as a real credential.
735
777
  if (parsedValue === null) {
778
+ if (dryRun) {
779
+ const preview = previewUnsetConfigValue(cwd, kp);
780
+ if ((0, secrets_cjs_1.isSecretKey)(kp)) {
781
+ const maskedPrev = preview.previousValue === undefined
782
+ ? undefined
783
+ : (0, secrets_cjs_1.maskSecret)(preview.previousValue);
784
+ output({ dry_run: true, would_unset: true, key: kp, value: null, previousValue: maskedPrev, masked: true }, raw, `${kp} unset (dry run)`);
785
+ return;
786
+ }
787
+ output({ dry_run: true, would_unset: true, key: kp, value: null, previousValue: preview.previousValue }, raw, `${kp} unset (dry run)`);
788
+ return;
789
+ }
736
790
  const unsetResult = unsetConfigValue(cwd, kp);
737
791
  if ((0, secrets_cjs_1.isSecretKey)(kp)) {
738
792
  const maskedPrev = unsetResult.previousValue === undefined
@@ -787,6 +841,12 @@ function cmdConfigSet(cwd, keyPath, value, raw) {
787
841
  error(`Invalid workflow.post_planning_gaps '${val}'. Must be a boolean (true or false).`);
788
842
  }
789
843
  }
844
+ // Compact Content mode gate (#4139)
845
+ if (kp === 'workflow.compact_content') {
846
+ if (typeof parsedValue !== 'boolean') {
847
+ error(`Invalid workflow.compact_content '${val}'. Must be a boolean (true or false).`);
848
+ }
849
+ }
790
850
  // Per-plan executor routing via agent_hint frontmatter (#1689)
791
851
  if (kp === 'workflow.agent_hint_routing') {
792
852
  if (typeof parsedValue !== 'boolean') {
@@ -835,6 +895,48 @@ function cmdConfigSet(cwd, keyPath, value, raw) {
835
895
  error(`Invalid statusline.show_git '${val}'. Must be a boolean (true or false).`);
836
896
  }
837
897
  }
898
+ // Context-monitor fire-points (#4285) — a percentage of the context window
899
+ // REMAINING, so the domain is 0-100 and the hook compares them against
900
+ // `remaining_percentage`. Rejecting an out-of-domain value here keeps accept
901
+ // and honour in agreement ON THE DOMAIN: the hook falls back to its default
902
+ // for a value outside it, so reporting success would be a lie. That agreement
903
+ // is per-key and no wider — a value accepted here can still be superseded at
904
+ // read time by the hook's pair check, and a scoped write (GSD_PROJECT /
905
+ // GSD_WORKSTREAM) lands in a config the hook does not read at all. The PAIR
906
+ // (critical < warning) is deliberately NOT enforced here: config-set writes
907
+ // one key per call, so a two-step retune can be transiently inconsistent on
908
+ // disk and a check here would reject that intermediate write.
909
+ if (kp === 'hooks.context_warning_threshold' || kp === 'hooks.context_critical_threshold') {
910
+ if (typeof parsedValue !== 'number' || !Number.isFinite(parsedValue) || parsedValue < 0 || parsedValue > 100) {
911
+ error(`Invalid ${kp} '${val}'. Must be a number between 0 and 100 (percent of context window remaining).`);
912
+ }
913
+ // The two ENDPOINTS that are in range but can never form a valid pair are
914
+ // refused here rather than stored (#4285 review). `critical < warning` must
915
+ // hold at read time and BOTH sides are clamped to 0-100, so `warning: 0`
916
+ // has no legal partner (nothing is below 0) and `critical: 100` has none
917
+ // either (nothing above 100). Either one is silently discarded by the hook
918
+ // for EVERY value of the other key — verified: both resolve to the 35/25
919
+ // defaults against a present, absent, or extreme partner, while 0.001 and
920
+ // 99.999 are honoured.
921
+ //
922
+ // Storing a value the reader can never honour is exactly the
923
+ // accept-then-discard shape this codebase refuses elsewhere, so this fails
924
+ // at write time where the operator can see it. The pair itself is still NOT
925
+ // checked here — config-set writes one key per call, so a two-step retune
926
+ // is legitimately inconsistent on disk in between.
927
+ if (kp === 'hooks.context_warning_threshold' && parsedValue === 0) {
928
+ error(`Invalid ${kp} '${val}'. 0 is in range but unusable: the monitor requires `
929
+ + `hooks.context_critical_threshold < hooks.context_warning_threshold, and no valid `
930
+ + `critical value is below 0, so a warning of 0 would always fall back to the 35/25 `
931
+ + `defaults. Use a value above 0.`);
932
+ }
933
+ if (kp === 'hooks.context_critical_threshold' && parsedValue === 100) {
934
+ error(`Invalid ${kp} '${val}'. 100 is in range but unusable: the monitor requires `
935
+ + `hooks.context_critical_threshold < hooks.context_warning_threshold, and no valid `
936
+ + `warning value is above 100, so a critical of 100 would always fall back to the `
937
+ + `35/25 defaults. Use a value below 100.`);
938
+ }
939
+ }
838
940
  // Fallow scope + profile enum validation (#3424)
839
941
  const VALID_FALLOW_SCOPES = ['phase', 'repo'];
840
942
  if (kp === 'code_quality.fallow.scope')
@@ -921,6 +1023,19 @@ function cmdConfigSet(cwd, keyPath, value, raw) {
921
1023
  }
922
1024
  }
923
1025
  }
1026
+ if (dryRun) {
1027
+ const preview = previewConfigValue(cwd, kp, parsedValue);
1028
+ if ((0, secrets_cjs_1.isSecretKey)(kp)) {
1029
+ const masked = (0, secrets_cjs_1.maskSecret)(parsedValue);
1030
+ const maskedPrev = preview.previousValue === undefined
1031
+ ? undefined
1032
+ : (0, secrets_cjs_1.maskSecret)(preview.previousValue);
1033
+ output({ dry_run: true, would_update: true, key: kp, value: masked, previousValue: maskedPrev, masked: true }, raw, `${kp}=${masked} (dry run)`);
1034
+ return;
1035
+ }
1036
+ output({ dry_run: true, would_update: true, key: kp, value: parsedValue, previousValue: preview.previousValue }, raw, `${kp}=${String(parsedValue)} (dry run)`);
1037
+ return;
1038
+ }
924
1039
  const setConfigValueResult = setConfigValue(cwd, kp, parsedValue);
925
1040
  // Mask secrets in both JSON and text output. The plaintext is written
926
1041
  // to config.json (that's where secrets live on disk); the CLI output
@@ -43,6 +43,11 @@ const phaseIdModule = require("./phase-id.cjs");
43
43
  const planningWorkspace = require("./planning-workspace.cjs");
44
44
  // eslint-disable-next-line @typescript-eslint/no-require-imports
45
45
  const shellCommandProjection = require("./shell-command-projection.cjs");
46
+ // planning-scope.cjs is a leaf module (no imports of its own — see its file
47
+ // header), so importing it here directly cannot introduce a cycle.
48
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
49
+ const planningScopeMod = require("./planning-scope.cjs");
50
+ const { SCOPE } = planningScopeMod;
46
51
  // ─── Line-ending normalization ─────────────────────────────────────────────────
47
52
  /**
48
53
  * Normalize every line ending in `content` to a bare `\n`, ONCE — the shared
@@ -271,16 +276,38 @@ function transliterateForSlug(text) {
271
276
  * Degrades on an unreadable directory instead of throwing: empty arrays,
272
277
  * every flag false, scope UNREADABLE (mirroring scanPhasePlans's own
273
278
  * degrade path).
279
+ *
280
+ * #4014 (epic #3473 B4-unreadable): this function's OWN readdirSync used to
281
+ * catch its own failure and then return `scope: scan.scope` — the scope of
282
+ * the UNRELATED, already-successful scanPhasePlans call — silently dropping
283
+ * that THIS read failed. The own-readdirSync is now routed through
284
+ * findContextMdIn's directory-string form (which never throws and reports
285
+ * its own SCOPE.UNREADABLE), and the two independently-scoped answers are
286
+ * combined into the worse of the two below — never letting a successful
287
+ * scan.scope mask this function's own failed read.
288
+ *
289
+ * The combination is written out as a two-value comparison rather than
290
+ * calling planning-snapshot.cts's `worstScope` directly: that module
291
+ * requires core-utils.cjs (for `findOrphanSummaries`) at its own top level,
292
+ * so importing it back here would create a tight, direct
293
+ * core-utils.cjs <-> planning-snapshot.cjs cycle — a materially different
294
+ * (and riskier) shape than this file's existing, documented lazy-access
295
+ * cyclic partners (planning-workspace.cjs, phase-id.cjs), which
296
+ * planning-snapshot.cts sits *above* in the dependency graph, not beside.
297
+ * `findContextMdIn`'s directory-string form can only ever report
298
+ * SCOPE.COMPLETE or SCOPE.UNREADABLE (a single readdirSync is binary — see
299
+ * its own doc comment), so per planning-snapshot.cts's SCOPE_SEVERITY
300
+ * ordering (UNREADABLE is maximal), `worstScope(scan.scope, ownScope)` is
301
+ * exactly `ownScope === UNREADABLE ? UNREADABLE : scan.scope` — the
302
+ * expression below is that identity, not a re-derivation of the ordering.
274
303
  */
275
304
  function getPhaseFileStats(phaseDir) {
276
305
  // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment
277
306
  const scanPhasePlans = require('./plan-scan.cjs');
278
307
  const scan = scanPhasePlans(phaseDir);
279
- let files;
280
- try {
281
- files = node_fs_1.default.readdirSync(phaseDir);
282
- }
283
- catch {
308
+ const { files, scope: ownScope } = planningWorkspace.findContextMdIn(phaseDir);
309
+ const scope = ownScope === SCOPE.UNREADABLE ? SCOPE.UNREADABLE : scan.scope;
310
+ if (ownScope === SCOPE.UNREADABLE) {
284
311
  return {
285
312
  plans: scan.planFiles,
286
313
  summaries: scan.summaryFiles,
@@ -288,7 +315,7 @@ function getPhaseFileStats(phaseDir) {
288
315
  hasContext: false,
289
316
  hasVerification: false,
290
317
  hasReviews: false,
291
- scope: scan.scope,
318
+ scope,
292
319
  };
293
320
  }
294
321
  const scopedFiles = phaseIdModule.scopeToPhase(files, node_path_1.default.basename(phaseDir));
@@ -299,7 +326,7 @@ function getPhaseFileStats(phaseDir) {
299
326
  hasContext: planningWorkspace.findContextMdIn(scopedFiles) !== null,
300
327
  hasVerification: scopedFiles.some(f => f.endsWith('-VERIFICATION.md') || f === 'VERIFICATION.md'),
301
328
  hasReviews: scopedFiles.some(f => f.endsWith('-REVIEWS.md') || f === 'REVIEWS.md'),
302
- scope: scan.scope,
329
+ scope,
303
330
  };
304
331
  }
305
332
  /**
@@ -435,7 +435,7 @@ function cmdClassify(cwd, options = {}, raw) {
435
435
  }
436
436
  let resolvedPath;
437
437
  try {
438
- resolvedPath = (0, security_cjs_1.requireSafePath)(filePath, cwd, 'SUMMARY file', { allowAbsolute: true });
438
+ resolvedPath = (0, security_cjs_1.requireSafePath)(filePath, cwd, 'SUMMARY file', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
439
439
  }
440
440
  catch (e) {
441
441
  // Emit a structured command error instead of leaking a raw stack trace.