@opengsd/gsd-core 1.7.0 → 1.9.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 (261) 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 +45 -1
  4. package/README.md +2 -0
  5. package/agents/gsd-code-fixer.md +1 -1
  6. package/agents/gsd-codebase-mapper.md +1 -1
  7. package/agents/gsd-debug-session-manager.md +78 -4
  8. package/agents/gsd-debugger.md +87 -29
  9. package/agents/gsd-executor.md +49 -9
  10. package/agents/gsd-intel-updater.md +3 -3
  11. package/agents/gsd-phase-researcher.md +4 -2
  12. package/agents/gsd-plan-checker.md +20 -0
  13. package/agents/gsd-planner.md +44 -59
  14. package/agents/gsd-project-researcher.md +2 -2
  15. package/agents/gsd-ui-auditor.md +0 -40
  16. package/agents/gsd-verifier.md +2 -2
  17. package/bin/install.js +1338 -135
  18. package/commands/gsd/ai-integration-phase.md +1 -1
  19. package/commands/gsd/mempalace-capture.md +9 -5
  20. package/commands/gsd/new-milestone.md +1 -1
  21. package/commands/gsd/plan-phase.md +5 -3
  22. package/commands/gsd/plan-review-convergence.md +7 -2
  23. package/gsd-core/bin/gsd-tools.cjs +2690 -2472
  24. package/gsd-core/bin/lib/adapter-imperative.cjs +8 -1
  25. package/gsd-core/bin/lib/agent-command-router.cjs +20 -5
  26. package/gsd-core/bin/lib/api-coverage.cjs +360 -53
  27. package/gsd-core/bin/lib/audit.cjs +8 -8
  28. package/gsd-core/bin/lib/broken-windows.cjs +716 -0
  29. package/gsd-core/bin/lib/capability-command-router.cjs +733 -0
  30. package/gsd-core/bin/lib/capability-consent.cjs +40 -1
  31. package/gsd-core/bin/lib/capability-lifecycle.cjs +58 -0
  32. package/gsd-core/bin/lib/capability-loader.cjs +23 -1
  33. package/gsd-core/bin/lib/capability-registry.cjs +1450 -160
  34. package/gsd-core/bin/lib/capability-trust.cjs +468 -33
  35. package/gsd-core/bin/lib/capability-validator.cjs +882 -6
  36. package/gsd-core/bin/lib/capability-writer.cjs +6 -1
  37. package/gsd-core/bin/lib/check-command-router.cjs +140 -27
  38. package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +15 -0
  39. package/gsd-core/bin/lib/claude-orchestration-command-router.cjs +209 -31
  40. package/gsd-core/bin/lib/claude-orchestration.cjs +203 -25
  41. package/gsd-core/bin/lib/command-aliases.cjs +14 -0
  42. package/gsd-core/bin/lib/commands.cjs +326 -21
  43. package/gsd-core/bin/lib/config-loader.cjs +214 -30
  44. package/gsd-core/bin/lib/config.cjs +158 -22
  45. package/gsd-core/bin/lib/core-utils.cjs +6 -1
  46. package/gsd-core/bin/lib/decisions.cjs +32 -8
  47. package/gsd-core/bin/lib/docs.cjs +6 -0
  48. package/gsd-core/bin/lib/estimate-cli.cjs +336 -0
  49. package/gsd-core/bin/lib/external-descriptor-trust.cjs +14 -2
  50. package/gsd-core/bin/lib/frontmatter.cjs +125 -15
  51. package/gsd-core/bin/lib/gap-checker.cjs +17 -2
  52. package/gsd-core/bin/lib/host-integration.cjs +215 -8
  53. package/gsd-core/bin/lib/init.cjs +155 -66
  54. package/gsd-core/bin/lib/install-engine.cjs +299 -23
  55. package/gsd-core/bin/lib/install-profiles.cjs +239 -1
  56. package/gsd-core/bin/lib/installer-migrations/005-opencode-baseline-commands-dir.cjs +146 -0
  57. package/gsd-core/bin/lib/installer-migrations/006-pi-extension-cjs-to-js.cjs +91 -0
  58. package/gsd-core/bin/lib/installer-migrations.cjs +44 -5
  59. package/gsd-core/bin/lib/markdown-sectionizer.cjs +107 -0
  60. package/gsd-core/bin/lib/milestone.cjs +248 -14
  61. package/gsd-core/bin/lib/model-catalog.cjs +69 -4
  62. package/gsd-core/bin/lib/model-resolver.cjs +189 -7
  63. package/gsd-core/bin/lib/observability/logger.cjs +7 -2
  64. package/gsd-core/bin/lib/onboard-projection.cjs +11 -8
  65. package/gsd-core/bin/lib/phase-command-router.cjs +10 -1
  66. package/gsd-core/bin/lib/phase-estimation.cjs +398 -0
  67. package/gsd-core/bin/lib/phase-id.cjs +304 -9
  68. package/gsd-core/bin/lib/phase.cjs +258 -17
  69. package/gsd-core/bin/lib/plan-drift-guard.cjs +1 -1
  70. package/gsd-core/bin/lib/plan-scan.cjs +70 -2
  71. package/gsd-core/bin/lib/planning-workspace.cjs +9 -2
  72. package/gsd-core/bin/lib/profile-output.cjs +34 -8
  73. package/gsd-core/bin/lib/review-lane-descriptor.cjs +927 -0
  74. package/gsd-core/bin/lib/review-lane-invocation.cjs +348 -0
  75. package/gsd-core/bin/lib/review-lane-runner.cjs +594 -0
  76. package/gsd-core/bin/lib/review-reviewer-selection.cjs +114 -32
  77. package/gsd-core/bin/lib/roadmap-parser.cjs +61 -10
  78. package/gsd-core/bin/lib/roadmap.cjs +23 -7
  79. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +38 -5
  80. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +23 -9
  81. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +156 -0
  82. package/gsd-core/bin/lib/runtime-name-policy.cjs +15 -2
  83. package/gsd-core/bin/lib/smart-entry.cjs +70 -5
  84. package/gsd-core/bin/lib/state-document.cjs +171 -24
  85. package/gsd-core/bin/lib/state-transition.cjs +50 -11
  86. package/gsd-core/bin/lib/state.cjs +206 -32
  87. package/gsd-core/bin/lib/surface.cjs +51 -9
  88. package/gsd-core/bin/lib/uat-predicate.cjs +6 -4
  89. package/gsd-core/bin/lib/uat.cjs +428 -11
  90. package/gsd-core/bin/lib/ui-consideration-probe.cjs +2 -2
  91. package/gsd-core/bin/lib/unusable-input.cjs +216 -0
  92. package/gsd-core/bin/lib/validate.cjs +44 -8
  93. package/gsd-core/bin/lib/verification.cjs +163 -31
  94. package/gsd-core/bin/lib/verify.cjs +348 -42
  95. package/gsd-core/bin/lib/worktree-safety.cjs +360 -15
  96. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  97. package/gsd-core/bin/shared/config-schema.manifest.json +4 -15
  98. package/gsd-core/bin/shared/model-catalog.json +5 -0
  99. package/gsd-core/bin/shared/runtime-aliases.manifest.json +5 -0
  100. package/gsd-core/references/api-coverage.md +37 -7
  101. package/gsd-core/references/checkpoints.md +1 -1
  102. package/gsd-core/references/common-bug-patterns.md +13 -0
  103. package/gsd-core/references/context-budget.md +40 -0
  104. package/gsd-core/references/debugger-bug-taxonomy.md +111 -0
  105. package/gsd-core/references/debugger-fix-acceptance.md +157 -0
  106. package/gsd-core/references/debugger-philosophy.md +1 -0
  107. package/gsd-core/references/debugger-prevention.md +98 -0
  108. package/gsd-core/references/debugger-rca-branching.md +98 -0
  109. package/gsd-core/references/debugger-repro-hardening.md +130 -0
  110. package/gsd-core/references/debugger-sbfl.md +110 -0
  111. package/gsd-core/references/debugger-semantic-recall.md +81 -0
  112. package/gsd-core/references/execute-phase-quota-recovery.md +55 -0
  113. package/gsd-core/references/execute-phase-requirement-revert.md +8 -0
  114. package/gsd-core/references/execute-phase-response-language.md +7 -0
  115. package/gsd-core/references/gate-prompts.md +6 -3
  116. package/gsd-core/references/model-profile-resolution.md +64 -13
  117. package/gsd-core/references/offer-next.md +88 -0
  118. package/gsd-core/references/planner-antipatterns.md +6 -0
  119. package/gsd-core/references/planner-mvp-mode.md +12 -13
  120. package/gsd-core/references/planner-preconditions.md +156 -0
  121. package/gsd-core/references/planner-reversibility.md +132 -0
  122. package/gsd-core/references/planning-config.md +2 -1
  123. package/gsd-core/references/reviewer-instances.md +28 -19
  124. package/gsd-core/references/runtime-aware-dispatch.md +42 -0
  125. package/gsd-core/references/skeleton-template.md +1 -1
  126. package/gsd-core/references/thinking-models-planning.md +3 -1
  127. package/gsd-core/references/ui-consideration-probe.md +2 -2
  128. package/gsd-core/references/worktree-branch-check.md +4 -4
  129. package/gsd-core/templates/DEBUG.md +5 -3
  130. package/gsd-core/templates/summary-minimal.md +4 -0
  131. package/gsd-core/templates/summary-standard.md +4 -0
  132. package/gsd-core/templates/summary.md +7 -0
  133. package/gsd-core/workflows/add-phase.md +2 -0
  134. package/gsd-core/workflows/add-tests.md +3 -1
  135. package/gsd-core/workflows/add-todo.md +32 -1
  136. package/gsd-core/workflows/ai-integration-phase.md +8 -6
  137. package/gsd-core/workflows/audit-fix.md +6 -2
  138. package/gsd-core/workflows/audit-milestone.md +8 -0
  139. package/gsd-core/workflows/autonomous.md +19 -15
  140. package/gsd-core/workflows/check-todos.md +5 -3
  141. package/gsd-core/workflows/cleanup.md +7 -1
  142. package/gsd-core/workflows/code-review-fix.md +14 -6
  143. package/gsd-core/workflows/code-review.md +93 -24
  144. package/gsd-core/workflows/complete-milestone.md +3 -0
  145. package/gsd-core/workflows/debug.md +35 -7
  146. package/gsd-core/workflows/diagnose-issues.md +5 -1
  147. package/gsd-core/workflows/discovery-phase.md +7 -0
  148. package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -4
  149. package/gsd-core/workflows/discuss-phase/modes/auto.md +0 -6
  150. package/gsd-core/workflows/discuss-phase/templates/context.md +16 -2
  151. package/gsd-core/workflows/discuss-phase-assumptions.md +18 -9
  152. package/gsd-core/workflows/discuss-phase.md +2 -2
  153. package/gsd-core/workflows/do.md +7 -1
  154. package/gsd-core/workflows/docs-update.md +9 -0
  155. package/gsd-core/workflows/eval-review.md +4 -1
  156. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +4 -0
  157. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +160 -0
  158. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +4 -4
  159. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -2
  160. package/gsd-core/workflows/execute-phase.md +110 -149
  161. package/gsd-core/workflows/execute-plan.md +20 -8
  162. package/gsd-core/workflows/explore.md +4 -0
  163. package/gsd-core/workflows/extract-learnings.md +21 -0
  164. package/gsd-core/workflows/graduation.md +3 -0
  165. package/gsd-core/workflows/health.md +7 -1
  166. package/gsd-core/workflows/help/modes/full.md +9 -5
  167. package/gsd-core/workflows/import.md +11 -2
  168. package/gsd-core/workflows/inbox.md +7 -0
  169. package/gsd-core/workflows/ingest-docs.md +19 -10
  170. package/gsd-core/workflows/manager.md +3 -1
  171. package/gsd-core/workflows/map-codebase.md +17 -10
  172. package/gsd-core/workflows/mvp-phase.md +3 -0
  173. package/gsd-core/workflows/new-milestone.md +79 -23
  174. package/gsd-core/workflows/new-project.md +28 -19
  175. package/gsd-core/workflows/new-workspace.md +3 -1
  176. package/gsd-core/workflows/next.md +5 -2
  177. package/gsd-core/workflows/onboard.md +3 -0
  178. package/gsd-core/workflows/plan-phase.md +56 -51
  179. package/gsd-core/workflows/plan-review-convergence.md +61 -12
  180. package/gsd-core/workflows/plant-seed.md +3 -0
  181. package/gsd-core/workflows/profile-user.md +7 -1
  182. package/gsd-core/workflows/progress.md +31 -3
  183. package/gsd-core/workflows/quick.md +33 -10
  184. package/gsd-core/workflows/remove-workspace.md +3 -0
  185. package/gsd-core/workflows/review.md +172 -585
  186. package/gsd-core/workflows/scan.md +10 -2
  187. package/gsd-core/workflows/secure-phase.md +13 -2
  188. package/gsd-core/workflows/settings-integrations.md +3 -0
  189. package/gsd-core/workflows/settings.md +3 -0
  190. package/gsd-core/workflows/ship.md +88 -11
  191. package/gsd-core/workflows/sketch.md +3 -0
  192. package/gsd-core/workflows/smart-entry.md +4 -1
  193. package/gsd-core/workflows/spike.md +7 -1
  194. package/gsd-core/workflows/ui-phase.md +11 -2
  195. package/gsd-core/workflows/ui-review.md +11 -1
  196. package/gsd-core/workflows/undo.md +7 -0
  197. package/gsd-core/workflows/update.md +106 -5
  198. package/gsd-core/workflows/validate-phase.md +13 -2
  199. package/gsd-core/workflows/verify-phase.md +2 -2
  200. package/gsd-core/workflows/verify-work.md +15 -4
  201. package/hooks/dist/gsd-context-monitor.js +27 -9
  202. package/hooks/dist/gsd-cursor-session-start.js +6 -2
  203. package/hooks/dist/gsd-cursor-stop.js +6 -2
  204. package/hooks/dist/gsd-cursor-subagent-start.js +6 -2
  205. package/hooks/dist/gsd-graphify-update.sh +9 -0
  206. package/hooks/dist/gsd-phase-boundary.sh +14 -2
  207. package/hooks/dist/gsd-prompt-guard.js +101 -2
  208. package/hooks/dist/gsd-read-guard.js +100 -2
  209. package/hooks/dist/gsd-read-injection-scanner.js +109 -2
  210. package/hooks/dist/gsd-statusline.js +97 -9
  211. package/hooks/dist/gsd-workflow-guard.js +110 -6
  212. package/hooks/dist/gsd-worktree-path-guard.js +132 -8
  213. package/hooks/dist/lib/cursor-workspace.js +74 -0
  214. package/hooks/gsd-context-monitor.js +27 -9
  215. package/hooks/gsd-cursor-session-start.js +6 -2
  216. package/hooks/gsd-cursor-stop.js +6 -2
  217. package/hooks/gsd-cursor-subagent-start.js +6 -2
  218. package/hooks/gsd-graphify-update.sh +9 -0
  219. package/hooks/gsd-phase-boundary.sh +14 -2
  220. package/hooks/gsd-prompt-guard.js +101 -2
  221. package/hooks/gsd-read-guard.js +100 -2
  222. package/hooks/gsd-read-injection-scanner.js +109 -2
  223. package/hooks/gsd-statusline.js +97 -9
  224. package/hooks/gsd-workflow-guard.js +110 -6
  225. package/hooks/gsd-worktree-path-guard.js +132 -8
  226. package/hooks/lib/cursor-workspace.js +74 -0
  227. package/package.json +10 -8
  228. package/pi/gsd.cjs +34 -3
  229. package/scripts/changeset/lint.cjs +1 -0
  230. package/scripts/changeset/parse.cjs +26 -0
  231. package/scripts/check-coverage-gate.cjs +51 -0
  232. package/scripts/check-glossary-refs.cjs +244 -0
  233. package/scripts/ci-rebase-check.cjs +48 -4
  234. package/scripts/ci-test-scope.cjs +67 -17
  235. package/scripts/gen-adr-index.cjs +528 -0
  236. package/scripts/gen-capability-matrix.cjs +26 -2
  237. package/scripts/gen-capability-registry.cjs +132 -34
  238. package/scripts/gen-emitted-baseline.cjs +145 -0
  239. package/scripts/gen-test-timings.cjs +201 -0
  240. package/scripts/lint-compiled-artifact-sync.cjs +146 -0
  241. package/scripts/lint-emitted-drift-ack.cjs +149 -0
  242. package/scripts/lint-fix-has-regression-test.cjs +131 -0
  243. package/scripts/lint-portable-timeout.cjs +140 -0
  244. package/scripts/lint-resolution-provenance.cjs +9 -0
  245. package/scripts/lint-test-file-count.allowlist.json +1 -0
  246. package/scripts/mutation-matrix.cjs +4 -0
  247. package/scripts/prompt-injection-scan.sh +6 -0
  248. package/scripts/registry-schema.cjs +57 -8
  249. package/scripts/release-notes/conventional-title.cjs +19 -1
  250. package/scripts/release-notes/format-github-release-notes.cjs +7 -3
  251. package/scripts/release-tarball-smoke.cjs +18 -11
  252. package/scripts/run-tests.cjs +420 -58
  253. package/scripts/workflow-size.cjs +16 -8
  254. package/skills/gsd-ai-integration-phase/SKILL.md +1 -1
  255. package/skills/gsd-mempalace-capture/SKILL.md +9 -5
  256. package/skills/gsd-new-milestone/SKILL.md +1 -1
  257. package/skills/gsd-plan-phase/SKILL.md +5 -3
  258. package/skills/gsd-plan-review-convergence/SKILL.md +7 -2
  259. package/vscode/package.json +1 -1
  260. package/scripts/gen-golden-install-parity-zcode.cjs +0 -77
  261. package/scripts/update-size-baseline.cjs +0 -68
@@ -11,12 +11,17 @@
11
11
  * epic #1267; callers import resolvers from model-resolver.cjs directly.
12
12
  *
13
13
  * Dependencies (leaf modules only):
14
- * - node:fs / node:path (stdlib, not currently needed — included for future use)
14
+ * - node:fs / node:path (read the per-install .gsd-runtime marker + project config for the #2297 omit gate)
15
+ * - ./runtime-name-policy.cjs (resolveRuntimeNameFromCandidates — canonicalize the active runtime)
16
+ * - ./planning-workspace.cjs (planningDir — workstream/project-aware project-config path)
15
17
  * - ./config-loader.cjs (loadConfig)
16
18
  * - ./configuration.cjs (CONFIG_DEFAULTS as CANONICAL_CONFIG_DEFAULTS)
17
19
  * - ./model-profiles.cjs (MODEL_PROFILES, AGENT_TO_PHASE_TYPE, AGENT_DEFAULT_TIERS, VALID_AGENT_TIERS, nextTier)
18
- * - ./model-catalog.cjs (MODEL_ALIAS_MAP, RUNTIME_PROFILE_MAP, PROVIDER_PRESETS)
20
+ * - ./model-catalog.cjs (MODEL_ALIAS_MAP, RUNTIME_PROFILE_MAP, PROVIDER_PRESETS, VALID_TIERS)
19
21
  */
22
+ var __importDefault = (this && this.__importDefault) || function (mod) {
23
+ return (mod && mod.__esModule) ? mod : { "default": mod };
24
+ };
20
25
  // eslint-disable-next-line @typescript-eslint/no-require-imports
21
26
  const configLoaderModule = require("./config-loader.cjs");
22
27
  const { loadConfig } = configLoaderModule;
@@ -26,6 +31,92 @@ const configuration_cjs_1 = require("./configuration.cjs");
26
31
  const modelProfiles = require("./model-profiles.cjs");
27
32
  const { MODEL_PROFILES, AGENT_TO_PHASE_TYPE, AGENT_DEFAULT_TIERS, VALID_AGENT_TIERS, nextTier } = modelProfiles;
28
33
  const model_catalog_cjs_1 = require("./model-catalog.cjs");
34
+ const node_fs_1 = __importDefault(require("node:fs"));
35
+ const node_path_1 = __importDefault(require("node:path"));
36
+ const runtime_name_policy_cjs_1 = require("./runtime-name-policy.cjs");
37
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
38
+ const planningWorkspaceMod = require("./planning-workspace.cjs");
39
+ const { planningDir } = planningWorkspaceMod;
40
+ // ─── #2297: per-install runtime identity for the resolve_model_ids:"omit" gate ─
41
+ //
42
+ // The installer writes `resolve_model_ids:"omit"` into the SHARED
43
+ // ~/.gsd/defaults.json for every runtime that lacks native model aliases (#1156).
44
+ // Because that file is machine-wide, a non-Claude install would otherwise poison
45
+ // a Claude no-project resolution into returning '' — silently defeating Claude's
46
+ // adaptive tier aliases. The "omit" must therefore apply only when a runtime that
47
+ // genuinely lacks native aliases is the one resolving.
48
+ //
49
+ // In a no-project session there is no `.planning/config.json` (so config.runtime
50
+ // is null) and GSD_RUNTIME is not exported by gsd-core, so the only reliable
51
+ // current-runtime signal is the per-install marker the installer co-locates next
52
+ // to VERSION at <install>/gsd-core/.gsd-runtime (this file's dir is
53
+ // <install>/gsd-core/bin/lib). Precedence for the gate: project config.runtime →
54
+ // GSD_RUNTIME env (manual/CI override + test seam) → install marker → 'claude'.
55
+ //
56
+ // `claude` is currently the ONLY runtime with nativeModelAliases:true; a
57
+ // registry-parity test guards this set so a future alias-capable runtime fails
58
+ // loudly here instead of silently omitting.
59
+ const RUNTIMES_WITH_NATIVE_ALIASES = new Set(['claude']);
60
+ let _installMarkerCache;
61
+ function readInstallRuntimeMarker() {
62
+ if (_installMarkerCache !== undefined)
63
+ return _installMarkerCache;
64
+ try {
65
+ const markerPath = node_path_1.default.join(__dirname, '..', '..', '.gsd-runtime');
66
+ const raw = node_fs_1.default.readFileSync(markerPath, 'utf8').trim();
67
+ _installMarkerCache = raw || null;
68
+ }
69
+ catch {
70
+ // No marker: dev/source tree, or an install predating #2297. Fall through to
71
+ // the 'claude' default (keeps tier aliases — never worse than the bug).
72
+ _installMarkerCache = null;
73
+ }
74
+ return _installMarkerCache;
75
+ }
76
+ // Test seams for the install-marker rung (the dev/source tree has no marker, so
77
+ // the file read always bottoms out at 'claude' — these let tests exercise the
78
+ // third precedence rung and reset the module-level cache between cases).
79
+ function _setInstallRuntimeMarkerForTests(value) {
80
+ _installMarkerCache = value;
81
+ }
82
+ function _resetInstallRuntimeMarkerCacheForTests() {
83
+ _installMarkerCache = undefined;
84
+ }
85
+ // The runtime whose install is actually resolving, canonicalized so an alias or
86
+ // case variant (e.g. "claude-code"/"Claude") cannot defeat the native-alias
87
+ // check below (#2297 review). Precedence mirrors resolveRuntime()
88
+ // (runtime-slash.cts): GSD_RUNTIME env → project config.runtime → per-install
89
+ // .gsd-runtime marker → 'claude'.
90
+ function resolveActiveRuntime(config) {
91
+ return (0, runtime_name_policy_cjs_1.resolveRuntimeNameFromCandidates)(process.env['GSD_RUNTIME'], config['runtime'], readInstallRuntimeMarker()) || 'claude';
92
+ }
93
+ // Did the PROJECT's own config (root `.planning/config.json` or the active
94
+ // workstream/project override) explicitly set resolve_model_ids to "omit"?
95
+ // Project config takes precedence over the shared ~/.gsd/defaults.json (#2297
96
+ // out-of-scope guard + #2517 finding #4): an explicit project "omit" is honored
97
+ // regardless of runtime, whereas an "omit" that came only from the global
98
+ // defaults is ignored by native-alias runtimes. Workstream/project-scope aware
99
+ // via planningDir (mirrors loadConfig's precedence: workstream value wins over
100
+ // root); a plain read avoids loadConfig's normalization side effects.
101
+ function projectExplicitlySetsOmit(cwd) {
102
+ const wsDir = planningDir(cwd);
103
+ const rootDir = node_path_1.default.join(cwd, '.planning');
104
+ const layers = wsDir === rootDir ? [rootDir] : [wsDir, rootDir]; // workstream > root
105
+ for (const dir of layers) {
106
+ try {
107
+ const parsed = JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.join(dir, 'config.json'), 'utf8'));
108
+ const value = parsed?.['resolve_model_ids'];
109
+ // First layer that sets the key wins (matches loadConfig's deep-merge
110
+ // precedence). A layer that omits the key falls through to the next.
111
+ if (value !== undefined)
112
+ return value === 'omit';
113
+ }
114
+ catch {
115
+ // Absent/unreadable layer — try the next.
116
+ }
117
+ }
118
+ return false;
119
+ }
29
120
  /**
30
121
  * #2517 — Resolve the runtime-aware tier entry for (runtime, tier).
31
122
  */
@@ -209,8 +300,7 @@ function resolveModelInternal(cwd, agentType) {
209
300
  const phaseTypeTier = (phaseType && configModels && typeof configModels === 'object')
210
301
  ? configModels[phaseType]
211
302
  : undefined;
212
- const VALID_TIERS = new Set(['opus', 'sonnet', 'haiku', 'inherit']);
213
- const tier = (phaseTypeTier && VALID_TIERS.has(phaseTypeTier))
303
+ const tier = (phaseTypeTier && model_catalog_cjs_1.VALID_TIERS.has(phaseTypeTier))
214
304
  ? phaseTypeTier
215
305
  : (profile === 'inherit'
216
306
  ? 'inherit'
@@ -248,8 +338,18 @@ function resolveModelInternal(cwd, agentType) {
248
338
  if (entry?.model)
249
339
  return entry.model;
250
340
  }
251
- // 4. resolve_model_ids: "omit"
252
- if (config['resolve_model_ids'] === 'omit') {
341
+ // 4. resolve_model_ids: "omit" — runtime-aware (#2297). Honor "omit" when the
342
+ // PROJECT explicitly set it (user intent — project config wins, #2517 finding
343
+ // #4) OR when the active runtime genuinely lacks native model aliases. Only a
344
+ // native-alias runtime (Claude) ignores an "omit" that came solely from the
345
+ // SHARED ~/.gsd/defaults.json — the #2297 poisoning fix — and falls through to
346
+ // its tier aliases below. Active runtime: GSD_RUNTIME → config.runtime → the
347
+ // per-install .gsd-runtime marker → 'claude' (canonicalized).
348
+ // NOTE: a non-Claude runtime that HAS a populated runtime-tier map already
349
+ // returned its own model id at step 3 above, before this gate — for those the
350
+ // explicit-project-omit honoring here is moot (step 3 wins, by #2517 design).
351
+ if (config['resolve_model_ids'] === 'omit'
352
+ && (projectExplicitlySetsOmit(cwd) || !RUNTIMES_WITH_NATIVE_ALIASES.has(resolveActiveRuntime(config)))) {
253
353
  return '';
254
354
  }
255
355
  // 5. Profile lookup (Claude-native default).
@@ -262,7 +362,11 @@ function resolveModelInternal(cwd, agentType) {
262
362
  if (tier === 'inherit')
263
363
  return 'inherit';
264
364
  const alias = tier;
265
- if (config['resolve_model_ids']) {
365
+ // Only the explicit `true` opt-in materializes full model IDs (#1569). Guard
366
+ // against the loose-truthy check catching a "omit" that a native-alias runtime
367
+ // ignored above (#2297): "omit" must fall through to the tier ALIAS here, not
368
+ // be materialized into a full ID Claude's Agent tool cannot spawn.
369
+ if (config['resolve_model_ids'] === true) {
266
370
  return model_catalog_cjs_1.MODEL_ALIAS_MAP[alias] || alias;
267
371
  }
268
372
  return alias;
@@ -353,6 +457,80 @@ function resolveModelForTier(cwd, agentType, attempt) {
353
457
  }
354
458
  return alias;
355
459
  }
460
+ /**
461
+ * Keep only usable model ids: non-empty strings. A malformed config can put
462
+ * anything in here (nulls, numbers, blank strings), and a blank model id would
463
+ * resolve to an unusable agent invocation rather than failing visibly. Invalid
464
+ * entries are dropped and the surviving order is preserved, so the ladder stays
465
+ * predictable (ADR 227 — validate shape, not just type).
466
+ */
467
+ function sanitizeProviderEscalation(raw) {
468
+ if (!Array.isArray(raw))
469
+ return [];
470
+ return raw.filter((entry) => typeof entry === 'string' && entry.trim().length > 0);
471
+ }
472
+ /**
473
+ * #2296 — Resolve the model for one attempt of the PROVIDER escalation ladder.
474
+ *
475
+ * The tier ladder (`resolveModelForTier`) escalates within one provider's
476
+ * `tier_models`, which does not help when that provider is the thing that is
477
+ * throttled. This walks `dynamic_routing.provider_escalation` instead: an
478
+ * ordered list of alternative model ids, capped by
479
+ * `min(max_escalations, list length)`.
480
+ *
481
+ * `applicable` is the caller's policy decision (only a quota-exceeded
482
+ * classification should consult this ladder). It is a parameter rather than a
483
+ * class check here so this module keeps depending only on leaf modules, per
484
+ * CONTEXT.md's Model Resolution module contract.
485
+ *
486
+ * Attempt 0 — and every non-applicable call — stays on the source model.
487
+ * `exhausted` reports that the ladder is spent so the caller can fail loudly
488
+ * naming every model it tried.
489
+ */
490
+ function resolveProviderEscalation(cwd, agentType, attempt, applicable) {
491
+ // The model that would be used with no provider escalation at all.
492
+ const from = resolveModelForTier(cwd, agentType, 0);
493
+ const stay = (exhausted = false) => ({
494
+ from,
495
+ to: from,
496
+ escalated: false,
497
+ exhausted,
498
+ attempted: [from],
499
+ index: 0,
500
+ });
501
+ if (!applicable)
502
+ return stay();
503
+ const config = loadConfig(cwd);
504
+ const dr = config['dynamic_routing'];
505
+ if (!dr || typeof dr !== 'object' || dr['enabled'] !== true)
506
+ return stay();
507
+ if (dr['escalate_on_failure'] === false)
508
+ return stay();
509
+ const list = sanitizeProviderEscalation(dr['provider_escalation']);
510
+ if (list.length === 0)
511
+ return stay();
512
+ // Same default and same validity rule as the tier ladder above — a negative or
513
+ // non-integer max_escalations is invalid config, not a request for zero.
514
+ const maxEscalations = Number.isInteger(dr['max_escalations']) && dr['max_escalations'] >= 0
515
+ ? dr['max_escalations']
516
+ : 1;
517
+ const cap = Math.min(maxEscalations, list.length);
518
+ // An explicit cap of 0 means the ladder exists but is spent before it starts.
519
+ if (cap === 0)
520
+ return stay(true);
521
+ const attemptN = Number.isInteger(attempt) && attempt > 0 ? attempt : 0;
522
+ if (attemptN === 0)
523
+ return stay();
524
+ const index = Math.min(attemptN, cap);
525
+ return {
526
+ from,
527
+ to: list[index - 1],
528
+ escalated: true,
529
+ exhausted: attemptN > cap,
530
+ attempted: [from, ...list.slice(0, index)],
531
+ index,
532
+ };
533
+ }
356
534
  // ─── #443 — Unified effort + fast_mode resolvers ─────────────────────────────
357
535
  const VALID_EFFORTS = ['minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
358
536
  const EFFORT_SET = new Set(VALID_EFFORTS);
@@ -515,14 +693,18 @@ function resolveEffortForTier(cwd, agentType, attempt) {
515
693
  }
516
694
  module.exports = {
517
695
  resolveTierEntry,
696
+ CLAUDE_AGENT_ALIASES,
518
697
  resolveModelPolicy,
519
698
  resolveModelInternal,
520
699
  _resetModelPolicyWarningCacheForTests,
521
700
  _resetModelOverrideWarningCacheForTests,
701
+ _setInstallRuntimeMarkerForTests,
702
+ _resetInstallRuntimeMarkerCacheForTests,
522
703
  VALID_GRANULARITIES,
523
704
  resolveGranularityInternal,
524
705
  assertValidGranularityOverride,
525
706
  resolveModelForTier,
707
+ resolveProviderEscalation,
526
708
  VALID_EFFORTS,
527
709
  EFFORT_SET,
528
710
  nextEffort,
@@ -41,7 +41,12 @@ function _safeStringify(value) {
41
41
  }
42
42
  }
43
43
  /**
44
- * Determine whether the audit file should be written to.
44
+ * Determine whether observability is opt-in enabled — via the GSD_AUDIT env var
45
+ * or config.audit.enabled. Exported (#2620) so the live dispatch seam can decide
46
+ * whether to inject the reference logger at all: when observability is off we
47
+ * inject nothing (the Hub stays byte-for-byte silent, preserving the default
48
+ * dispatch output contract, incl. --json-errors); when on, the caller injects
49
+ * createDefaultLogger and gets the stderr-on-error line + opt-in file audit.
45
50
  */
46
51
  function _isAuditEnabled(config) {
47
52
  if (process.env['GSD_AUDIT'] === '1')
@@ -143,4 +148,4 @@ function createDefaultLogger({ cwd = process.cwd(), config } = {}) {
143
148
  },
144
149
  };
145
150
  }
146
- module.exports = { createDefaultLogger, createNoOpLogger };
151
+ module.exports = { createDefaultLogger, createNoOpLogger, isAuditEnabled: _isAuditEnabled };
@@ -269,7 +269,9 @@ function buildOnboardProjection(cwd, options) {
269
269
  projectExists,
270
270
  mapReadiness: mapReadinessValue,
271
271
  onboardingSummaryExists,
272
- onboardingSummaryPath: toPosixPath(node_path_1.default.relative(cwd, onboardingSummaryPath)),
272
+ // #2376: absolute (anchored on cwd/project_root), not orchestrator-cwd-relative —
273
+ // a spawned subagent's own cwd may differ from the orchestrator's.
274
+ onboardingSummaryPath: toPosixPath(onboardingSummaryPath),
273
275
  hasPlanningArtifacts,
274
276
  missingPlanningFiles,
275
277
  handoffCommands,
@@ -289,13 +291,14 @@ function buildOnboardProjection(cwd, options) {
289
291
  doc_candidate_count: docCandidates.length,
290
292
  doc_candidates: docCandidates,
291
293
  onboarding_summary_exists: onboardingSummaryExists,
292
- onboarding_summary_path: toPosixPath(node_path_1.default.relative(cwd, onboardingSummaryPath)),
293
- project_path: toPosixPath(node_path_1.default.relative(cwd, node_fs_1.default.existsSync(projectRootPath) ? projectRootPath : projectScopedPath)),
294
- requirements_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'REQUIREMENTS.md'))),
295
- roadmap_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'ROADMAP.md'))),
296
- state_path: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'STATE.md'))),
297
- codebase_dir: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningRoot(cwd), 'codebase'))),
298
- onboarding_dir: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningRoot(cwd), 'onboarding'))),
294
+ // #2376: absolute — see comment on onboardingSummaryPath above.
295
+ onboarding_summary_path: toPosixPath(onboardingSummaryPath),
296
+ project_path: toPosixPath(node_fs_1.default.existsSync(projectRootPath) ? projectRootPath : projectScopedPath),
297
+ requirements_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'REQUIREMENTS.md')),
298
+ roadmap_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
299
+ state_path: toPosixPath(node_path_1.default.join(planningDir(cwd), 'STATE.md')),
300
+ codebase_dir: toPosixPath(node_path_1.default.join(planningRoot(cwd), 'codebase')),
301
+ onboarding_dir: toPosixPath(node_path_1.default.join(planningRoot(cwd), 'onboarding')),
299
302
  };
300
303
  }
301
304
  module.exports = {
@@ -20,6 +20,12 @@ const command_aliases_cjs_1 = require("./command-aliases.cjs");
20
20
  // eslint-disable-next-line @typescript-eslint/no-require-imports
21
21
  const commandRoutingHub = require("./command-routing-hub.cjs");
22
22
  const { createHub, ERROR_KINDS, makeInvalidArgs } = commandRoutingHub;
23
+ // #2620 (ADR-0174 §6): inject the reference DispatchLogger on the live phase
24
+ // dispatch path, but only when observability is opt-in enabled; otherwise the
25
+ // Hub stays byte-for-byte silent via its no-op fallback.
26
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
27
+ const observabilityLogger = require("./observability/logger.cjs");
28
+ const { createDefaultLogger, isAuditEnabled } = observabilityLogger;
23
29
  // ─── Implementation ───────────────────────────────────────────────────────────
24
30
  function routePhaseCommand({ phase, args, cwd, raw, error }) {
25
31
  // ── Unsupported subcommands ─────────────────────────────────────────────────
@@ -229,7 +235,10 @@ function routePhaseCommand({ phase, args, cwd, raw, error }) {
229
235
  const manifest = { phase: manifestSubcommands };
230
236
  // ── Construct hub ──────────────────────────────────────────────────────────
231
237
  // #175: Hub is CJS-only — no mode param, no sdkLoader.
232
- const hub = createHub({ cjsRegistry, manifest });
238
+ // #2620: wire the reference logger (ADR-0174 §6) only when observability is
239
+ // opt-in enabled; otherwise leave it unset so the Hub stays byte-for-byte
240
+ // silent via its no-op fallback.
241
+ const hub = createHub({ cjsRegistry, manifest, logger: isAuditEnabled() ? createDefaultLogger({ cwd }) : undefined });
233
242
  // ── Dispatch ────────────────────────────────────────────────────────────────
234
243
  const result = hub.dispatch({
235
244
  family: 'phase',