@opengsd/gsd-core 1.11.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (498) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.opencode/plugins/gsd-core.js +12 -0
  4. package/agents/gsd-code-fixer.md +1 -1
  5. package/agents/gsd-debug-session-manager.md +1 -1
  6. package/agents/gsd-debugger.md +1 -1
  7. package/agents/gsd-dom-verifier.md +169 -0
  8. package/agents/gsd-eval-auditor.md +1 -1
  9. package/agents/gsd-executor.md +78 -42
  10. package/agents/gsd-framework-selector.md +1 -3
  11. package/agents/gsd-intel-updater.md +1 -1
  12. package/agents/gsd-mempalace-curator.md +0 -1
  13. package/agents/gsd-pattern-mapper.md +11 -0
  14. package/agents/gsd-phase-researcher.md +3 -1
  15. package/agents/gsd-plan-checker.md +91 -112
  16. package/agents/gsd-planner.md +20 -4
  17. package/agents/gsd-project-researcher.md +1 -1
  18. package/agents/gsd-research-synthesizer.md +2 -2
  19. package/agents/gsd-roadmapper.md +15 -11
  20. package/agents/gsd-ui-checker.md +82 -7
  21. package/agents/gsd-ui-researcher.md +70 -3
  22. package/agents/gsd-verifier.md +24 -2
  23. package/bin/install.js +847 -200
  24. package/commands/gsd/discuss-phase.md +1 -1
  25. package/commands/gsd/execute-phase.md +1 -1
  26. package/commands/gsd/import.md +1 -1
  27. package/commands/gsd/ns-workflow.md +2 -1
  28. package/commands/gsd/phase.md +1 -1
  29. package/commands/gsd/quick-batch.md +105 -0
  30. package/commands/gsd/quick.md +8 -4
  31. package/commands/gsd/surface.md +18 -8
  32. package/gsd-core/bin/gsd-tools.cjs +761 -100
  33. package/gsd-core/bin/lib/active-workstream-store.cjs +8 -0
  34. package/gsd-core/bin/lib/adr-parser.cjs +13 -7
  35. package/gsd-core/bin/lib/agent-install-check.cjs +162 -0
  36. package/gsd-core/bin/lib/api-coverage.cjs +30 -9
  37. package/gsd-core/bin/lib/artifacts.cjs +2 -0
  38. package/gsd-core/bin/lib/assumption-delta.cjs +30 -11
  39. package/gsd-core/bin/lib/audit.cjs +163 -41
  40. package/gsd-core/bin/lib/broken-windows.cjs +306 -28
  41. package/gsd-core/bin/lib/capability-activation.cjs +27 -0
  42. package/gsd-core/bin/lib/capability-lock.cjs +10 -4
  43. package/gsd-core/bin/lib/capability-registry.cjs +785 -144
  44. package/gsd-core/bin/lib/capability-state.cjs +25 -4
  45. package/gsd-core/bin/lib/capability-validator.cjs +321 -18
  46. package/gsd-core/bin/lib/capability-writer.cjs +14 -4
  47. package/gsd-core/bin/lib/check-command-router.cjs +229 -6
  48. package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
  49. package/gsd-core/bin/lib/cli-exit.cjs +496 -10
  50. package/gsd-core/bin/lib/clusters.cjs +1 -0
  51. package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
  52. package/gsd-core/bin/lib/codex-agent-toml.cjs +410 -4
  53. package/gsd-core/bin/lib/command-aliases.cjs +16 -0
  54. package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
  55. package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
  56. package/gsd-core/bin/lib/commands.cjs +877 -54
  57. package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
  58. package/gsd-core/bin/lib/config-loader.cjs +121 -29
  59. package/gsd-core/bin/lib/config.cjs +92 -2
  60. package/gsd-core/bin/lib/configuration.cjs +129 -37
  61. package/gsd-core/bin/lib/core-utils.cjs +118 -14
  62. package/gsd-core/bin/lib/decisions.cjs +213 -1
  63. package/gsd-core/bin/lib/edge-probe.cjs +23 -2
  64. package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
  65. package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
  66. package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
  67. package/gsd-core/bin/lib/frontmatter.cjs +975 -326
  68. package/gsd-core/bin/lib/gap-checker.cjs +41 -8
  69. package/gsd-core/bin/lib/git-base-branch.cjs +182 -39
  70. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
  71. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
  72. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +60 -14
  73. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +75 -22
  74. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +22 -8
  75. package/gsd-core/bin/lib/health-diagnostic.cjs +23 -3
  76. package/gsd-core/bin/lib/host-integration.cjs +96 -11
  77. package/gsd-core/bin/lib/init-command-router.cjs +132 -21
  78. package/gsd-core/bin/lib/init.cjs +252 -56
  79. package/gsd-core/bin/lib/install-engine.cjs +252 -15
  80. package/gsd-core/bin/lib/install-model-override-resolver.cjs +78 -1
  81. package/gsd-core/bin/lib/install-profiles.cjs +100 -18
  82. package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
  83. package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
  84. package/gsd-core/bin/lib/installer-migrations.cjs +10 -7
  85. package/gsd-core/bin/lib/intel.cjs +101 -26
  86. package/gsd-core/bin/lib/io.cjs +195 -15
  87. package/gsd-core/bin/lib/learnings.cjs +85 -14
  88. package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
  89. package/gsd-core/bin/lib/loop-resolver.cjs +14 -8
  90. package/gsd-core/bin/lib/markdown-table.cjs +175 -4
  91. package/gsd-core/bin/lib/milestone.cjs +112 -7
  92. package/gsd-core/bin/lib/model-catalog.cjs +177 -19
  93. package/gsd-core/bin/lib/model-resolver.cjs +10 -28
  94. package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
  95. package/gsd-core/bin/lib/phase-command-router.cjs +13 -6
  96. package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
  97. package/gsd-core/bin/lib/phase-id.cjs +321 -13
  98. package/gsd-core/bin/lib/phase-lifecycle.cjs +24 -16
  99. package/gsd-core/bin/lib/phase-locator.cjs +138 -17
  100. package/gsd-core/bin/lib/phase.cjs +1175 -115
  101. package/gsd-core/bin/lib/plan-document.cjs +273 -0
  102. package/gsd-core/bin/lib/plan-scan.cjs +13 -2
  103. package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
  104. package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
  105. package/gsd-core/bin/lib/planning-snapshot.cjs +165 -34
  106. package/gsd-core/bin/lib/planning-workspace.cjs +159 -28
  107. package/gsd-core/bin/lib/probe-core.cjs +4 -1
  108. package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
  109. package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
  110. package/gsd-core/bin/lib/quick-batch-command-router.cjs +285 -0
  111. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
  112. package/gsd-core/bin/lib/quick-batch.cjs +840 -0
  113. package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
  114. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +71 -45
  115. package/gsd-core/bin/lib/review-lane-descriptor.cjs +62 -14
  116. package/gsd-core/bin/lib/review-lane-invocation.cjs +73 -1
  117. package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
  118. package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
  119. package/gsd-core/bin/lib/roadmap-parser.cjs +577 -41
  120. package/gsd-core/bin/lib/roadmap.cjs +248 -64
  121. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +329 -41
  122. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +16 -17
  123. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +320 -109
  124. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +487 -83
  125. package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
  126. package/gsd-core/bin/lib/runtime-slash.cjs +72 -2
  127. package/gsd-core/bin/lib/shell-command-projection.cjs +75 -8
  128. package/gsd-core/bin/lib/smart-entry.cjs +19 -31
  129. package/gsd-core/bin/lib/spec-section.cjs +12 -7
  130. package/gsd-core/bin/lib/state-command-router.cjs +47 -18
  131. package/gsd-core/bin/lib/state-contract.cjs +359 -0
  132. package/gsd-core/bin/lib/state-document.cjs +216 -5
  133. package/gsd-core/bin/lib/state-md-schema.cjs +231 -0
  134. package/gsd-core/bin/lib/state-transition.cjs +850 -145
  135. package/gsd-core/bin/lib/state.cjs +1629 -287
  136. package/gsd-core/bin/lib/surface.cjs +33 -10
  137. package/gsd-core/bin/lib/task-command-router.cjs +111 -1
  138. package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
  139. package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
  140. package/gsd-core/bin/lib/teams-status.cjs +4 -1
  141. package/gsd-core/bin/lib/uat-predicate.cjs +58 -20
  142. package/gsd-core/bin/lib/uat.cjs +2542 -387
  143. package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
  144. package/gsd-core/bin/lib/ui-safety-gate.cjs +37 -7
  145. package/gsd-core/bin/lib/unusable-input.cjs +13 -0
  146. package/gsd-core/bin/lib/update-context.cjs +6 -2
  147. package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
  148. package/gsd-core/bin/lib/validate.cjs +230 -12
  149. package/gsd-core/bin/lib/vendor/README.md +43 -5
  150. package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
  151. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  152. package/gsd-core/bin/lib/verification.cjs +287 -13
  153. package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
  154. package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
  155. package/gsd-core/bin/lib/verify.cjs +441 -56
  156. package/gsd-core/bin/lib/workstream-inventory.cjs +20 -2
  157. package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
  158. package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
  159. package/gsd-core/bin/lib/worktree-safety.cjs +185 -21
  160. package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
  161. package/gsd-core/bin/shared/config-schema.manifest.json +13 -0
  162. package/gsd-core/bin/shared/exit-codes.json +8 -0
  163. package/gsd-core/bin/shared/exit-codes.sh +20 -0
  164. package/gsd-core/bin/shared/model-catalog.json +8 -1
  165. package/gsd-core/bin/verify-reapply-patches.cjs +70 -3
  166. package/gsd-core/references/agent-contracts.md +6 -5
  167. package/gsd-core/references/api-coverage.md +24 -2
  168. package/gsd-core/references/autonomous-smart-discuss.md +3 -3
  169. package/gsd-core/references/checkpoints.md +37 -19
  170. package/gsd-core/references/decimal-phase-calculation.md +5 -5
  171. package/gsd-core/references/edge-probe.md +17 -5
  172. package/gsd-core/references/execute-mvp-tdd.md +18 -18
  173. package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
  174. package/gsd-core/references/execute-phase-response-language.md +6 -0
  175. package/gsd-core/references/execute-phase-wave-guard.md +11 -9
  176. package/gsd-core/references/executor-examples.md +42 -0
  177. package/gsd-core/references/failing-direction.md +78 -0
  178. package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
  179. package/gsd-core/references/gate-prompts.md +1 -1
  180. package/gsd-core/references/git-integration.md +5 -5
  181. package/gsd-core/references/git-planning-commit.md +3 -3
  182. package/gsd-core/references/gsd-run-resolver.md +1 -1
  183. package/gsd-core/references/loop-hook-dispatch.md +22 -0
  184. package/gsd-core/references/model-profiles.md +1 -1
  185. package/gsd-core/references/mvp-concepts.md +2 -2
  186. package/gsd-core/references/nyquist-compliance.md +74 -0
  187. package/gsd-core/references/offer-next.md +3 -5
  188. package/gsd-core/references/phase-argument-parsing.md +3 -3
  189. package/gsd-core/references/plan-checker-examples.md +41 -0
  190. package/gsd-core/references/planner-antipatterns.md +25 -0
  191. package/gsd-core/references/planner-chunked.md +5 -1
  192. package/gsd-core/references/planner-coupling.md +42 -0
  193. package/gsd-core/references/planner-failing-direction.md +53 -0
  194. package/gsd-core/references/planner-human-verify-mode.md +15 -1
  195. package/gsd-core/references/planner-quick-batch.md +71 -0
  196. package/gsd-core/references/planner-reviews.md +47 -0
  197. package/gsd-core/references/planner-revision.md +76 -3
  198. package/gsd-core/references/planner-verify-command-grounding.md +17 -0
  199. package/gsd-core/references/planning-config.md +39 -9
  200. package/gsd-core/references/response-language-directive.md +9 -0
  201. package/gsd-core/references/reviewer-instances.md +31 -0
  202. package/gsd-core/references/revision-loop.md +118 -11
  203. package/gsd-core/references/runtime-aware-dispatch.md +1 -1
  204. package/gsd-core/references/tdd.md +15 -12
  205. package/gsd-core/references/ui-brand.md +65 -21
  206. package/gsd-core/references/ui-consideration-probe.md +1 -1
  207. package/gsd-core/references/universal-anti-patterns.md +2 -2
  208. package/gsd-core/references/verifier-evidence-gate.md +160 -0
  209. package/gsd-core/references/verify-command-path-resolvability.md +42 -0
  210. package/gsd-core/references/verify-mvp-mode.md +1 -1
  211. package/gsd-core/references/workstream-flag.md +11 -11
  212. package/gsd-core/templates/README.md +1 -1
  213. package/gsd-core/templates/SECURITY.md +3 -3
  214. package/gsd-core/templates/UI-SPEC.md +25 -3
  215. package/gsd-core/templates/VALIDATION.md +3 -3
  216. package/gsd-core/templates/phase-prompt.md +7 -0
  217. package/gsd-core/templates/state.md +7 -0
  218. package/gsd-core/templates/verification-report.md +5 -0
  219. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  220. package/gsd-core/workflows/add-backlog.md +3 -1
  221. package/gsd-core/workflows/add-phase.md +5 -3
  222. package/gsd-core/workflows/add-tests.md +4 -9
  223. package/gsd-core/workflows/add-todo.md +2 -2
  224. package/gsd-core/workflows/ai-integration-phase.md +5 -10
  225. package/gsd-core/workflows/analyze-dependencies.md +2 -0
  226. package/gsd-core/workflows/audit-fix.md +14 -3
  227. package/gsd-core/workflows/audit-milestone.md +11 -9
  228. package/gsd-core/workflows/audit-uat.md +19 -2
  229. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
  230. package/gsd-core/workflows/autonomous.md +12 -26
  231. package/gsd-core/workflows/check-todos.md +2 -2
  232. package/gsd-core/workflows/cleanup.md +3 -3
  233. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +16 -14
  234. package/gsd-core/workflows/code-review-fix.md +3 -1
  235. package/gsd-core/workflows/code-review.md +192 -69
  236. package/gsd-core/workflows/complete-milestone.md +28 -14
  237. package/gsd-core/workflows/debug.md +6 -4
  238. package/gsd-core/workflows/diagnose-issues.md +17 -7
  239. package/gsd-core/workflows/discuss-phase/modes/advisor.md +3 -1
  240. package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
  241. package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
  242. package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
  243. package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
  244. package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -7
  245. package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
  246. package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
  247. package/gsd-core/workflows/discuss-phase/modes/text.md +3 -1
  248. package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
  249. package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
  250. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
  251. package/gsd-core/workflows/discuss-phase-assumptions.md +3 -3
  252. package/gsd-core/workflows/discuss-phase-power.md +2 -0
  253. package/gsd-core/workflows/discuss-phase.md +2 -2
  254. package/gsd-core/workflows/do.md +46 -19
  255. package/gsd-core/workflows/docs-update.md +6 -5
  256. package/gsd-core/workflows/edit-phase.md +3 -1
  257. package/gsd-core/workflows/eval-review.md +5 -10
  258. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +3 -1
  259. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +129 -11
  260. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
  261. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
  262. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  263. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +29 -5
  264. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
  265. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
  266. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +4 -2
  267. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
  268. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
  269. package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
  270. package/gsd-core/workflows/execute-phase.md +68 -66
  271. package/gsd-core/workflows/execute-plan.md +25 -20
  272. package/gsd-core/workflows/explore.md +3 -1
  273. package/gsd-core/workflows/extract-learnings.md +3 -1
  274. package/gsd-core/workflows/fast.md +8 -2
  275. package/gsd-core/workflows/forensics.md +3 -1
  276. package/gsd-core/workflows/graduation.md +6 -6
  277. package/gsd-core/workflows/health.md +4 -7
  278. package/gsd-core/workflows/help/modes/brief.md +2 -0
  279. package/gsd-core/workflows/help/modes/default.md +2 -0
  280. package/gsd-core/workflows/help/modes/full.md +12 -0
  281. package/gsd-core/workflows/help/modes/topic.md +2 -0
  282. package/gsd-core/workflows/help.md +2 -0
  283. package/gsd-core/workflows/import.md +17 -14
  284. package/gsd-core/workflows/inbox.md +5 -6
  285. package/gsd-core/workflows/ingest-docs.md +45 -12
  286. package/gsd-core/workflows/insert-phase.md +7 -5
  287. package/gsd-core/workflows/list-phase-assumptions.md +2 -0
  288. package/gsd-core/workflows/list-seeds.md +7 -3
  289. package/gsd-core/workflows/list-workspaces.md +3 -1
  290. package/gsd-core/workflows/manager.md +15 -26
  291. package/gsd-core/workflows/map-codebase.md +3 -1
  292. package/gsd-core/workflows/milestone-summary.md +3 -1
  293. package/gsd-core/workflows/mvp-phase.md +3 -3
  294. package/gsd-core/workflows/new-milestone.md +10 -22
  295. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  296. package/gsd-core/workflows/new-project.md +17 -29
  297. package/gsd-core/workflows/new-workspace.md +2 -2
  298. package/gsd-core/workflows/next.md +4 -2
  299. package/gsd-core/workflows/node-repair.md +2 -0
  300. package/gsd-core/workflows/note.md +2 -0
  301. package/gsd-core/workflows/onboard.md +1 -1
  302. package/gsd-core/workflows/pause-work.md +20 -5
  303. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
  304. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
  305. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +4 -4
  306. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +12 -3
  307. package/gsd-core/workflows/plan-phase.md +251 -54
  308. package/gsd-core/workflows/plan-review-convergence.md +148 -19
  309. package/gsd-core/workflows/plant-seed.md +3 -3
  310. package/gsd-core/workflows/pr-branch.md +195 -51
  311. package/gsd-core/workflows/profile-user.md +17 -15
  312. package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
  313. package/gsd-core/workflows/progress.md +52 -15
  314. package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
  315. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +38 -5
  316. package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
  317. package/gsd-core/workflows/quick/steps/research-phase.md +5 -7
  318. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
  319. package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
  320. package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
  321. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
  322. package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
  323. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
  324. package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
  325. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
  326. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
  327. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
  328. package/gsd-core/workflows/quick-batch.md +203 -0
  329. package/gsd-core/workflows/quick.md +33 -32
  330. package/gsd-core/workflows/reapply-patches.md +2 -0
  331. package/gsd-core/workflows/remove-phase.md +6 -4
  332. package/gsd-core/workflows/remove-workspace.md +3 -3
  333. package/gsd-core/workflows/resume-project.md +14 -14
  334. package/gsd-core/workflows/review.md +404 -21
  335. package/gsd-core/workflows/scan.md +3 -1
  336. package/gsd-core/workflows/section-manifest.json +12 -0
  337. package/gsd-core/workflows/secure-phase.md +3 -3
  338. package/gsd-core/workflows/session-report.md +2 -0
  339. package/gsd-core/workflows/settings-advanced.md +9 -9
  340. package/gsd-core/workflows/settings-integrations.md +66 -32
  341. package/gsd-core/workflows/settings.md +4 -6
  342. package/gsd-core/workflows/ship.md +22 -16
  343. package/gsd-core/workflows/sketch-wrap-up.md +13 -17
  344. package/gsd-core/workflows/sketch.md +13 -19
  345. package/gsd-core/workflows/smart-entry.md +4 -6
  346. package/gsd-core/workflows/spec-phase.md +31 -4
  347. package/gsd-core/workflows/spike-wrap-up.md +9 -11
  348. package/gsd-core/workflows/spike.md +21 -32
  349. package/gsd-core/workflows/stats.md +4 -2
  350. package/gsd-core/workflows/sync-skills.md +13 -5
  351. package/gsd-core/workflows/thread.md +13 -7
  352. package/gsd-core/workflows/transition.md +7 -5
  353. package/gsd-core/workflows/ui-phase.md +36 -21
  354. package/gsd-core/workflows/ui-review.md +7 -11
  355. package/gsd-core/workflows/ultraplan-phase.md +7 -13
  356. package/gsd-core/workflows/undo.md +9 -17
  357. package/gsd-core/workflows/update.md +47 -48
  358. package/gsd-core/workflows/validate-phase.md +3 -3
  359. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
  360. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
  361. package/gsd-core/workflows/verify-work.md +106 -21
  362. package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
  363. package/hooks/dist/gsd-check-update-worker.js +19 -2
  364. package/hooks/dist/gsd-config-reload.js +18 -12
  365. package/hooks/dist/gsd-context-monitor.js +302 -22
  366. package/hooks/dist/gsd-cursor-post-tool.js +3 -1
  367. package/hooks/dist/gsd-cursor-pre-tool.js +3 -1
  368. package/hooks/dist/gsd-cursor-session-start.js +2 -1
  369. package/hooks/dist/gsd-cursor-stop.js +2 -1
  370. package/hooks/dist/gsd-cursor-subagent-start.js +28 -23
  371. package/hooks/dist/gsd-cursor-subagent-stop.js +3 -1
  372. package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
  373. package/hooks/dist/gsd-graphify-update.sh +22 -18
  374. package/hooks/dist/gsd-node-runner.sh +77 -0
  375. package/hooks/dist/gsd-phase-boundary.sh +1 -0
  376. package/hooks/dist/gsd-prompt-guard.js +46 -12
  377. package/hooks/dist/gsd-read-guard.js +18 -7
  378. package/hooks/dist/gsd-read-injection-scanner.js +22 -13
  379. package/hooks/dist/gsd-secret-read-guard.js +1079 -0
  380. package/hooks/dist/gsd-session-state.sh +1 -0
  381. package/hooks/dist/gsd-statusline.js +222 -29
  382. package/hooks/dist/gsd-validate-commit.sh +523 -12
  383. package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
  384. package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
  385. package/hooks/dist/gsd-workflow-guard.js +36 -17
  386. package/hooks/dist/gsd-worktree-path-guard.js +36 -21
  387. package/hooks/dist/gsd-write-guard.js +35 -25
  388. package/hooks/dist/lib/cli-exit.js +560 -0
  389. package/hooks/dist/lib/exit-code-registry.js +98 -0
  390. package/hooks/dist/lib/git-cmd.js +210 -1
  391. package/hooks/dist/lib/git-probe.js +84 -0
  392. package/hooks/dist/lib/hook-exit.js +81 -0
  393. package/hooks/dist/lib/injection-patterns.js +36 -6
  394. package/hooks/dist/managed-hooks-registry.cjs +4 -0
  395. package/hooks/gsd-agent-isolation-guard.js +77 -38
  396. package/hooks/gsd-check-update-worker.js +19 -2
  397. package/hooks/gsd-config-reload.js +18 -12
  398. package/hooks/gsd-context-monitor.js +302 -22
  399. package/hooks/gsd-cursor-post-tool.js +3 -1
  400. package/hooks/gsd-cursor-pre-tool.js +3 -1
  401. package/hooks/gsd-cursor-session-start.js +2 -1
  402. package/hooks/gsd-cursor-stop.js +2 -1
  403. package/hooks/gsd-cursor-subagent-start.js +28 -23
  404. package/hooks/gsd-cursor-subagent-stop.js +3 -1
  405. package/hooks/gsd-ensure-canonical-path.js +2 -1
  406. package/hooks/gsd-graphify-update.sh +22 -18
  407. package/hooks/gsd-node-runner.sh +77 -0
  408. package/hooks/gsd-phase-boundary.sh +1 -0
  409. package/hooks/gsd-prompt-guard.js +46 -12
  410. package/hooks/gsd-read-guard.js +18 -7
  411. package/hooks/gsd-read-injection-scanner.js +22 -13
  412. package/hooks/gsd-secret-read-guard.js +1079 -0
  413. package/hooks/gsd-session-state.sh +1 -0
  414. package/hooks/gsd-statusline.js +222 -29
  415. package/hooks/gsd-validate-commit.sh +523 -12
  416. package/hooks/gsd-windsurf-pre-command.js +16 -11
  417. package/hooks/gsd-windsurf-pre-write.js +22 -13
  418. package/hooks/gsd-workflow-guard.js +36 -17
  419. package/hooks/gsd-worktree-path-guard.js +36 -21
  420. package/hooks/gsd-write-guard.js +35 -25
  421. package/hooks/hooks.json +6 -0
  422. package/hooks/lib/cli-exit.js +560 -0
  423. package/hooks/lib/exit-code-registry.js +98 -0
  424. package/hooks/lib/git-cmd.js +210 -1
  425. package/hooks/lib/git-probe.js +84 -0
  426. package/hooks/lib/hook-exit.js +81 -0
  427. package/hooks/lib/injection-patterns.js +36 -6
  428. package/hooks/managed-hooks-registry.cjs +4 -0
  429. package/package.json +14 -9
  430. package/scripts/base64-scan.sh +74 -12
  431. package/scripts/build-hooks.js +12 -0
  432. package/scripts/check-glossary-refs.cjs +77 -15
  433. package/scripts/check-mutation-score-ratchet.cjs +156 -0
  434. package/scripts/ci-check-job-near-cap.cjs +49 -0
  435. package/scripts/ci-pr-mergeability.cjs +262 -0
  436. package/scripts/ci-test-scope.cjs +52 -12
  437. package/scripts/ci-timeout-report.cjs +230 -0
  438. package/scripts/docs-guard-registry.cjs +406 -0
  439. package/scripts/gen-capability-registry.cjs +8 -6
  440. package/scripts/gen-exit-code-docs.cjs +318 -0
  441. package/scripts/gen-exit-code-registry.cjs +891 -0
  442. package/scripts/gen-features.cjs +836 -0
  443. package/scripts/gen-hooks-cli-exit.cjs +239 -0
  444. package/scripts/gen-install-tree-fixtures.cjs +2 -2
  445. package/scripts/gen-loop-host-contract.cjs +189 -4
  446. package/scripts/gen-scripts-cli-exit.cjs +185 -0
  447. package/scripts/gen-state-md-docs.cjs +727 -0
  448. package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
  449. package/scripts/lib/ci-job-timing.cjs +72 -0
  450. package/scripts/lib/cli-exit.cjs +546 -44
  451. package/scripts/lib/drift-scan.cjs +32 -2
  452. package/scripts/lib/exit-code-registry.cjs +98 -0
  453. package/scripts/lib/ndjson-reporter.cjs +119 -0
  454. package/scripts/lib/shellcheck-fetch.cjs +247 -0
  455. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
  456. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  457. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  458. package/scripts/lint-docs-guard-registration.cjs +495 -0
  459. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +198 -0
  460. package/scripts/lint-eslint-glob-coverage.allowlist.json +4 -0
  461. package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
  462. package/scripts/lint-health-diagnostic-rule-table.cjs +65 -8
  463. package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
  464. package/scripts/lint-phase-enumeration-drift.cjs +45 -14
  465. package/scripts/lint-phase-id-drift.cjs +133 -8
  466. package/scripts/lint-planning-prompt-drift.cjs +38 -1
  467. package/scripts/lint-portable-grep.cjs +176 -0
  468. package/scripts/lint-removed-but-needed.cjs +184 -16
  469. package/scripts/lint-response-language-coverage.cjs +524 -0
  470. package/scripts/lint-seam-enforcement.cjs +182 -0
  471. package/scripts/lint-slug-derivation-drift.cjs +921 -0
  472. package/scripts/lint-source-test-name-collision.cjs +241 -0
  473. package/scripts/lint-state-write-path-drift.cjs +337 -432
  474. package/scripts/lint-test-file-count.allowlist.json +124 -4
  475. package/scripts/lint-test-file-count.cjs +25 -3
  476. package/scripts/lint-unreachable-guard-drift.cjs +51 -64
  477. package/scripts/lint-vendored-deps.cjs +208 -35
  478. package/scripts/lint-workflow-shellcheck-baseline.json +1027 -0
  479. package/scripts/lint-workflow-shellcheck.cjs +614 -0
  480. package/scripts/mutation-matrix.cjs +599 -50
  481. package/scripts/npm-audit-baseline.cjs +376 -0
  482. package/scripts/prompt-injection-scan.sh +83 -14
  483. package/scripts/require-issue-link-policy.cjs +16 -1
  484. package/scripts/secret-scan.sh +75 -13
  485. package/scripts/select-docs-guards.cjs +56 -0
  486. package/scripts/sync-runtime-launcher.cjs +22 -3
  487. package/skills/gsd-discuss-phase/SKILL.md +1 -1
  488. package/skills/gsd-execute-phase/SKILL.md +1 -1
  489. package/skills/gsd-import/SKILL.md +1 -1
  490. package/skills/gsd-ns-workflow/SKILL.md +1 -0
  491. package/skills/gsd-phase/SKILL.md +1 -1
  492. package/skills/gsd-quick/SKILL.md +8 -4
  493. package/skills/gsd-quick-batch/SKILL.md +105 -0
  494. package/skills/gsd-surface/SKILL.md +18 -8
  495. package/vscode/package.json +1 -1
  496. package/bin/lib/ui-safety-gate.cjs +0 -109
  497. package/scripts/lint-emitted-drift-ack.cjs +0 -344
  498. package/scripts/state-write-path-drift-baseline.json +0 -19
@@ -32,6 +32,7 @@ const retiredArtifactCleanup = require("./retired-artifact-cleanup.cjs");
32
32
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
33
33
  const external_descriptor_trust_cjs_1 = require("./external-descriptor-trust.cjs");
34
34
  const commonjs_marker_cjs_1 = require("./commonjs-marker.cjs");
35
+ const testHomeGuard = require("./real-home-guard.cjs");
35
36
  // #2874 (ADR-58 cleanup phase): the injectable fs seam for the
36
37
  // installRuntimeArtifacts call tree. `installFs()` resolves to real
37
38
  // `node:fs` unless a call is wrapped in `withInstallFs(deps.fs, ...)` —
@@ -54,6 +55,148 @@ const { processAttribution } = runtimeArtifactConversion;
54
55
  // resolveRuntimeArtifactLayout: accessed via module ref (not destructured) so
55
56
  // test stubs that monkeypatch the module's exports are seen at call time.
56
57
  const { getDirName } = runtimeNamePolicy;
58
+ function withInstallerPackageSource(configDir, fn) {
59
+ // Reuse the existing compatibility-marker contract through the existing fs
60
+ // seam. The marker exists only in this synchronous call tree: no disk state,
61
+ // layout export, stage argument, or caller-settable authority flag is added.
62
+ const markerPath = node_path_1.default.resolve(configDir, '.gsd-source');
63
+ const packageCommandsRoot = runtimeArtifactLayout.findInstallSourceRoot();
64
+ const markerBytes = Buffer.from(packageCommandsRoot + '\n');
65
+ const base = installFs();
66
+ const overlay = {
67
+ ...base,
68
+ existsSync: (candidate) => node_path_1.default.resolve(candidate) === markerPath || base.existsSync(candidate),
69
+ lstatSync: (candidate) => node_path_1.default.resolve(candidate) === markerPath
70
+ ? { isFile: () => true, isDirectory: () => false, isSymbolicLink: () => false }
71
+ : base.lstatSync(candidate),
72
+ readFileSync: ((candidate, encoding) => {
73
+ if (node_path_1.default.resolve(candidate) !== markerPath) {
74
+ return encoding ? base.readFileSync(candidate, encoding) : base.readFileSync(candidate);
75
+ }
76
+ return encoding ? markerBytes.toString(encoding) : Buffer.from(markerBytes);
77
+ }),
78
+ };
79
+ return withInstallFs(overlay, fn);
80
+ }
81
+ function isRuntimeSurfaceSourceUnavailable(message) {
82
+ return message.startsWith('Runtime Surface source is unavailable or incomplete for ') &&
83
+ message.endsWith('install or upgrade gsd-core before materializing this surface.');
84
+ }
85
+ function assertCorpusTreeHasNoSymlinks(root) {
86
+ if (!installFs().existsSync(root))
87
+ return;
88
+ const stat = installFs().lstatSync(root);
89
+ if (stat.isSymbolicLink()) {
90
+ throw new Error(`Runtime Surface corpus path is a symlink: ${root}`);
91
+ }
92
+ if (!stat.isDirectory())
93
+ return;
94
+ for (const name of installFs().readdirSync(root)) {
95
+ assertCorpusTreeHasNoSymlinks(node_path_1.default.join(root, name));
96
+ }
97
+ }
98
+ function previousOwnedCorpusFiles(configDir, prefix) {
99
+ try {
100
+ const files = installerMigrations.readInstallManifest(configDir).files;
101
+ return Object.keys(files)
102
+ .filter((entry) => entry.startsWith(prefix))
103
+ .map((entry) => entry.slice(prefix.length))
104
+ .filter((entry) => entry !== '' && !node_path_1.default.posix.isAbsolute(entry) && !entry.split('/').some((part) => part === '' || part === '.' || part === '..'));
105
+ }
106
+ catch {
107
+ // An absent or unreadable prior manifest provides no ownership evidence.
108
+ // Preserve existing entries rather than guessing that they are stale.
109
+ return [];
110
+ }
111
+ }
112
+ function pruneEmptyCorpusParents(start, stop) {
113
+ let current = node_path_1.default.dirname(start);
114
+ while (current !== stop && current.startsWith(stop + node_path_1.default.sep)) {
115
+ if (installFs().readdirSync(current).length > 0)
116
+ return;
117
+ installFs().rmdirSync(current);
118
+ current = node_path_1.default.dirname(current);
119
+ }
120
+ }
121
+ function syncRuntimeSurfaceCorpus(source, destination, configDir, manifestPrefix) {
122
+ if (hasExistingSymlinkBetween(node_path_1.default.resolve(configDir), destination, { allowOptInFollow: isSymlinkedDestOptIn() })) {
123
+ throw new Error(`syncRuntimeSurfaceCorpus: destination "${destination}" contains a symlink the install root "${configDir}" does not trust — refusing to write.`);
124
+ }
125
+ assertCorpusTreeHasNoSymlinks(destination);
126
+ // Remove only paths the previous manifest proves GSD owned and which the
127
+ // executing package no longer ships. Unknown neighbouring files survive.
128
+ for (const relative of previousOwnedCorpusFiles(configDir, manifestPrefix)) {
129
+ const sourceEntry = node_path_1.default.join(source, ...relative.split('/'));
130
+ let sourceIsFile = false;
131
+ try {
132
+ sourceIsFile = installFs().lstatSync(sourceEntry).isFile();
133
+ }
134
+ catch {
135
+ sourceIsFile = false;
136
+ }
137
+ if (sourceIsFile)
138
+ continue;
139
+ const target = node_path_1.default.join(destination, ...relative.split('/'));
140
+ if (!installFs().existsSync(target))
141
+ continue;
142
+ const targetStat = installFs().lstatSync(target);
143
+ if (!targetStat.isFile()) {
144
+ throw new Error(`Runtime Surface corpus ownership conflict at ${target}`);
145
+ }
146
+ installFs().rmSync(target, { force: true });
147
+ pruneEmptyCorpusParents(target, destination);
148
+ }
149
+ installFs().mkdirSync(node_path_1.default.dirname(destination), { recursive: true });
150
+ installFs().cpSync(source, destination, { recursive: true });
151
+ }
152
+ /**
153
+ * Provision the raw, installation-owned input needed to re-materialize a
154
+ * global Runtime Surface after the executing package tree disappears.
155
+ *
156
+ * The corpus deliberately lives below the already-installed `gsd-core/`
157
+ * tree and is accepted only after its manifest ownership and hashes verify.
158
+ * The compatibility marker does not replace that installed-corpus authority.
159
+ */
160
+ function provisionRuntimeSurfaceCorpus(layout, configDir, scope) {
161
+ const required = new Set();
162
+ if ((0, install_scope_cjs_1.isGlobalScope)(scope)) {
163
+ for (const kind of layout.kinds) {
164
+ if (kind.kind === 'commands' || kind.kind === 'skills')
165
+ required.add('commands');
166
+ if (kind.kind === 'agents' || kind.kind === 'kimi-agents')
167
+ required.add('agents');
168
+ }
169
+ }
170
+ if (required.size === 0)
171
+ return;
172
+ const corpusRoot = node_path_1.default.join(configDir, 'gsd-core');
173
+ if (required.has('commands')) {
174
+ const source = runtimeArtifactLayout.findInstallSourceRoot();
175
+ const destination = node_path_1.default.join(corpusRoot, 'commands', 'gsd');
176
+ syncRuntimeSurfaceCorpus(source, destination, configDir, 'gsd-core/commands/gsd/');
177
+ }
178
+ if (required.has('agents')) {
179
+ const source = node_path_1.default.join(executingPackageRoot(), 'agents');
180
+ const destination = node_path_1.default.join(corpusRoot, 'agents');
181
+ syncRuntimeSurfaceCorpus(source, destination, configDir, 'gsd-core/agents/');
182
+ }
183
+ const markerFile = _hostBehaviors(layout.runtime).sourceMarkerFile;
184
+ if (typeof markerFile === 'string' && markerFile !== '' && required.has('commands')) {
185
+ try {
186
+ const markerPath = runtimeArtifactInstallPlan.assertDestWithinConfigHome(configDir, markerFile);
187
+ if (hasExistingSymlinkBetween(node_path_1.default.resolve(configDir), markerPath, { allowOptInFollow: isSymlinkedDestOptIn() })) {
188
+ throw new Error(`compatibility marker "${markerPath}" contains an untrusted symlink`);
189
+ }
190
+ installFs().writeFileSync(markerPath, node_path_1.default.join(corpusRoot, 'commands', 'gsd') + '\n', 'utf8');
191
+ }
192
+ catch {
193
+ // The existing installer marker writer owns the user-facing warning and
194
+ // keeps marker failure non-fatal. The installed corpus remains usable
195
+ // without the compatibility marker.
196
+ }
197
+ }
198
+ }
199
+ function executingPackageRoot() { return node_path_1.default.dirname(node_path_1.default.dirname(runtimeArtifactLayout.findInstallSourceRoot())); }
57
200
  // ---------------------------------------------------------------------------
58
201
  // USER_OWNED_ARTIFACTS
59
202
  // ---------------------------------------------------------------------------
@@ -409,6 +552,15 @@ function _resolveDevPreferencesSkillTarget(targetDir, runtime, scope = 'global')
409
552
  // must confine against installRoot, not targetDir, or it would flag the
410
553
  // legitimate override destination as an escape.
411
554
  let installRoot = targetDir;
555
+ // Reported in Codex review of #3725: `installRoot !== targetDir` was used as the
556
+ // stand-in for "the skills kind declared a `home` override", and the two are NOT
557
+ // equivalent — a resolved `home` that happens to EQUAL targetDir (a configDir of
558
+ // `$HOME/.agents`, which is exactly where codex's override points) makes the
559
+ // inequality false while the override is very much declared, skipping the guard
560
+ // and writing SKILL.md into the real home. Report the declaration itself instead
561
+ // of inferring it from two paths, read off the SAME layout resolution the
562
+ // destination came from so the guard cannot vouch for a path this does not write.
563
+ let hasHomeOverride = false;
412
564
  if (runtime) {
413
565
  const layout = runtimeArtifactLayout.resolveRuntimeArtifactLayout(runtime, targetDir, scope);
414
566
  const skillsKindEntry = layout.kinds.find((k) => k.kind === 'skills');
@@ -420,6 +572,7 @@ function _resolveDevPreferencesSkillTarget(targetDir, runtime, scope = 'global')
420
572
  // -> $HOME/.agents) instead of always resolving against targetDir, so a
421
573
  // legacy dev-preferences migration lands in the SAME tree the installer
422
574
  // and surface-apply use. Runtimes with no `home` override are unaffected.
575
+ hasHomeOverride = skillsKindEntry.home != null;
423
576
  installRoot = skillsKindEntry.home ?? targetDir;
424
577
  skillDir = node_path_1.default.join(runtimeArtifactInstallPlan.assertDestWithinConfigHome(installRoot, skillsKindEntry.destSubpath), stemName);
425
578
  }
@@ -427,14 +580,42 @@ function _resolveDevPreferencesSkillTarget(targetDir, runtime, scope = 'global')
427
580
  // Legacy fallback for callers that have not yet been updated to pass runtime
428
581
  skillDir = node_path_1.default.join(runtimeArtifactInstallPlan.assertDestWithinConfigHome(targetDir, 'skills'), 'gsd-dev-preferences');
429
582
  }
430
- return { skillFile: node_path_1.default.join(skillDir, 'SKILL.md'), installRoot };
583
+ return { skillFile: node_path_1.default.join(skillDir, 'SKILL.md'), installRoot, hasHomeOverride };
431
584
  }
432
- function migrateLegacyDevPreferencesToSkill(targetDir, saved, runtime, scope = 'global') {
585
+ /**
586
+ * @param deps - #3712 test seam, mirroring the one on `installRuntimeArtifacts`
587
+ * and `uninstallRuntimeArtifacts`. This is the SIXTH writer that resolves a
588
+ * skills-kind `home`, and its guard's trigger condition — "HOME equals the
589
+ * passwd home" — cannot be reproduced without pointing at the developer's real
590
+ * home, so it is injected rather than simulated. Production callers pass
591
+ * nothing and bind real `os`/`process.env`.
592
+ */
593
+ function migrateLegacyDevPreferencesToSkill(targetDir, saved, runtime, scope = 'global', deps = {}) {
433
594
  if (!saved || !saved.has('dev-preferences.md'))
434
595
  return false;
435
596
  const target = _resolveDevPreferencesSkillTarget(targetDir, runtime, scope);
436
597
  if (!target)
437
598
  return false; // runtime has no skills layout at this scope (e.g. cline local)
599
+ // #3712 — the SIXTH writer that resolves a skills-kind `home` override.
600
+ // Exported and directly callable, and `_runLegacyInstallMigrations` runs it
601
+ // BEFORE installRuntimeArtifacts' own assertion, so a future runtime pairing a
602
+ // home override with this migration would write to the real home ahead of any
603
+ // guard. It creates rather than prunes, which is why it was missed.
604
+ //
605
+ // Guards the destination ALREADY RESOLVED above, never a second resolution of
606
+ // its own. An earlier revision re-ran resolveRuntimeArtifactLayout() here —
607
+ // and without `capabilityRegistry`, so a registry-dependent descriptor could
608
+ // make the two disagree and leave the guard vouching for a path the migration
609
+ // does not write. That is the generative-fix-divergence shape; reported in
610
+ // review of #3725. `target.hasHomeOverride` is that same resolution's own answer
611
+ // to "did the skills kind declare a `home`?" — not re-derived, and not inferred
612
+ // from `installRoot !== targetDir`, which is false whenever the override happens
613
+ // to resolve onto targetDir itself (Codex review of #3725).
614
+ if (runtime && target.hasHomeOverride) {
615
+ testHomeGuard.assertTestHomeSandboxed('migrateLegacyDevPreferencesToSkill', runtime, [
616
+ { kind: 'skills', home: node_path_1.default.dirname(target.skillFile) },
617
+ ], { os: deps.os, env: deps.env });
618
+ }
438
619
  const { skillFile, installRoot } = target;
439
620
  const skillDir = node_path_1.default.dirname(skillFile);
440
621
  // Security fix: `existsSync` FOLLOWS symlinks and reports `false` for a
@@ -938,6 +1119,7 @@ function _runLegacyUninstallCleanup(runtime, configDir, scope = 'global') {
938
1119
  */
939
1120
  function installRuntimeArtifacts(runtime, configDir, scope, resolvedProfile, resolveAttribution = () => undefined, capabilityRegistry, deps = {}) {
940
1121
  return withInstallFs(deps.fs, () => {
1122
+ const layout = runtimeArtifactLayout.resolveRuntimeArtifactLayout(runtime, configDir, scope, capabilityRegistry);
941
1123
  // A removed descriptor kind is no longer visited by the layout loop, so it
942
1124
  // cannot prune its own previous output. Clean manifest-proven retired files
943
1125
  // before materializing the current layout (#2644).
@@ -947,6 +1129,7 @@ function installRuntimeArtifacts(runtime, configDir, scope, resolvedProfile, res
947
1129
  // generic layout-driven loop below, mirroring the bespoke install path that
948
1130
  // previously lived inline in bin/install.js.
949
1131
  const behaviors = _hostBehaviors(runtime);
1132
+ const projectDir = scope === 'global' ? process.cwd() : configDir;
950
1133
  if (behaviors.combinedFamilyInstall) {
951
1134
  // #2329: combined-family runtimes (OpenCode/Kilo) bypass
952
1135
  // _runLegacyInstallMigrations below entirely (early return), so their
@@ -955,12 +1138,24 @@ function installRuntimeArtifacts(runtime, configDir, scope, resolvedProfile, res
955
1138
  // #2874 design row 2: this early return must ALSO return an executed
956
1139
  // plan — installOpencodeFamilyArtifacts reports what it wrote, so a
957
1140
  // whole runtime family returning undefined is no longer a hole.
958
- return installOpencodeFamilyArtifacts(runtime, configDir, scope, resolvedProfile, resolveAttribution, behaviors, capabilityRegistry);
1141
+ // An injected filesystem supplies its own hermetic corpus fixture. The
1142
+ // real installer is the authority that refreshes package bytes into the
1143
+ // durable installed corpus; attempting that cross-filesystem copy through
1144
+ // an in-memory destination adapter would read from the wrong filesystem.
1145
+ if (!deps.fs)
1146
+ provisionRuntimeSurfaceCorpus(layout, configDir, scope);
1147
+ return installOpencodeFamilyArtifacts(runtime, configDir, scope, resolvedProfile, resolveAttribution, behaviors, capabilityRegistry, deps.packageRoot, projectDir);
959
1148
  }
960
1149
  // Legacy cleanup before layout-driven writes
961
1150
  _runLegacyInstallMigrations(runtime, configDir, scope);
962
- const layout = runtimeArtifactLayout.resolveRuntimeArtifactLayout(runtime, configDir, scope, capabilityRegistry);
963
- const planResult = runtimeArtifactInstallPlan.createRuntimeArtifactInstallPlan({
1151
+ if (!deps.fs)
1152
+ provisionRuntimeSurfaceCorpus(layout, configDir, scope);
1153
+ // #3712: a global `home` override escapes the sandboxed configDir. Refuse to
1154
+ // execute when a test run would land that escape in the developer's real home.
1155
+ testHomeGuard.assertTestHomeSandboxed('installRuntimeArtifacts', runtime, layout?.kinds, {
1156
+ os: deps.os, env: deps.env,
1157
+ });
1158
+ const createPlan = () => runtimeArtifactInstallPlan.createRuntimeArtifactInstallPlan({
964
1159
  // `Layout` is structurally identical across the layout/install-plan .cjs
965
1160
  // modules but nominally distinct to tsc (untyped .cjs boundary) — bridge it.
966
1161
  layout: layout,
@@ -968,7 +1163,16 @@ function installRuntimeArtifacts(runtime, configDir, scope, resolvedProfile, res
968
1163
  homedir: () => node_os_1.default.homedir(),
969
1164
  platform: process.platform,
970
1165
  resolveAttribution,
1166
+ projectDir,
971
1167
  });
1168
+ let planResult = createPlan();
1169
+ if (scope === 'global' &&
1170
+ !planResult.ok &&
1171
+ planResult.kind === 'stage_failed' &&
1172
+ planResult.cleanupDirs.length === 0 &&
1173
+ isRuntimeSurfaceSourceUnavailable(planResult.message)) {
1174
+ planResult = withInstallerPackageSource(configDir, createPlan);
1175
+ }
972
1176
  const cleanupDirs = planResult.ok ? planResult.plan.cleanupDirs : planResult.cleanupDirs;
973
1177
  // #2874 row 1/4/5: per-kind executed-plan entries, appended only as the
974
1178
  // loop below actually finishes writing each kind — a kind that throws
@@ -1132,8 +1336,10 @@ function installRuntimeArtifacts(runtime, configDir, scope, resolvedProfile, res
1132
1336
  // is safe even when configDir has no .gsd-source marker (artifactLayout: []).
1133
1337
  let nativePluginInstalled = false;
1134
1338
  if (behaviors.nativePlugin) {
1135
- const commandsGsdDir = runtimeArtifactLayout.findInstallSourceRoot(configDir);
1136
- const src = node_path_1.default.dirname(node_path_1.default.dirname(commandsGsdDir));
1339
+ // Native plugin sources live only in the executing package, never in the
1340
+ // durable Runtime Surface corpus. Do not derive this package root from a
1341
+ // config-scoped provider that may now correctly resolve installed input.
1342
+ const src = deps.packageRoot ?? executingPackageRoot();
1137
1343
  _installNativePluginIfDeclared(runtime, configDir, behaviors, src);
1138
1344
  nativePluginInstalled = true;
1139
1345
  }
@@ -1183,6 +1389,13 @@ function installOpencodeFamilySkills(runtime, targetDir, rawCommandsDir, pathPre
1183
1389
  const skillsKindEntry = layout.kinds.find((k) => k.kind === 'skills');
1184
1390
  if (!skillsKindEntry)
1185
1391
  return 0;
1392
+ // #3712: combined-family runtimes take installRuntimeArtifacts' early return
1393
+ // BEFORE its guard runs, and this writer honors `skillsKindEntry.home` below and
1394
+ // then prunes that destination. opencode/kilo declare no `home` today, so there
1395
+ // is no live escape — but that makes this a bypass waiting on a descriptor
1396
+ // change rather than a safe omission, so it is guarded at the writer instead.
1397
+ // Scoped to the SKILLS kind alone, for the same reason as the agents writer.
1398
+ testHomeGuard.assertTestHomeSandboxed('installOpencodeFamilySkills', runtime, [skillsKindEntry]);
1186
1399
  const rawDir = rawCommandsDir;
1187
1400
  if (!rawDir || !installFs().existsSync(rawDir))
1188
1401
  return 0;
@@ -1337,20 +1550,37 @@ function installOpencodeFamilySkills(runtime, targetDir, rawCommandsDir, pathPre
1337
1550
  * @param capabilityRegistry - #2362: optional composed capability registry, threaded
1338
1551
  * straight through to resolveRuntimeArtifactLayout (unused by the agents kind today,
1339
1552
  * but kept for signature parity with the skills/commands siblings on this call tree)
1553
+ * @param projectDir - project/config discovery root, distinct from the artifact destination
1340
1554
  * @returns `{ sourceDir, destDir }` describing what was written, or `null` when the
1341
1555
  * runtime's layout declares no `agents` kind.
1342
1556
  */
1343
- function installAgentsKindStandalone(runtime, targetDir, scope, resolvedProfile, pathPrefix, resolveAttribution = () => undefined, capabilityRegistry) {
1557
+ function installAgentsKindStandalone(runtime, targetDir, scope, resolvedProfile, pathPrefix, resolveAttribution = () => undefined, capabilityRegistry, projectDir) {
1344
1558
  const layout = runtimeArtifactLayout.resolveRuntimeArtifactLayout(runtime, targetDir, scope, capabilityRegistry);
1345
- const agentsKindEntry = layout.kinds.find((k) => k.kind === 'agents');
1559
+ const agentsKindEntry = layout.kinds.find((kind) => kind.kind === 'agents');
1346
1560
  if (!agentsKindEntry)
1347
1561
  return null;
1562
+ // #3712: this writer selects `agentsKindEntry.home` over targetDir below and then
1563
+ // prunes that destination via _removeGsdEntries, so it is a fifth route into the
1564
+ // developer's real home. No agents kind declares a `home` override today, so like
1565
+ // installOpencodeFamilySkills it is guarded against a descriptor change rather
1566
+ // than a present escape. Scoped to the AGENTS kind alone: passing the whole
1567
+ // layout made codex's unrelated skills-kind override trip a writer that never
1568
+ // touches it, which is a false refusal, not a tighter guard.
1569
+ testHomeGuard.assertTestHomeSandboxed('installAgentsKindStandalone', runtime, [agentsKindEntry]);
1348
1570
  // ADR-1235 §1: same agentCtx shape createRuntimeArtifactInstallPlan builds
1349
1571
  // for the generic layout-driven loop (runtime-artifact-install-plan.cts) —
1350
1572
  // targetDir IS the install root the inline agent loop called `targetDir`.
1351
1573
  const attribution = resolveAttribution ? resolveAttribution(runtime) : undefined;
1352
- const agentCtx = { runtime, pathPrefix, attribution, targetDir };
1353
- const stagedDir = agentsKindEntry.stage(resolvedProfile, agentCtx);
1574
+ const agentCtx = { runtime, pathPrefix, attribution, targetDir, projectDir: projectDir ?? targetDir };
1575
+ let stagedDir;
1576
+ try {
1577
+ stagedDir = agentsKindEntry.stage(resolvedProfile, agentCtx);
1578
+ }
1579
+ catch (err) {
1580
+ if (scope !== 'global' || !isRuntimeSurfaceSourceUnavailable(err.message))
1581
+ throw err;
1582
+ stagedDir = withInstallerPackageSource(targetDir, () => agentsKindEntry.stage(resolvedProfile, agentCtx));
1583
+ }
1354
1584
  const stagedAgentFiles = installFs().existsSync(stagedDir)
1355
1585
  ? installFs().readdirSync(stagedDir).filter((f) => f.endsWith('.md'))
1356
1586
  : [];
@@ -1611,12 +1841,13 @@ function _migrateLegacyOpencodeCommandDir(runtime, configDir, behaviors) {
1611
1841
  * installOpencodeFamilySkills so an installed third-party capability skill
1612
1842
  * materializes for this combined-family (OpenCode/Kilo) install path too.
1613
1843
  * Absent -> no third-party skills staged (fail closed).
1844
+ * @param projectDir - project/config discovery root, distinct from configDir for global installs
1614
1845
  * @returns #2874 design row 2: an executed-plan value, same top-level shape
1615
1846
  * (`runtime`/`scope`/`kinds`/`cleanup`/`postSteps`) as the generic
1616
1847
  * `installRuntimeArtifacts` branch — this was the one early return a
1617
1848
  * `void`-shaped hole survived unnoticed in.
1618
1849
  */
1619
- function installOpencodeFamilyArtifacts(runtime, configDir, scope, resolvedProfile, resolveAttribution = () => undefined, behaviors = {}, capabilityRegistry) {
1850
+ function installOpencodeFamilyArtifacts(runtime, configDir, scope, resolvedProfile, resolveAttribution = () => undefined, behaviors = {}, capabilityRegistry, packageRoot, projectDir) {
1620
1851
  // #2870: `scope` keeps its exported required `string` signature (no
1621
1852
  // signature change). It is always the `installRuntimeArtifacts`-forwarded
1622
1853
  // 'global' | 'local' literal produced by bin/install.js's scope-resolution
@@ -1629,7 +1860,7 @@ function installOpencodeFamilyArtifacts(runtime, configDir, scope, resolvedProfi
1629
1860
  // into stageSkillsForProfile/stageSkillsForRuntimeAsSkills. The repo/package
1630
1861
  // root (needed below for the native plugin source) is two levels up.
1631
1862
  const commandsGsdDir = runtimeArtifactLayout.findInstallSourceRoot(configDir);
1632
- const src = node_path_1.default.dirname(node_path_1.default.dirname(commandsGsdDir));
1863
+ const src = packageRoot ?? executingPackageRoot();
1633
1864
  const rawCommandsDir = installProfiles.stageSkillsForProfile(commandsGsdDir, resolvedProfile);
1634
1865
  const pathPrefix = runtimeArtifactConversion._computePathPrefix({
1635
1866
  isGlobal,
@@ -1652,7 +1883,7 @@ function installOpencodeFamilyArtifacts(runtime, configDir, scope, resolvedProfi
1652
1883
  // generic layout-driven loop uses (see installAgentsKindStandalone's own
1653
1884
  // doc). A `null` result means this runtime's layout declares no `agents`
1654
1885
  // kind — nothing written, nothing reported (no #1879-F15 inert claim).
1655
- const agentsResult = installAgentsKindStandalone(runtime, configDir, scope, resolvedProfile, pathPrefix, resolveAttribution, capabilityRegistry);
1886
+ const agentsResult = installAgentsKindStandalone(runtime, configDir, scope, resolvedProfile, pathPrefix, resolveAttribution, capabilityRegistry, projectDir);
1656
1887
  _installNativePluginIfDeclared(runtime, configDir, behaviors, src);
1657
1888
  // #2874 design row 2: report what this combined-family install wrote,
1658
1889
  // mirroring the generic branch's top-level shape. `cleanup` is `[]` — this
@@ -1683,7 +1914,7 @@ function installOpencodeFamilyArtifacts(runtime, configDir, scope, resolvedProfi
1683
1914
  * @param configDir resolved runtime config directory
1684
1915
  * @param scope
1685
1916
  */
1686
- function uninstallRuntimeArtifacts(runtime, configDir, scope) {
1917
+ function uninstallRuntimeArtifacts(runtime, configDir, scope, deps = {}) {
1687
1918
  // A retired descriptor kind is absent from the current uninstall plan, just
1688
1919
  // as it is absent from the install plan. Sweep manifest-proven output from
1689
1920
  // retired kinds before removing the current layout so a direct uninstall
@@ -1695,6 +1926,12 @@ function uninstallRuntimeArtifacts(runtime, configDir, scope) {
1695
1926
  // (the layout's gsd-* prefix pass would wipe a skill dir created here).
1696
1927
  const stagedLegacyArtifacts = _runLegacyUninstallCleanup(runtime, configDir, scope);
1697
1928
  const layout = runtimeArtifactLayout.resolveRuntimeArtifactLayout(runtime, configDir, scope);
1929
+ // #3712: uninstall resolves the SAME `kind.home` override as install and then
1930
+ // prunes it via _removeGsdEntries below, so it is a second escape route into
1931
+ // the developer's real home, not a read-only path. Guard it identically.
1932
+ testHomeGuard.assertTestHomeSandboxed('uninstallRuntimeArtifacts', runtime, layout?.kinds, {
1933
+ os: deps.os, env: deps.env,
1934
+ });
1698
1935
  const plan = runtimeArtifactInstallPlan.createRuntimeArtifactUninstallPlan(layout);
1699
1936
  const kindsByName = new Map(layout.kinds.map((kind) => [kind.kind, kind]));
1700
1937
  for (const item of plan.items) {
@@ -45,7 +45,7 @@ const modelCatalog = require("./model-catalog.cjs");
45
45
  const { MODEL_PROFILES: GSD_MODEL_PROFILES } = modelCatalog;
46
46
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- model-resolver.cjs is an export= CommonJS module
47
47
  const modelResolverModule = require("./model-resolver.cjs");
48
- const { resolveTierEntry: gsdResolveTierEntry } = modelResolverModule;
48
+ const { resolveTierEntry: gsdResolveTierEntry, resolveModelPolicy: gsdResolveModelPolicy } = modelResolverModule;
49
49
  /**
50
50
  * Read `model_overrides` from `~/.gsd/defaults.json` at install time.
51
51
  * Returns an object mapping agent names to model IDs, or null if the file
@@ -114,6 +114,50 @@ function readGsdEffectiveModelOverrides(targetDir = null, options = {}) {
114
114
  // Per-project wins on conflict; preserve non-conflicting global keys.
115
115
  return { ...(global || {}), ...(projectOverrides || {}) };
116
116
  }
117
+ function readGsdAgentTools(config) {
118
+ const raw = config?.agent_tools;
119
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw))
120
+ return null;
121
+ const result = {};
122
+ for (const [selector, value] of Object.entries(raw)) {
123
+ // An explicitly present selector always has a verdict. Invalid values are
124
+ // empty so a project config cannot accidentally restore a global grant.
125
+ result[selector] = Array.isArray(value)
126
+ ? value.filter((entry) => typeof entry === 'string')
127
+ .map((entry) => entry.trim())
128
+ .filter((entry) => entry.length > 0
129
+ && !entry.endsWith(':')
130
+ && !/[\s\u0000-\u001F\u007F-\u009F,#"'\u2028\u2029]/.test(entry))
131
+ : [];
132
+ }
133
+ return result;
134
+ }
135
+ /**
136
+ * Resolve valid install-time `agent_tools` grants from global defaults and
137
+ * the nearest project config. Project selectors replace only matching global
138
+ * selectors; malformed whole files remain harmless absence like the existing
139
+ * model override resolver.
140
+ */
141
+ function readGsdEffectiveAgentTools(targetDir = null, options = {}) {
142
+ const home = options.homedir ? options.homedir() : node_os_1.default.homedir();
143
+ const globalConfig = _readGsdConfigFile(node_path_1.default.join(home, '.gsd', 'defaults.json'), 'global defaults');
144
+ let projectConfig = null;
145
+ if (targetDir) {
146
+ const candidate = _findAncestorGsdConfigPath(targetDir);
147
+ if (candidate) {
148
+ projectConfig = _readGsdConfigFile(candidate, 'project config');
149
+ }
150
+ }
151
+ const global = readGsdAgentTools(globalConfig);
152
+ const project = readGsdAgentTools(projectConfig);
153
+ if (projectConfig
154
+ && Object.prototype.hasOwnProperty.call(projectConfig, 'agent_tools')
155
+ && project === null)
156
+ return {};
157
+ if (!global && !project)
158
+ return null;
159
+ return { ...(global || {}), ...(project || {}) };
160
+ }
117
161
  /**
118
162
  * Build a runtime-aware tier resolver for the install path (#2517).
119
163
  *
@@ -145,6 +189,10 @@ function readGsdRuntimeProfileResolver(targetDir = null) {
145
189
  model_profile_overrides: (projectConfig && projectConfig.model_profile_overrides) ||
146
190
  (homeDefaults && homeDefaults.model_profile_overrides) ||
147
191
  null,
192
+ // #3705: same project-wins-over-home precedence as every other key here.
193
+ model_policy: (projectConfig && projectConfig.model_policy) ||
194
+ (homeDefaults && homeDefaults.model_policy) ||
195
+ null,
148
196
  };
149
197
  if (!merged.runtime)
150
198
  return null;
@@ -164,6 +212,34 @@ function readGsdRuntimeProfileResolver(targetDir = null) {
164
212
  const tier = agentModels[profile] || agentModels.balanced;
165
213
  if (!tier)
166
214
  return null;
215
+ // #3705: policy sits between the per-agent override and the tier table —
216
+ // the position dispatch uses. Measured against `query resolve-model` on the
217
+ // same config: policy-only -> the policy model; tier-overrides-only -> the
218
+ // override; BOTH -> the policy model. So policy outranks
219
+ // `model_profile_overrides`, and an explicit `model_overrides[agent]`
220
+ // (applied by resolveAgentModelOverride below) outranks policy.
221
+ //
222
+ // Calls the exported owner (#49) rather than re-deriving tier -> model here:
223
+ // a second implementation of that mapping is the very divergence this issue
224
+ // is, one layer down. A null from it (unknown provider, missing tier key, a
225
+ // `runtime_tiers` miss) falls through to the tier table — never to null,
226
+ // which would omit a frontmatter key that used to be written.
227
+ // The policy object must carry the effective runtime before it is resolved.
228
+ // `resolveModelPolicy`'s `runtime_tiers` branch reads `policy['runtime']`, but
229
+ // the documented config shape (docs/CONFIGURATION.md) puts `runtime` at the
230
+ // TOP level and keeps only `provider`/`runtime_tiers` inside `model_policy`.
231
+ // Dispatch injects it — `{ ...config.model_policy, runtime: effectiveRuntime }`
232
+ // (model-resolver.cts) — and passing the policy unmodified here silently
233
+ // skipped `runtime_tiers` entirely, falling through to the flat hi/med/lo keys
234
+ // or the catalog tier. That is the same "catalog Anthropic ID baked over a
235
+ // configured provider" defect this fix exists to close, so the injection is
236
+ // mirrored rather than assumed.
237
+ const policyForRuntime = merged.model_policy
238
+ ? { ...merged.model_policy, runtime }
239
+ : null;
240
+ const policyModel = gsdResolveModelPolicy(policyForRuntime, tier);
241
+ if (policyModel)
242
+ return { model: policyModel };
167
243
  return gsdResolveTierEntry({
168
244
  runtime,
169
245
  tier,
@@ -198,6 +274,7 @@ function resolveAgentModelOverride(agentName, modelOverrides, runtimeResolver) {
198
274
  module.exports = {
199
275
  readGsdGlobalModelOverrides,
200
276
  readGsdEffectiveModelOverrides,
277
+ readGsdEffectiveAgentTools,
201
278
  readGsdRuntimeProfileResolver,
202
279
  resolveAgentModelOverride,
203
280
  };