@opengsd/gsd-core 1.10.0 → 1.11.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 (328) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/agents/gsd-debug-session-manager.md +11 -0
  4. package/agents/gsd-doc-synthesizer.md +2 -4
  5. package/agents/gsd-executor.md +5 -5
  6. package/agents/gsd-mempalace-curator.md +5 -2
  7. package/agents/gsd-phase-researcher.md +20 -1
  8. package/agents/gsd-plan-checker.md +37 -0
  9. package/agents/gsd-planner.md +44 -46
  10. package/agents/gsd-user-profiler.md +3 -0
  11. package/agents/gsd-verifier.md +12 -3
  12. package/bin/install.js +841 -971
  13. package/bin/lib/ui-safety-gate.cjs +2 -0
  14. package/commands/gsd/code-review.md +1 -1
  15. package/commands/gsd/execute-phase.md +1 -1
  16. package/commands/gsd/map-codebase.md +1 -1
  17. package/commands/gsd/mempalace-capture.md +1 -1
  18. package/commands/gsd/mempalace-recall.md +1 -1
  19. package/commands/gsd/new-milestone.md +1 -1
  20. package/commands/gsd/quick.md +1 -1
  21. package/commands/gsd/review-backlog.md +2 -1
  22. package/commands/gsd/verify-work.md +1 -1
  23. package/gsd-core/bin/gsd-tools.cjs +469 -88
  24. package/gsd-core/bin/lib/active-workstream-store.cjs +138 -22
  25. package/gsd-core/bin/lib/agent-install-check.cjs +230 -32
  26. package/gsd-core/bin/lib/api-coverage.cjs +3 -5
  27. package/gsd-core/bin/lib/artifacts.cjs +3 -0
  28. package/gsd-core/bin/lib/assumption-delta.cjs +2 -4
  29. package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
  30. package/gsd-core/bin/lib/audit.cjs +876 -240
  31. package/gsd-core/bin/lib/broken-windows.cjs +1 -1
  32. package/gsd-core/bin/lib/capability-consent.cjs +149 -15
  33. package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
  34. package/gsd-core/bin/lib/capability-registry.cjs +575 -101
  35. package/gsd-core/bin/lib/capability-source.cjs +92 -0
  36. package/gsd-core/bin/lib/capability-trust.cjs +444 -25
  37. package/gsd-core/bin/lib/capability-validator.cjs +495 -22
  38. package/gsd-core/bin/lib/capability-writer.cjs +3 -2
  39. package/gsd-core/bin/lib/check-command-router.cjs +71 -37
  40. package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
  41. package/gsd-core/bin/lib/codex-agent-toml.cjs +329 -0
  42. package/gsd-core/bin/lib/command-aliases.cjs +22 -0
  43. package/gsd-core/bin/lib/command-roster.cjs +44 -1
  44. package/gsd-core/bin/lib/commands.cjs +651 -86
  45. package/gsd-core/bin/lib/commonjs-marker.cjs +12 -6
  46. package/gsd-core/bin/lib/complexity-trigger.cjs +1172 -0
  47. package/gsd-core/bin/lib/config-loader.cjs +75 -0
  48. package/gsd-core/bin/lib/config.cjs +10 -1
  49. package/gsd-core/bin/lib/core-utils.cjs +127 -29
  50. package/gsd-core/bin/lib/decisions.cjs +23 -0
  51. package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
  52. package/gsd-core/bin/lib/frontmatter.cjs +155 -20
  53. package/gsd-core/bin/lib/gap-checker.cjs +68 -7
  54. package/gsd-core/bin/lib/git-base-branch.cjs +102 -0
  55. package/gsd-core/bin/lib/gsd2-import.cjs +10 -1
  56. package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
  57. package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
  58. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +145 -0
  59. package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
  60. package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
  61. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
  62. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +265 -0
  63. package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
  64. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
  65. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +173 -0
  66. package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
  67. package/gsd-core/bin/lib/health-diagnostic.cjs +431 -0
  68. package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
  69. package/gsd-core/bin/lib/init.cjs +321 -129
  70. package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
  71. package/gsd-core/bin/lib/install-engine.cjs +745 -258
  72. package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
  73. package/gsd-core/bin/lib/install-model-override-resolver.cjs +203 -0
  74. package/gsd-core/bin/lib/install-profiles.cjs +134 -57
  75. package/gsd-core/bin/lib/install-scope.cjs +270 -0
  76. package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
  77. package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
  78. package/gsd-core/bin/lib/installer-migrations.cjs +138 -31
  79. package/gsd-core/bin/lib/io.cjs +10 -0
  80. package/gsd-core/bin/lib/markdown-sectionizer.cjs +2 -1
  81. package/gsd-core/bin/lib/markdown-table.cjs +133 -20
  82. package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
  83. package/gsd-core/bin/lib/milestone.cjs +754 -70
  84. package/gsd-core/bin/lib/model-catalog.cjs +59 -1
  85. package/gsd-core/bin/lib/model-resolver.cjs +183 -40
  86. package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
  87. package/gsd-core/bin/lib/pattern.cjs +122 -0
  88. package/gsd-core/bin/lib/phase-estimation.cjs +1 -1
  89. package/gsd-core/bin/lib/phase-id.cjs +444 -36
  90. package/gsd-core/bin/lib/phase-lifecycle.cjs +28 -3
  91. package/gsd-core/bin/lib/phase-locator.cjs +125 -18
  92. package/gsd-core/bin/lib/phase.cjs +646 -143
  93. package/gsd-core/bin/lib/plan-dependency-graph.cjs +72 -1
  94. package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
  95. package/gsd-core/bin/lib/plan-scan.cjs +86 -2
  96. package/gsd-core/bin/lib/planning-scope.cjs +31 -0
  97. package/gsd-core/bin/lib/planning-snapshot.cjs +890 -0
  98. package/gsd-core/bin/lib/planning-workspace.cjs +56 -6
  99. package/gsd-core/bin/lib/probe-core.cjs +1 -1
  100. package/gsd-core/bin/lib/profile-output.cjs +1 -1
  101. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +740 -0
  102. package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +11 -6
  103. package/gsd-core/bin/lib/review-lane-descriptor.cjs +13 -4
  104. package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
  105. package/gsd-core/bin/lib/review-lane-runner.cjs +421 -66
  106. package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
  107. package/gsd-core/bin/lib/roadmap-command-router.cjs +34 -0
  108. package/gsd-core/bin/lib/roadmap-parser.cjs +943 -184
  109. package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
  110. package/gsd-core/bin/lib/roadmap.cjs +385 -94
  111. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +608 -46
  112. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
  113. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +426 -55
  114. package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
  115. package/gsd-core/bin/lib/runtime-homes.cjs +69 -3
  116. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +115 -3
  117. package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
  118. package/gsd-core/bin/lib/runtime-slash.cjs +27 -9
  119. package/gsd-core/bin/lib/security.cjs +104 -5
  120. package/gsd-core/bin/lib/shell-command-projection.cjs +275 -3
  121. package/gsd-core/bin/lib/smart-entry.cjs +142 -22
  122. package/gsd-core/bin/lib/state-command-router.cjs +5 -1
  123. package/gsd-core/bin/lib/state-document.cjs +152 -8
  124. package/gsd-core/bin/lib/state-transition.cjs +371 -117
  125. package/gsd-core/bin/lib/state.cjs +1794 -357
  126. package/gsd-core/bin/lib/surface.cjs +23 -9
  127. package/gsd-core/bin/lib/text-lines.cjs +80 -0
  128. package/gsd-core/bin/lib/token-scanner.cjs +76 -0
  129. package/gsd-core/bin/lib/uat-predicate.cjs +9 -3
  130. package/gsd-core/bin/lib/uat.cjs +399 -56
  131. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
  132. package/gsd-core/bin/lib/ui-safety-gate.cjs +14 -5
  133. package/gsd-core/bin/lib/unusable-input.cjs +24 -0
  134. package/gsd-core/bin/lib/update-context.cjs +8 -2
  135. package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
  136. package/gsd-core/bin/lib/validate.cjs +20 -6
  137. package/gsd-core/bin/lib/vendor/README.md +37 -0
  138. package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
  139. package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
  140. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  141. package/gsd-core/bin/lib/verification.cjs +258 -8
  142. package/gsd-core/bin/lib/verify.cjs +368 -888
  143. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +53 -32
  144. package/gsd-core/bin/lib/workstream-inventory.cjs +63 -10
  145. package/gsd-core/bin/lib/workstream.cjs +2 -2
  146. package/gsd-core/bin/lib/worktree-safety.cjs +176 -9
  147. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  148. package/gsd-core/bin/shared/config-schema.manifest.json +7 -1
  149. package/gsd-core/references/agent-contracts.md +43 -26
  150. package/gsd-core/references/checkpoints.md +2 -2
  151. package/gsd-core/references/context-budget.md +1 -1
  152. package/gsd-core/references/dispatch-isolation-gate.md +138 -0
  153. package/gsd-core/references/doc-conflict-engine.md +1 -1
  154. package/gsd-core/references/execute-mvp-tdd.md +3 -3
  155. package/gsd-core/references/execute-phase-between-wave-reset.md +6 -2
  156. package/gsd-core/references/execute-phase-context-guard.md +1 -1
  157. package/gsd-core/references/execute-phase-response-language.md +1 -1
  158. package/gsd-core/references/execute-phase-wave-guard.md +6 -2
  159. package/gsd-core/references/gate-prompts.md +1 -1
  160. package/gsd-core/references/git-planning-commit.md +2 -1
  161. package/gsd-core/references/loop-hook-dispatch.md +39 -2
  162. package/gsd-core/references/model-profiles.md +12 -4
  163. package/gsd-core/references/mvp-concepts.md +9 -9
  164. package/gsd-core/references/planner-guidance.md +3 -9
  165. package/gsd-core/references/planner-preconditions.md +1 -1
  166. package/gsd-core/references/planner-reviews.md +1 -1
  167. package/gsd-core/references/planning-config.md +8 -6
  168. package/gsd-core/references/revision-loop.md +1 -1
  169. package/gsd-core/references/specless-probe-fallback.md +1 -1
  170. package/gsd-core/references/universal-anti-patterns.md +3 -3
  171. package/gsd-core/references/verifier-phase-gates.md +192 -0
  172. package/gsd-core/references/verify-mvp-mode.md +1 -1
  173. package/gsd-core/references/workstream-flag.md +22 -6
  174. package/gsd-core/templates/discussion-log.md +1 -1
  175. package/gsd-core/templates/phase-prompt.md +2 -4
  176. package/gsd-core/templates/state.md +4 -4
  177. package/gsd-core/templates/verification-report.md +9 -1
  178. package/gsd-core/workflows/ai-integration-phase.md +9 -11
  179. package/gsd-core/workflows/autonomous.md +1 -1
  180. package/gsd-core/workflows/cleanup.md +62 -3
  181. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +13 -3
  182. package/gsd-core/workflows/code-review-fix.md +37 -10
  183. package/gsd-core/workflows/code-review.md +38 -12
  184. package/gsd-core/workflows/complete-milestone.md +141 -18
  185. package/gsd-core/workflows/debug.md +7 -5
  186. package/gsd-core/workflows/diagnose-issues.md +35 -9
  187. package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -1
  188. package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
  189. package/gsd-core/workflows/discuss-phase-assumptions.md +2 -1
  190. package/gsd-core/workflows/edit-phase.md +26 -1
  191. package/gsd-core/workflows/eval-review.md +3 -5
  192. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +31 -6
  193. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
  194. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +2 -0
  195. package/gsd-core/workflows/execute-phase.md +38 -50
  196. package/gsd-core/workflows/execute-plan.md +36 -4
  197. package/gsd-core/workflows/explore.md +131 -4
  198. package/gsd-core/workflows/fast.md +10 -2
  199. package/gsd-core/workflows/health.md +73 -4
  200. package/gsd-core/workflows/import.md +4 -4
  201. package/gsd-core/workflows/ingest-docs.md +5 -5
  202. package/gsd-core/workflows/mvp-phase.md +6 -3
  203. package/gsd-core/workflows/new-milestone.md +14 -9
  204. package/gsd-core/workflows/new-project.md +14 -14
  205. package/gsd-core/workflows/next.md +12 -0
  206. package/gsd-core/workflows/plan-phase.md +41 -17
  207. package/gsd-core/workflows/plan-review-convergence.md +50 -2
  208. package/gsd-core/workflows/progress.md +34 -6
  209. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +4 -4
  210. package/gsd-core/workflows/quick/steps/quick-verification.md +27 -6
  211. package/gsd-core/workflows/quick/steps/research-phase.md +2 -2
  212. package/gsd-core/workflows/quick.md +35 -15
  213. package/gsd-core/workflows/review.md +26 -5
  214. package/gsd-core/workflows/secure-phase.md +1 -1
  215. package/gsd-core/workflows/session-report.md +2 -1
  216. package/gsd-core/workflows/settings.md +66 -2
  217. package/gsd-core/workflows/ship.md +104 -44
  218. package/gsd-core/workflows/spec-phase.md +30 -12
  219. package/gsd-core/workflows/sync-skills.md +63 -8
  220. package/gsd-core/workflows/transition.md +46 -11
  221. package/gsd-core/workflows/ui-phase.md +5 -5
  222. package/gsd-core/workflows/ui-review.md +2 -2
  223. package/gsd-core/workflows/update.md +1 -1
  224. package/gsd-core/workflows/validate-phase.md +1 -1
  225. package/gsd-core/workflows/verify-work.md +9 -7
  226. package/hooks/dist/gsd-agent-isolation-guard.js +103 -14
  227. package/hooks/dist/gsd-check-update-worker.js +56 -13
  228. package/hooks/dist/gsd-check-update.js +19 -1
  229. package/hooks/dist/gsd-cursor-pre-tool.js +0 -3
  230. package/hooks/dist/gsd-cursor-subagent-start.js +77 -2
  231. package/hooks/dist/gsd-cursor-subagent-stop.js +3 -2
  232. package/hooks/dist/gsd-prompt-guard.js +21 -20
  233. package/hooks/dist/gsd-read-injection-scanner.js +38 -24
  234. package/hooks/dist/gsd-statusline.js +18 -0
  235. package/hooks/dist/gsd-update-banner.js +22 -1
  236. package/hooks/dist/gsd-workflow-guard.js +134 -36
  237. package/hooks/dist/lib/git-cmd.js +92 -59
  238. package/hooks/dist/lib/injection-patterns.js +45 -0
  239. package/hooks/dist/lib/isolation-deny-reason.js +39 -0
  240. package/hooks/dist/lib/isolation-sentinel.js +9 -0
  241. package/hooks/gsd-agent-isolation-guard.js +103 -14
  242. package/hooks/gsd-check-update-worker.js +56 -13
  243. package/hooks/gsd-check-update.js +19 -1
  244. package/hooks/gsd-cursor-pre-tool.js +0 -3
  245. package/hooks/gsd-cursor-subagent-start.js +77 -2
  246. package/hooks/gsd-cursor-subagent-stop.js +3 -2
  247. package/hooks/gsd-prompt-guard.js +21 -20
  248. package/hooks/gsd-read-injection-scanner.js +38 -24
  249. package/hooks/gsd-statusline.js +18 -0
  250. package/hooks/gsd-update-banner.js +22 -1
  251. package/hooks/gsd-workflow-guard.js +134 -36
  252. package/hooks/lib/git-cmd.js +92 -59
  253. package/hooks/lib/injection-patterns.js +45 -0
  254. package/hooks/lib/isolation-deny-reason.js +39 -0
  255. package/hooks/lib/isolation-sentinel.js +9 -0
  256. package/package.json +21 -9
  257. package/pi/gsd.cjs +19 -5
  258. package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
  259. package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
  260. package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
  261. package/scripts/changeset/lint.cjs +60 -5
  262. package/scripts/check-alias-drift.cjs +7 -43
  263. package/scripts/check-contract-drift.cjs +297 -0
  264. package/scripts/ci-test-scope.cjs +19 -2
  265. package/scripts/command-contract-helpers.cjs +903 -1
  266. package/scripts/gen-adr-index.cjs +728 -38
  267. package/scripts/gen-capability-registry.cjs +3 -15
  268. package/scripts/gen-context-index.cjs +2 -11
  269. package/scripts/gen-health-docs.cjs +390 -0
  270. package/scripts/gen-inventory-manifest.cjs +50 -4
  271. package/scripts/gen-loop-host-contract.cjs +4 -24
  272. package/scripts/gen-registry.cjs +3 -14
  273. package/scripts/lib/alias-drift-families.cjs +46 -0
  274. package/scripts/lib/drift-scan.cjs +278 -0
  275. package/scripts/lint-allow-test-rule-refs.allowlist.json +1 -26
  276. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
  277. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
  278. package/scripts/lint-canary-version-leak.cjs +73 -0
  279. package/scripts/lint-command-contract.cjs +96 -13
  280. package/scripts/lint-completion-predicate-drift.cjs +933 -0
  281. package/scripts/lint-completion-ratio-drift.cjs +214 -0
  282. package/scripts/lint-default-flip-documentation.cjs +193 -0
  283. package/scripts/lint-eslint-glob-coverage.allowlist.json +34 -0
  284. package/scripts/lint-eslint-glob-coverage.cjs +340 -0
  285. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
  286. package/scripts/lint-health-diagnostic-rule-table.cjs +404 -0
  287. package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
  288. package/scripts/lint-milestone-window-drift.cjs +468 -0
  289. package/scripts/lint-phase-enumeration-drift.cjs +479 -0
  290. package/scripts/lint-plan-count-drift.cjs +318 -0
  291. package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
  292. package/scripts/lint-planning-prompt-drift.cjs +434 -0
  293. package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
  294. package/scripts/lint-regression-test-names.cjs +15 -13
  295. package/scripts/lint-removed-but-needed.cjs +320 -0
  296. package/scripts/lint-state-field-drift.cjs +805 -0
  297. package/scripts/lint-state-write-path-drift.cjs +1045 -0
  298. package/scripts/lint-test-file-count.allowlist.json +21 -10
  299. package/scripts/lint-unreachable-guard-drift.cjs +843 -0
  300. package/scripts/lint-vendored-deps.cjs +124 -0
  301. package/scripts/pr-changed-files.cjs +63 -0
  302. package/scripts/pr-template-policy.cjs +14 -4
  303. package/scripts/prompt-injection-scan.sh +25 -0
  304. package/scripts/require-issue-link-policy.cjs +192 -0
  305. package/scripts/state-write-path-drift-baseline.json +19 -0
  306. package/scripts/sync-runtime-launcher.cjs +2 -4
  307. package/skills/gsd-autonomous/SKILL.md +0 -1
  308. package/skills/gsd-code-review/SKILL.md +1 -1
  309. package/skills/gsd-execute-phase/SKILL.md +1 -2
  310. package/skills/gsd-map-codebase/SKILL.md +1 -1
  311. package/skills/gsd-mempalace-capture/SKILL.md +1 -1
  312. package/skills/gsd-mempalace-recall/SKILL.md +1 -1
  313. package/skills/gsd-new-milestone/SKILL.md +1 -1
  314. package/skills/gsd-next/SKILL.md +0 -1
  315. package/skills/gsd-plan-phase/SKILL.md +0 -1
  316. package/skills/gsd-progress/SKILL.md +0 -1
  317. package/skills/gsd-quick/SKILL.md +1 -1
  318. package/skills/gsd-review-backlog/SKILL.md +2 -1
  319. package/skills/gsd-stats/SKILL.md +0 -1
  320. package/skills/gsd-verify-work/SKILL.md +1 -1
  321. package/vscode/package.json +1 -1
  322. package/gsd-core/workflows/discovery-phase.md +0 -298
  323. package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
  324. package/gsd-core/workflows/verify-phase.md +0 -574
  325. package/scripts/affected-tests-lib.cjs +0 -554
  326. package/scripts/lint-allow-test-rule-refs.cjs +0 -162
  327. package/scripts/run-affected-tests.cjs +0 -7
  328. package/scripts/run-tests.cjs +0 -1051
@@ -10,12 +10,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
10
10
  return (mod && mod.__esModule) ? mod : { "default": mod };
11
11
  };
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.RUNTIMES_WITH_FAST_MODE = exports.EFFORT_ARGV = exports.EFFORT_RENDERING = exports.KNOWN_PROVIDERS = exports.PROVIDER_PRESETS = exports.RUNTIMES_WITH_REASONING_EFFORT = exports.KNOWN_RUNTIMES = exports.RUNTIME_PROFILE_MAP = exports.MODEL_ALIAS_MAP = exports.AGENT_DEFAULT_TIERS = exports.AGENT_TO_PHASE_TYPE = exports.MODEL_PROFILES = exports.ADAPTIVE_TIER_VALUES = exports.VALID_TIERS = exports.VALID_AGENT_TIERS = exports.VALID_PHASE_TYPES = exports.VALID_PROFILES = exports.catalog = void 0;
13
+ exports.RUNTIMES_WITH_FAST_MODE = exports.EFFORT_ARGV = exports.EFFORT_RENDERING = exports.CLAUDE_AGENT_ALIASES = exports.KNOWN_PROVIDERS = exports.PROVIDER_PRESETS = exports.RUNTIMES_WITH_REASONING_EFFORT = exports.KNOWN_RUNTIMES = exports.RUNTIME_PROFILE_MAP = exports.MODEL_ALIAS_MAP = exports.AGENT_DEFAULT_TIERS = exports.AGENT_TO_PHASE_TYPE = exports.MODEL_PROFILES = exports.ADAPTIVE_TIER_VALUES = exports.VALID_TIERS = exports.VALID_AGENT_TIERS = exports.VALID_PHASE_TYPES = exports.VALID_PROFILES = exports.catalog = void 0;
14
+ exports.isAnthropicFlavoredModel = isAnthropicFlavoredModel;
14
15
  exports.nextTier = nextTier;
15
16
  exports.formatAgentToModelMapAsTable = formatAgentToModelMapAsTable;
16
17
  exports.getAgentToModelMapForProfile = getAgentToModelMapForProfile;
17
18
  exports.renderEffortArgv = renderEffortArgv;
18
19
  exports.renderEffortForRuntime = renderEffortForRuntime;
20
+ exports.mergeEffortTierDefaults = mergeEffortTierDefaults;
19
21
  const node_path_1 = __importDefault(require("node:path"));
20
22
  // In .cts (CommonJS output) files, `require` is available as a global;
21
23
  // we use it directly to load JSON candidates.
@@ -103,6 +105,27 @@ exports.PROVIDER_PRESETS = _catalog.providerPresets ?? {};
103
105
  exports.KNOWN_PROVIDERS = new Set(Object.entries(exports.PROVIDER_PRESETS)
104
106
  .filter(([, tiers]) => Object.values(tiers).some((budgets) => budgets && Object.values(budgets).some((entry) => entry && entry.model)))
105
107
  .map(([name]) => name));
108
+ // ─── #3241 — Anthropic-flavored model detection ──────────────────────────────
109
+ //
110
+ // Moved here from src/model-resolver.cts (the "seam decision" in
111
+ // .gsd/phase/feat-3241-codex-omit-model-by-default/40-design.md): this leaf
112
+ // module is the one common dependency both model-resolver and the (layering-
113
+ // restricted) install-time Codex-posture checks can share without pulling
114
+ // model-resolver's config-loader dependency chain into a "pure read/verify"
115
+ // caller. model-resolver re-exports both names for back-compat.
116
+ //
117
+ // #2310 — True if `model` is an Anthropic-flavored value that must never appear as a
118
+ // Codex agent `.toml` `model`. Two forms: (a) a bare Claude Agent-tool tier alias
119
+ // (opus/sonnet/haiku/fable — CLAUDE_AGENT_ALIASES below); (b) any Claude model id in
120
+ // any provider namespacing — `claude-*`, `anthropic/claude-*`, `us.anthropic.claude-*`
121
+ // (the forms the catalog assigns to opencode/hermes/kilo, reachable on a Codex .toml
122
+ // via the runtime-resolver path). No OpenAI/Codex model id contains "claude", so a
123
+ // case-insensitive substring test is a safe, exhaustive guard for (b). Codex/ChatGPT
124
+ // rejects all of these.
125
+ exports.CLAUDE_AGENT_ALIASES = new Set(['opus', 'sonnet', 'haiku', 'fable']);
126
+ function isAnthropicFlavoredModel(model) {
127
+ return typeof model === 'string' && (exports.CLAUDE_AGENT_ALIASES.has(model) || model.toLowerCase().includes('claude'));
128
+ }
106
129
  function nextTier(currentTier) {
107
130
  const order = ['light', 'standard', 'heavy'];
108
131
  const idx = order.indexOf(String(currentTier));
@@ -205,6 +228,13 @@ function renderEffortArgv(host, universalEffort, effortSurface) {
205
228
  * Render a universal effort string for a specific runtime.
206
229
  */
207
230
  function renderEffortForRuntime(runtime, universalEffort) {
231
+ // #3533 (10d): 'inherit' is not a wire level on ANY runtime — it means
232
+ // "omit the key / pass no argument and follow the session/host default".
233
+ // Renderers must never emit it as a literal; null param/channel tells
234
+ // resolve-execution consumers there is no propagation.
235
+ if (universalEffort === 'inherit') {
236
+ return { value: 'inherit', param: null, channel: null };
237
+ }
208
238
  const spec = exports.EFFORT_RENDERING[runtime];
209
239
  if (!spec) {
210
240
  return { value: universalEffort, param: null, channel: null };
@@ -215,5 +245,33 @@ function renderEffortForRuntime(runtime, universalEffort) {
215
245
  channel: spec.channel,
216
246
  };
217
247
  }
248
+ /**
249
+ * #3531 (10c) — Merge a config `effort.routing_tier_defaults` block over the
250
+ * manifest tier defaults instead of replacing them. A partial config must not
251
+ * discard built-ins: per tier, a valid override value wins and an invalid one
252
+ * is ignored so the manifest value for that tier surfaces (ADR-443 D1's
253
+ * "invalid values fall through" holds within the merged layer).
254
+ *
255
+ * Pure: returns a new object and never mutates either input — the manifest
256
+ * constants (`CANONICAL_CONFIG_DEFAULTS`, the catalog cache) stay frozen. The
257
+ * validator is injected because `EFFORT_SET` lives in model-resolver, which
258
+ * imports this leaf (a reverse import would be a cycle); both effort
259
+ * resolvers pass their own `(v) => typeof v === 'string' && EFFORT_SET.has(v)`.
260
+ */
261
+ function mergeEffortTierDefaults(manifest, override, isValid) {
262
+ const merged = { ...(manifest || {}) };
263
+ if (override && typeof override === 'object' && !Array.isArray(override)) {
264
+ for (const [tier, value] of Object.entries(override)) {
265
+ // House pollution guard (mirrors _deepMergeConfig in config-loader): the
266
+ // string-only validator already makes these inert, but an explicit skip
267
+ // keeps this merge safe even if a caller's validator is ever relaxed.
268
+ if (tier === '__proto__' || tier === 'constructor' || tier === 'prototype')
269
+ continue;
270
+ if (isValid(value))
271
+ merged[tier] = value;
272
+ }
273
+ }
274
+ return merged;
275
+ }
218
276
  // ─── Fast mode propagation ───────────────────────────────────────────────────
219
277
  exports.RUNTIMES_WITH_FAST_MODE = new Set(['api']);
@@ -17,7 +17,8 @@
17
17
  * - ./config-loader.cjs (loadConfig)
18
18
  * - ./configuration.cjs (CONFIG_DEFAULTS as CANONICAL_CONFIG_DEFAULTS)
19
19
  * - ./model-profiles.cjs (MODEL_PROFILES, AGENT_TO_PHASE_TYPE, AGENT_DEFAULT_TIERS, VALID_AGENT_TIERS, nextTier)
20
- * - ./model-catalog.cjs (MODEL_ALIAS_MAP, RUNTIME_PROFILE_MAP, PROVIDER_PRESETS, VALID_TIERS)
20
+ * - ./model-catalog.cjs (MODEL_ALIAS_MAP, RUNTIME_PROFILE_MAP, PROVIDER_PRESETS, VALID_TIERS,
21
+ * CLAUDE_AGENT_ALIASES — re-exported below for back-compat, #3241)
21
22
  */
22
23
  var __importDefault = (this && this.__importDefault) || function (mod) {
23
24
  return (mod && mod.__esModule) ? mod : { "default": mod };
@@ -153,7 +154,9 @@ const CLAUDE_POLICY_ID_TO_ALIAS = {
153
154
  .map(([aliasName, id]) => [id, aliasName])),
154
155
  'claude-fable-5': 'fable',
155
156
  };
156
- const CLAUDE_AGENT_ALIASES = new Set(['opus', 'sonnet', 'haiku', 'fable']);
157
+ // CLAUDE_AGENT_ALIASES moved to ./model-catalog.cts (#3241) — imported above
158
+ // and re-exported below for back-compat (bin/install.js:474,
159
+ // tests/codex-config.test.cjs:24 depend on the name being on this module).
157
160
  // Dedupe stderr warnings so repeated agent resolutions don't spam (#1133).
158
161
  const _modelPolicyUnmappableWarned = new Set();
159
162
  function warnModelPolicyUnmappable(agentType, policyModel, tier) {
@@ -216,7 +219,7 @@ function mapClaudeOverrideForRuntime(override, configRuntime, agentType) {
216
219
  if (Object.hasOwn(CLAUDE_POLICY_ID_TO_ALIAS, override)) {
217
220
  return CLAUDE_POLICY_ID_TO_ALIAS[override];
218
221
  }
219
- if (CLAUDE_AGENT_ALIASES.has(override))
222
+ if (model_catalog_cjs_1.CLAUDE_AGENT_ALIASES.has(override))
220
223
  return override;
221
224
  if (override.startsWith('claude-')) {
222
225
  warnModelOverrideUnmappable(agentType, override);
@@ -278,33 +281,161 @@ function resolveModelPolicy(policy, tier) {
278
281
  return null;
279
282
  return budgetEntry.model;
280
283
  }
284
+ /**
285
+ * #2229 — the profile/phase-type tier for (config, agentType).
286
+ *
287
+ * Extracted verbatim from resolveModelInternal's step 2 so the same expression can
288
+ * answer "which tier did GSD resolve?" without also resolving a model id. The
289
+ * extraction is behaviour-preserving by construction: resolveModelInternal calls
290
+ * straight back into it.
291
+ *
292
+ * Returns null when the agent has no catalog entry and the profile is not `inherit`.
293
+ */
294
+ function computeProfileTier(config, agentType) {
295
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
296
+ const profile = String(config['model_profile'] || 'balanced').toLowerCase();
297
+ // Own-property guard: agentType is an unvalidated CLI positional (the
298
+ // `resolve-model <agent-type>` argument is never checked against a known
299
+ // agent list), so a prototype-chain agentType ("toString", "constructor")
300
+ // would otherwise return an inherited member from this plain object
301
+ // instead of undefined — verified reachable purely via the CLI.
302
+ const modelProfilesMap = MODEL_PROFILES;
303
+ const agentModels = Object.hasOwn(modelProfilesMap, agentType) ? modelProfilesMap[agentType] : undefined;
304
+ const phaseType = (AGENT_TO_PHASE_TYPE)[agentType];
305
+ const configModels = config['models'];
306
+ const phaseTypeTier = (phaseType && configModels && typeof configModels === 'object')
307
+ ? configModels[phaseType]
308
+ : undefined;
309
+ return (phaseTypeTier && model_catalog_cjs_1.VALID_TIERS.has(phaseTypeTier))
310
+ ? phaseTypeTier
311
+ : (profile === 'inherit'
312
+ ? 'inherit'
313
+ : (agentModels
314
+ // Own-property guard: `profile` is a config-supplied string
315
+ // (config['model_profile'], lower-cased); an already-lowercase
316
+ // prototype-chain key ("constructor", "__proto__") would otherwise
317
+ // return an inherited non-string member instead of falling back to
318
+ // 'balanced' (verified: profile:"constructor"/"__proto__" leaked a
319
+ // function/object through both the tier and model resolution paths).
320
+ ? ((Object.hasOwn(agentModels, profile) ? agentModels[profile] : undefined) || agentModels['balanced'])
321
+ : null));
322
+ }
323
+ /**
324
+ * #2229 — the effective model TIER for (config, agentType), as a signal a workflow can
325
+ * read: `gsd_run query resolve-model <agent> --pick tier`.
326
+ *
327
+ * Why this is not just "look at the resolved model": on every runtime the installer
328
+ * configures with `resolve_model_ids: "omit"` — which is every non-Claude runtime, see
329
+ * docs/CONFIGURATION.md — resolveModelInternal deliberately returns '' below. A guard
330
+ * keyed on the model id therefore cannot tell a budget-tier run from a top-tier one
331
+ * there, while the tier itself is computed ABOVE that early-return and stays knowable.
332
+ *
333
+ * Honesty contract — this never guesses, because a guard that reports a wrong tier is
334
+ * worse than one that reports none:
335
+ * - a per-agent `model_overrides` pin naming a known alias (or a full Claude id that
336
+ * maps to one) reports that alias;
337
+ * - a pin that maps to nothing reports 'unknown' — a raw model id carries no tier;
338
+ * - `model_profile: inherit` reports 'inherit' — the session model is not ours to name;
339
+ * - an agent with no catalog entry reports 'unknown'.
340
+ *
341
+ * Callers must treat 'unknown' and 'inherit' as "cannot tell", never as "adequate".
342
+ */
343
+ function resolveTierFromConfig(config, agentType) {
344
+ const rawOverrides = config['model_overrides'];
345
+ const modelOverrides = (rawOverrides && typeof rawOverrides === 'object' && !Array.isArray(rawOverrides))
346
+ ? rawOverrides
347
+ : null;
348
+ // Own-property guard: agentType is a caller-supplied string (the
349
+ // `resolve-model <agent-type>` CLI positional is not validated against a
350
+ // known agent list); a prototype-chain agentType ("toString",
351
+ // "constructor") against ANY model_overrides object — even `{}` — would
352
+ // otherwise return an inherited member instead of undefined.
353
+ const override = (modelOverrides && Object.hasOwn(modelOverrides, agentType))
354
+ ? modelOverrides[agentType]
355
+ : undefined;
356
+ if (override && typeof override === 'string') {
357
+ if (model_catalog_cjs_1.CLAUDE_AGENT_ALIASES.has(override))
358
+ return override;
359
+ // Own-property guard: this indexes a plain object with a config-supplied
360
+ // string, so a prototype-chain key ("toString", "constructor", "valueOf")
361
+ // would otherwise return an inherited member instead of undefined — and a
362
+ // function-valued tier is dropped entirely by JSON.stringify, silently
363
+ // removing the key a guard depends on.
364
+ const alias = Object.hasOwn(CLAUDE_POLICY_ID_TO_ALIAS, override)
365
+ ? CLAUDE_POLICY_ID_TO_ALIAS[override]
366
+ : undefined;
367
+ if (typeof alias === 'string' && alias)
368
+ return alias;
369
+ return 'unknown';
370
+ }
371
+ const profileTier = computeProfileTier(config, agentType);
372
+ // #3282 — mirror resolveModelInternal's step 2.5 (model_policy preset). The
373
+ // profile tier alone under-reports: model_policy can dispatch a DIFFERENT
374
+ // tier than the profile implies (e.g. a `balanced` profile's "sonnet" tier
375
+ // combined with `model_policy: {budget: 'low'}` actually spawns "haiku"),
376
+ // and reporting the profile tier there is exactly the under-report this
377
+ // fixes — a haiku-tier run must never be reported as "sonnet". Skipped
378
+ // under the same condition resolveModelInternal skips it (no tier, or
379
+ // "inherit" — the session model is not ours to name).
380
+ if (profileTier && profileTier !== 'inherit') {
381
+ const mergedPolicy = config['model_policy']
382
+ ? { ...config['model_policy'], runtime: config['runtime'] || 'claude' }
383
+ : null;
384
+ const policyModel = resolveModelPolicy(mergedPolicy, profileTier);
385
+ if (policyModel) {
386
+ // Map the policy-resolved id back to a tier alias with the same
387
+ // own-property-guarded lookups used above. If it maps, that alias IS
388
+ // the tier that actually runs — report it (the fix). If it does not
389
+ // map — including every non-Claude runtime, where resolveModelInternal
390
+ // returns the policy model verbatim with no tier meaning — the model
391
+ // carries no tier we can name; report 'unknown' rather than falling
392
+ // back to the profile tier, which would silently reintroduce the
393
+ // under-report this block exists to close.
394
+ const aliasForId = Object.hasOwn(CLAUDE_POLICY_ID_TO_ALIAS, policyModel)
395
+ ? CLAUDE_POLICY_ID_TO_ALIAS[policyModel]
396
+ : undefined;
397
+ if (typeof aliasForId === 'string' && aliasForId)
398
+ return aliasForId;
399
+ if (model_catalog_cjs_1.CLAUDE_AGENT_ALIASES.has(policyModel))
400
+ return policyModel;
401
+ return 'unknown';
402
+ }
403
+ }
404
+ return profileTier || 'unknown';
405
+ }
406
+ function resolveTierInternal(cwd, agentType) {
407
+ return resolveTierFromConfig(loadConfig(cwd), agentType);
408
+ }
281
409
  function resolveModelInternal(cwd, agentType) {
282
410
  const config = loadConfig(cwd);
283
411
  // 1. Per-agent override (#2041: map Claude full IDs → Agent-tool aliases on
284
412
  // the claude runtime, mirroring the model_policy path #1144; non-Claude
285
413
  // runtimes and non-Claude values pass through verbatim).
286
414
  const modelOverrides = config['model_overrides'];
287
- const override = modelOverrides?.[agentType];
415
+ // Own-property guard (see resolveTierFromConfig above): without it, an
416
+ // agentType of "toString" against `model_overrides: {}` returned the
417
+ // inherited Function.prototype.toString as the resolved "model" — verified
418
+ // reachable purely via the CLI, no override value needed.
419
+ const override = (modelOverrides && Object.hasOwn(modelOverrides, agentType))
420
+ ? modelOverrides[agentType]
421
+ : undefined;
288
422
  if (override) {
289
423
  const mapped = mapClaudeOverrideForRuntime(override, config['runtime'], agentType);
290
424
  if (mapped !== null)
291
425
  return mapped;
292
426
  // Unmappable Claude ID — fall through to tier resolution (matches model_policy).
293
427
  }
294
- // 2. Compute the tier
428
+ // 2. Compute the tier (#2229: shared with resolveTierFromConfig so the tier a
429
+ // workflow reads and the tier a model is resolved from can never diverge).
295
430
  // eslint-disable-next-line @typescript-eslint/no-base-to-string
296
431
  const profile = String(config['model_profile'] || 'balanced').toLowerCase();
297
- const agentModels = MODEL_PROFILES[agentType];
298
- const phaseType = (AGENT_TO_PHASE_TYPE)[agentType];
299
- const configModels = config['models'];
300
- const phaseTypeTier = (phaseType && configModels && typeof configModels === 'object')
301
- ? configModels[phaseType]
302
- : undefined;
303
- const tier = (phaseTypeTier && model_catalog_cjs_1.VALID_TIERS.has(phaseTypeTier))
304
- ? phaseTypeTier
305
- : (profile === 'inherit'
306
- ? 'inherit'
307
- : (agentModels ? (agentModels[profile] || agentModels['balanced']) : null));
432
+ // Own-property guard (see computeProfileTier above): without it, agentType
433
+ // "toString" returned Function.prototype.toString as `agentModels`
434
+ // (truthy), which skipped the "unknown agent" fallback below and made
435
+ // resolveModelInternal return undefined instead of a tier-derived string.
436
+ const modelProfilesMapForModel = MODEL_PROFILES;
437
+ const agentModels = Object.hasOwn(modelProfilesMapForModel, agentType) ? modelProfilesMapForModel[agentType] : undefined;
438
+ const tier = computeProfileTier(config, agentType);
308
439
  // 2.5. model_policy preset (#49, #1133)
309
440
  const configRuntime = config['runtime'];
310
441
  if (tier && tier !== 'inherit') {
@@ -320,11 +451,13 @@ function resolveModelInternal(cwd, agentType) {
320
451
  return policyModel;
321
452
  // Claude Code's Agent tool takes tier aliases (opus/sonnet/haiku/fable),
322
453
  // not full model IDs — map the policy-resolved ID back to an alias (#1133).
323
- const aliasForId = CLAUDE_POLICY_ID_TO_ALIAS[policyModel];
324
- if (aliasForId)
454
+ const aliasForId = Object.hasOwn(CLAUDE_POLICY_ID_TO_ALIAS, policyModel)
455
+ ? CLAUDE_POLICY_ID_TO_ALIAS[policyModel]
456
+ : undefined;
457
+ if (typeof aliasForId === 'string' && aliasForId)
325
458
  return aliasForId;
326
459
  // The policy value may already be a bare Claude agent alias (e.g. "fable").
327
- if (CLAUDE_AGENT_ALIASES.has(policyModel))
460
+ if (model_catalog_cjs_1.CLAUDE_AGENT_ALIASES.has(policyModel))
328
461
  return policyModel;
329
462
  // No Claude alias for this ID (e.g. a pinned minor version like
330
463
  // claude-opus-4-5). Warn once and fall through to the tier alias rather
@@ -415,7 +548,13 @@ function resolveModelForTier(cwd, agentType, attempt) {
415
548
  const config = loadConfig(cwd);
416
549
  const attemptN = Number.isInteger(attempt) && attempt > 0 ? attempt : 0;
417
550
  const modelOverrides = config['model_overrides'];
418
- const override = modelOverrides?.[agentType];
551
+ // Own-property guard (see resolveTierFromConfig above): without it, an
552
+ // agentType of "toString" against `model_overrides: {}` returned the
553
+ // inherited Function.prototype.toString as the resolved "model" — verified
554
+ // reachable purely via the CLI, no override value needed.
555
+ const override = (modelOverrides && Object.hasOwn(modelOverrides, agentType))
556
+ ? modelOverrides[agentType]
557
+ : undefined;
419
558
  if (override) {
420
559
  const mapped = mapClaudeOverrideForRuntime(override, config['runtime'], agentType);
421
560
  if (mapped !== null)
@@ -533,7 +672,12 @@ function resolveProviderEscalation(cwd, agentType, attempt, applicable) {
533
672
  }
534
673
  // ─── #443 — Unified effort + fast_mode resolvers ─────────────────────────────
535
674
  const VALID_EFFORTS = ['minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
536
- const EFFORT_SET = new Set(VALID_EFFORTS);
675
+ // #3533 (10d): the VOCABULARY carries one more member than the LADDER —
676
+ // 'inherit' is a declarable effort choice ("follow the session", expressed by
677
+ // OMITTING the effort key at the writer) but not a level nextEffort may step
678
+ // into. Keeping it out of VALID_EFFORTS means escalation (resolveEffortForTier)
679
+ // never walks past an explicit inherit: nextEffort('inherit') is null.
680
+ const EFFORT_SET = new Set([...VALID_EFFORTS, 'inherit']);
537
681
  /**
538
682
  * Walk one step up the effort ladder from `e`.
539
683
  */
@@ -576,26 +720,23 @@ function resolveEffortInternal(cwd, agentType, opts) {
576
720
  }
577
721
  }
578
722
  // Step 3: routing_tier_defaults by agent's default tier.
723
+ // #3531 (10c): the config block merges OVER the manifest tier defaults
724
+ // rather than replacing them — an effort block without
725
+ // routing_tier_defaults (or missing this agent's tier) falls back to the
726
+ // manifest built-in for that tier instead of skipping to effort.default.
727
+ // Invalid config values are dropped by the merge, so the manifest value for
728
+ // the tier surfaces (the same "invalid falls through" rule every layer has).
579
729
  const agentTier = (AGENT_DEFAULT_TIERS)[agentType];
580
730
  if (agentTier) {
581
- if (effortCfg && effortCfg['routing_tier_defaults'] &&
582
- typeof effortCfg['routing_tier_defaults'] === 'object' &&
583
- !Array.isArray(effortCfg['routing_tier_defaults'])) {
584
- const v = effortCfg['routing_tier_defaults'][agentTier];
585
- if (typeof v === 'string' && EFFORT_SET.has(v))
586
- return v;
587
- }
588
- else if (!effortCfg) {
589
- const canonicalEffort = (configuration_cjs_1.CONFIG_DEFAULTS)['effort'];
590
- const manifestDefaults = canonicalEffort && typeof canonicalEffort === 'object'
591
- ? canonicalEffort['routing_tier_defaults']
592
- : undefined;
593
- if (manifestDefaults && typeof manifestDefaults === 'object') {
594
- const v = manifestDefaults[agentTier];
595
- if (typeof v === 'string' && EFFORT_SET.has(v))
596
- return v;
597
- }
598
- }
731
+ const canonicalEffort = (configuration_cjs_1.CONFIG_DEFAULTS)['effort'];
732
+ const manifestDefaults = canonicalEffort && typeof canonicalEffort === 'object'
733
+ ? canonicalEffort['routing_tier_defaults']
734
+ : undefined;
735
+ const isValidEffort = (v) => typeof v === 'string' && EFFORT_SET.has(v);
736
+ const merged = (0, model_catalog_cjs_1.mergeEffortTierDefaults)(manifestDefaults, effortCfg ? effortCfg['routing_tier_defaults'] : undefined, isValidEffort);
737
+ const v = merged[agentTier];
738
+ if (isValidEffort(v))
739
+ return v;
599
740
  }
600
741
  // Step 4: effort.default
601
742
  if (effortCfg) {
@@ -693,9 +834,11 @@ function resolveEffortForTier(cwd, agentType, attempt) {
693
834
  }
694
835
  module.exports = {
695
836
  resolveTierEntry,
696
- CLAUDE_AGENT_ALIASES,
837
+ CLAUDE_AGENT_ALIASES: model_catalog_cjs_1.CLAUDE_AGENT_ALIASES,
697
838
  resolveModelPolicy,
698
839
  resolveModelInternal,
840
+ resolveTierInternal,
841
+ resolveTierFromConfig,
699
842
  _resetModelPolicyWarningCacheForTests,
700
843
  _resetModelOverrideWarningCacheForTests,
701
844
  _setInstallRuntimeMarkerForTests,
@@ -23,7 +23,7 @@
23
23
  * hang or take super-linear time on an adversarial `workflow.test_command`.
24
24
  *
25
25
  * Single source of truth: all four test-command gates (regression, post-merge,
26
- * audit-fix, verify-phase) route their resolved command through this helper so
26
+ * audit-fix) route their resolved command through this helper so
27
27
  * the paths cannot drift.
28
28
  *
29
29
  * Leaf module — depends only on node:fs / node:path.
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ /**
3
+ * pattern.cts — the pattern-construction seam (ADR-3212 §1, epic #3212 Phase 1,
4
+ * #3412).
5
+ *
6
+ * Source in src/pattern.cts, compiled to gsd-core/bin/lib/pattern.cjs
7
+ * (gitignored), per the repo's ADR-457 build-at-publish convention.
8
+ *
9
+ * Sole owner of building a `RegExp` from a runtime value. `escapeRegex`
10
+ * delegates to the built-in `RegExp.escape` (ES2026 / Node 24+) when present,
11
+ * falling back to an in-file metacharacter escape below Node 24 (#3498) —
12
+ * still the one owner: no module outside this seam escapes a value for regex
13
+ * use (ADR §1).
14
+ *
15
+ * Counts, corrected during implementation (design doc "Ground truth" #1;
16
+ * .gsd/phase/chore-3412-pattern-seam/40-design.md): the ADR's census counted
17
+ * ~12 *named* helper functions. The shape-based lint guard
18
+ * (eslint-rules/no-adhoc-regex-escape.cjs), which matches the escape-class
19
+ * SHAPE wherever it appears rather than named-function bodies, found 27
20
+ * additional inline copies that census-by-name could not see — **~39 escape
21
+ * sites total**. Call sites follow the same pattern: 17 were surveyed
22
+ * directly, but `src/phase-id.cts`'s `escapeRegex` turned out to have 8
23
+ * external production importers the pre-implementation survey missed, plus
24
+ * the 27 guard-found inline sites also call into the seam — **~44 call
25
+ * sites total**. The lesson worth keeping: a named-function census
26
+ * structurally cannot see an inline `.replace(...)` copy or an
27
+ * externally-imported symbol; only a shape-based guard (or a direct
28
+ * importer graph query) does.
29
+ *
30
+ * `escapeRegex` is the PRIMARY export: the large majority of call sites
31
+ * build a *source string* (alternation via `.map(escapeRegex).join('|')`, or
32
+ * template/concat interpolation into a larger pattern) rather than a
33
+ * standalone literal match. `literalPattern` is the minority convenience
34
+ * wrapper for the remaining "match this value literally" shape — not the
35
+ * dominant one (design doc "Ground truth" #2).
36
+ *
37
+ * Behavior-preserving for MATCH RESULTS, not for pattern TEXT: `RegExp.escape`
38
+ * hex-escapes the leading character of nearly every non-empty input (and `-`,
39
+ * space, and control characters throughout), so the escaped source string
40
+ * differs from the twelve deleted copies' output for almost every value.
41
+ * Match behavior against that source is unaffected — verified by the
42
+ * migration-equivalence property sweep in tests/pattern.test.cjs (rows 15-17).
43
+ */
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.MAX_USER_PATTERN_LEN = void 0;
46
+ exports.escapeRegex = escapeRegex;
47
+ exports.literalPattern = literalPattern;
48
+ exports.compileUserPattern = compileUserPattern;
49
+ // re2js is vendored, not an npm dependency at runtime: gsd-core/bin/** is
50
+ // copied into installed trees that have no node_modules, so this module must
51
+ // carry zero external requires (eslint-rules/no-external-require-in-bin.cjs
52
+ // enforces it). See gsd-core/bin/lib/vendor/README.md.
53
+ const re2js_cjs_1 = require("./vendor/re2js.cjs");
54
+ // #3498: RegExp.escape is ES2026 (first shipped in Node 24). The gsd-test
55
+ // matrix still runs a linux-node22 lane, and the build itself consumes this
56
+ // module (scripts/gen-loop-host-contract.cjs), so a hard dependency breaks
57
+ // `npm run build` on Node 22. Prefer the built-in when present; otherwise use
58
+ // the local metachar escape — still inside this file, so the #3212 sole-owner
59
+ // invariant (and lint-no-adhoc-regex-escape's scope) is preserved. Captured at
60
+ // module load so a runtime mutation of RegExp.escape cannot flip the path
61
+ // mid-process.
62
+ const escapeBuiltin = typeof RegExp.escape === 'function'
63
+ ? RegExp.escape.bind(RegExp)
64
+ : undefined;
65
+ const escapeMetachars = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
66
+ function escapeRegex(value) {
67
+ return (escapeBuiltin ?? escapeMetachars)(value);
68
+ }
69
+ function literalPattern(value, flags) {
70
+ return new RegExp(escapeRegex(value), flags);
71
+ }
72
+ /** Max length for a user-supplied regex pattern before it is refused (ReDoS/compile-cost mitigation). */
73
+ exports.MAX_USER_PATTERN_LEN = 512;
74
+ /** Always-false matcher shared by every neutralization path — a refused pattern must never be able to report a match. */
75
+ const NEVER_MATCH = () => false;
76
+ /**
77
+ * Compile an UNTRUSTED, user-supplied pattern (e.g. plan frontmatter) via RE2
78
+ * (re2js), whose matching is linear-time in input length by construction —
79
+ * there is no backtracking engine here to exploit, so the vulnerability class
80
+ * (catastrophic/exponential backtracking) is closed by the engine rather than
81
+ * detected by a heuristic scan of the pattern text.
82
+ *
83
+ * Never throws. A pattern that is empty, too long, or not valid RE2 syntax
84
+ * (backreferences and look-around are unsupported by RE2 — those are exactly
85
+ * the constructs that require backtracking) is REFUSED: `test()` always
86
+ * returns `false`, and `neutralized` reports why so callers can surface the
87
+ * refusal (#3477 follow-up: a neutralized pattern must not look like a plain
88
+ * "not found"). Restores the guards lost with
89
+ * `sdk/src/query/validate.ts:regexForKeyLinkPattern` (#3477); this revision
90
+ * (post-#3477-follow-up) replaces the hand-rolled backtracking-shape scanner
91
+ * with RE2's linear-time guarantee — a refused pattern is never re-attempted
92
+ * as a literal-escaped match, since guessing at a pattern we could not
93
+ * compile is what produced the prior false-pass regression.
94
+ *
95
+ * `pattern` is `unknown`, not `string`, because callers pull this straight off
96
+ * parsed plan frontmatter (untrusted YAML) — a non-string value must reach the
97
+ * `'empty'`/never-match branch rather than being force-cast by the caller.
98
+ */
99
+ function compileUserPattern(pattern) {
100
+ if (typeof pattern !== 'string' || pattern.length === 0) {
101
+ return { test: NEVER_MATCH, neutralized: 'empty' };
102
+ }
103
+ if (pattern.length > exports.MAX_USER_PATTERN_LEN) {
104
+ // The cap now bounds compile cost/memory, not backtracking (RE2 has none) —
105
+ // an over-long pattern is refused outright rather than truncated-and-compiled.
106
+ return { test: NEVER_MATCH, neutralized: 'too-long' };
107
+ }
108
+ try {
109
+ // translateRegExp accepts JS-flavored syntax (named groups, `/`-escaping,
110
+ // etc.) that RE2's own grammar doesn't, reducing spurious refusals of
111
+ // otherwise-safe, JS-authored patterns before compiling under RE2's
112
+ // linear-time engine.
113
+ const compiled = re2js_cjs_1.RE2JS.compile(re2js_cjs_1.RE2JS.translateRegExp(pattern));
114
+ return { test: (input) => compiled.test(input), neutralized: null };
115
+ }
116
+ catch {
117
+ // Backreferences, look-around, or any other RE2-unsupported/malformed
118
+ // syntax. No literal-escape fallback: a pattern we could not compile is
119
+ // never guessed at — guessing produced the #3477 false-pass regression.
120
+ return { test: NEVER_MATCH, neutralized: 'unsupported' };
121
+ }
122
+ }
@@ -231,7 +231,7 @@ function extractFrontmatterBlock(text, key) {
231
231
  if (typeof text !== 'string')
232
232
  return null;
233
233
  // Anchor at byte 0 — CRLF-tolerant.
234
- const fm = /^---\r?\n([\s\S]*?)\r?\n---\r?(?:\n|$)/.exec(text);
234
+ const fm = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|\r?$)/.exec(text);
235
235
  if (fm === null)
236
236
  return null;
237
237
  const lines = fm[1].split(/\r?\n/);