@opengsd/gsd-core 1.9.0 → 1.10.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 (223) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -3
  3. package/.opencode/plugins/gsd-core.js +8 -1
  4. package/agents/gsd-code-fixer.md +131 -34
  5. package/agents/gsd-debugger.md +12 -246
  6. package/agents/gsd-executor.md +7 -5
  7. package/agents/gsd-integration-checker.md +3 -0
  8. package/agents/gsd-plan-checker.md +9 -0
  9. package/agents/gsd-planner.md +5 -8
  10. package/agents/gsd-roadmapper.md +21 -3
  11. package/agents/gsd-verifier.md +14 -70
  12. package/bin/install.js +503 -341
  13. package/commands/gsd/mempalace-capture.md +1 -1
  14. package/commands/gsd/new-milestone.md +1 -1
  15. package/commands/gsd/plan-phase.md +1 -1
  16. package/gsd-core/bin/gsd-tools.cjs +607 -63
  17. package/gsd-core/bin/lib/active-workstream-store.cjs +25 -0
  18. package/gsd-core/bin/lib/agent-install-check.cjs +38 -6
  19. package/gsd-core/bin/lib/api-coverage.cjs +120 -0
  20. package/gsd-core/bin/lib/audit.cjs +89 -1
  21. package/gsd-core/bin/lib/broken-windows.cjs +36 -6
  22. package/gsd-core/bin/lib/capability-registry.cjs +96 -110
  23. package/gsd-core/bin/lib/capability-validator.cjs +12 -2
  24. package/gsd-core/bin/lib/check-command-router.cjs +43 -1
  25. package/gsd-core/bin/lib/command-aliases.cjs +72 -0
  26. package/gsd-core/bin/lib/commands.cjs +26 -25
  27. package/gsd-core/bin/lib/commonjs-marker.cjs +136 -0
  28. package/gsd-core/bin/lib/config-loader.cjs +1 -0
  29. package/gsd-core/bin/lib/config.cjs +12 -1
  30. package/gsd-core/bin/lib/context-composer.cjs +278 -0
  31. package/gsd-core/bin/lib/context-predicates.cjs +506 -0
  32. package/gsd-core/bin/lib/core-utils.cjs +91 -12
  33. package/gsd-core/bin/lib/docs.cjs +3 -2
  34. package/gsd-core/bin/lib/external-job.cjs +19 -4
  35. package/gsd-core/bin/lib/frontmatter.cjs +84 -12
  36. package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +57 -6
  37. package/gsd-core/bin/lib/git-base-branch.cjs +58 -15
  38. package/gsd-core/bin/lib/graphify.cjs +142 -27
  39. package/gsd-core/bin/lib/gsd2-import.cjs +27 -4
  40. package/gsd-core/bin/lib/host-integration.cjs +13 -1
  41. package/gsd-core/bin/lib/init-command-router.cjs +83 -8
  42. package/gsd-core/bin/lib/init.cjs +1021 -57
  43. package/gsd-core/bin/lib/install-engine.cjs +64 -10
  44. package/gsd-core/bin/lib/install-profiles.cjs +27 -1
  45. package/gsd-core/bin/lib/installer-migration-authoring.cjs +3 -1
  46. package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
  47. package/gsd-core/bin/lib/installer-migrations/007-retire-config-root-commonjs-marker.cjs +149 -0
  48. package/gsd-core/bin/lib/installer-migrations/008-cursor-retire-commands-surface.cjs +55 -0
  49. package/gsd-core/bin/lib/installer-migrations/009-pi-retire-reserved-hooks-dir.cjs +199 -0
  50. package/gsd-core/bin/lib/installer-migrations.cjs +87 -1
  51. package/gsd-core/bin/lib/io.cjs +28 -3
  52. package/gsd-core/bin/lib/markdown-sectionizer.cjs +6 -0
  53. package/gsd-core/bin/lib/mcp-catalog.cjs +518 -0
  54. package/gsd-core/bin/lib/mcp-server.cjs +135 -3
  55. package/gsd-core/bin/lib/milestone.cjs +106 -51
  56. package/gsd-core/bin/lib/phase-id.cjs +63 -0
  57. package/gsd-core/bin/lib/phase-locator.cjs +138 -45
  58. package/gsd-core/bin/lib/phase.cjs +260 -25
  59. package/gsd-core/bin/lib/plan-dependency-graph.cjs +232 -0
  60. package/gsd-core/bin/lib/planning-workspace.cjs +4 -0
  61. package/gsd-core/bin/lib/project-root.cjs +48 -0
  62. package/gsd-core/bin/lib/prompt-budget.cjs +128 -165
  63. package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +80 -0
  64. package/gsd-core/bin/lib/review-lane-descriptor.cjs +99 -0
  65. package/gsd-core/bin/lib/review-lane-runner.cjs +30 -6
  66. package/gsd-core/bin/lib/roadmap-command-router.cjs +42 -9
  67. package/gsd-core/bin/lib/roadmap-parser.cjs +100 -18
  68. package/gsd-core/bin/lib/roadmap.cjs +37 -7
  69. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +195 -62
  70. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +15 -3
  71. package/gsd-core/bin/lib/runtime-homes.cjs +154 -41
  72. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +105 -41
  73. package/gsd-core/bin/lib/section-manifest.cjs +209 -0
  74. package/gsd-core/bin/lib/shell-command-projection.cjs +113 -27
  75. package/gsd-core/bin/lib/smart-entry.cjs +12 -0
  76. package/gsd-core/bin/lib/state-transition.cjs +73 -8
  77. package/gsd-core/bin/lib/state.cjs +151 -62
  78. package/gsd-core/bin/lib/surface.cjs +12 -1
  79. package/gsd-core/bin/lib/uat-predicate.cjs +11 -1
  80. package/gsd-core/bin/lib/uat.cjs +320 -21
  81. package/gsd-core/bin/lib/unusable-input.cjs +9 -0
  82. package/gsd-core/bin/lib/verification.cjs +29 -12
  83. package/gsd-core/bin/lib/verify.cjs +29 -5
  84. package/gsd-core/bin/lib/workflow-fragments.cjs +557 -0
  85. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +181 -18
  86. package/gsd-core/bin/lib/workstream-inventory.cjs +519 -27
  87. package/gsd-core/bin/lib/workstream.cjs +6 -0
  88. package/gsd-core/bin/lib/worktree-base-ref.cjs +50 -6
  89. package/gsd-core/bin/lib/worktree-safety.cjs +276 -118
  90. package/gsd-core/bin/shared/config-schema.manifest.json +2 -0
  91. package/gsd-core/references/artifact-types.md +10 -3
  92. package/gsd-core/references/autonomous-ui-design-contract.md +42 -0
  93. package/gsd-core/references/debugger-techniques.md +255 -0
  94. package/gsd-core/references/research-documentation-lookup.md +5 -3
  95. package/gsd-core/references/specless-probe-fallback.md +7 -6
  96. package/gsd-core/references/verifier-wiring-patterns.md +100 -0
  97. package/gsd-core/references/worktree-branch-check.md +2 -2
  98. package/gsd-core/templates/summary-complex.md +2 -0
  99. package/gsd-core/templates/summary-minimal.md +2 -0
  100. package/gsd-core/templates/summary-standard.md +2 -0
  101. package/gsd-core/templates/summary.md +2 -0
  102. package/gsd-core/workflows/audit-milestone.md +3 -0
  103. package/gsd-core/workflows/autonomous/steps/converge-banner.md +1 -0
  104. package/gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md +11 -0
  105. package/gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md +7 -0
  106. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +21 -0
  107. package/gsd-core/workflows/autonomous/steps/converge-loop.md +7 -0
  108. package/gsd-core/workflows/autonomous.md +32 -69
  109. package/gsd-core/workflows/code-review/steps/dispatch-fix.md +39 -0
  110. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +83 -0
  111. package/gsd-core/workflows/code-review.md +42 -145
  112. package/gsd-core/workflows/complete-milestone/steps/git-tag.md +29 -0
  113. package/gsd-core/workflows/complete-milestone.md +23 -81
  114. package/gsd-core/workflows/debug.md +9 -12
  115. package/gsd-core/workflows/diagnose-issues.md +22 -0
  116. package/gsd-core/workflows/discovery-phase.md +4 -4
  117. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +15 -0
  118. package/gsd-core/workflows/discuss-phase-assumptions.md +5 -16
  119. package/gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md +51 -0
  120. package/gsd-core/workflows/docs-update.md +8 -51
  121. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +34 -2
  122. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +50 -0
  123. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +31 -0
  124. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +19 -0
  125. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +42 -0
  126. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +43 -37
  127. package/gsd-core/workflows/execute-phase.md +65 -137
  128. package/gsd-core/workflows/execute-plan.md +1 -1
  129. package/gsd-core/workflows/help/modes/full.md +6 -1
  130. package/gsd-core/workflows/ingest-docs.md +2 -1
  131. package/gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md +16 -0
  132. package/gsd-core/workflows/new-milestone/steps/reset-phase-safety.md +19 -0
  133. package/gsd-core/workflows/new-milestone.md +21 -38
  134. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +176 -0
  135. package/gsd-core/workflows/new-project/steps/auto-mode-detection.md +32 -0
  136. package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +18 -0
  137. package/gsd-core/workflows/new-project.md +13 -226
  138. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +15 -0
  139. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +110 -0
  140. package/gsd-core/workflows/plan-phase/steps/prd-express-gate.md +8 -0
  141. package/gsd-core/workflows/plan-phase/steps/research-only-early-exit.md +17 -0
  142. package/gsd-core/workflows/plan-phase/steps/research-only-modifiers.md +16 -0
  143. package/gsd-core/workflows/plan-phase/steps/reviews-prerequisite.md +17 -0
  144. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +149 -0
  145. package/gsd-core/workflows/plan-phase.md +49 -193
  146. package/gsd-core/workflows/progress/steps/forensic-audit.md +125 -0
  147. package/gsd-core/workflows/progress/steps/mvp-display.md +18 -0
  148. package/gsd-core/workflows/progress.md +11 -153
  149. package/gsd-core/workflows/quick/steps/discussion-phase.md +124 -0
  150. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +111 -0
  151. package/gsd-core/workflows/quick/steps/quick-verification.md +46 -0
  152. package/gsd-core/workflows/quick/steps/research-phase.md +72 -0
  153. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +37 -0
  154. package/gsd-core/workflows/quick.md +20 -390
  155. package/gsd-core/workflows/resume-project.md +3 -0
  156. package/gsd-core/workflows/review/steps/reviewer-instances-note-1.md +4 -0
  157. package/gsd-core/workflows/review/steps/reviewer-instances-note-2.md +3 -0
  158. package/gsd-core/workflows/review.md +15 -8
  159. package/gsd-core/workflows/section-manifest.json +219 -0
  160. package/gsd-core/workflows/sketch.md +1 -1
  161. package/gsd-core/workflows/spec-phase.md +17 -14
  162. package/gsd-core/workflows/spike-wrap-up.md +20 -5
  163. package/gsd-core/workflows/spike.md +50 -16
  164. package/gsd-core/workflows/sync-skills.md +49 -11
  165. package/gsd-core/workflows/transition/steps/workstream-collision-check.md +17 -0
  166. package/gsd-core/workflows/transition.md +8 -21
  167. package/gsd-core/workflows/ui-phase.md +8 -7
  168. package/gsd-core/workflows/update/steps/channel-banner.md +7 -0
  169. package/gsd-core/workflows/update.md +18 -7
  170. package/gsd-core/workflows/verify-phase.md +4 -7
  171. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +36 -0
  172. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +21 -0
  173. package/gsd-core/workflows/verify-work.md +8 -58
  174. package/hooks/dist/gsd-agent-isolation-guard.js +428 -0
  175. package/hooks/dist/gsd-check-update-worker.js +14 -5
  176. package/hooks/dist/gsd-cursor-subagent-start.js +532 -26
  177. package/hooks/dist/gsd-read-injection-scanner.js +7 -0
  178. package/hooks/dist/gsd-statusline.js +72 -6
  179. package/hooks/dist/gsd-worktree-path-guard.js +2 -1
  180. package/hooks/dist/gsd-write-guard.js +359 -0
  181. package/hooks/dist/lib/isolation-sentinel.js +268 -0
  182. package/hooks/dist/managed-hooks-registry.cjs +2 -0
  183. package/hooks/gsd-agent-isolation-guard.js +428 -0
  184. package/hooks/gsd-check-update-worker.js +14 -5
  185. package/hooks/gsd-cursor-subagent-start.js +532 -26
  186. package/hooks/gsd-read-injection-scanner.js +7 -0
  187. package/hooks/gsd-statusline.js +72 -6
  188. package/hooks/gsd-worktree-path-guard.js +2 -1
  189. package/hooks/gsd-write-guard.js +359 -0
  190. package/hooks/hooks.json +12 -0
  191. package/hooks/lib/isolation-sentinel.js +268 -0
  192. package/hooks/managed-hooks-registry.cjs +2 -0
  193. package/package.json +14 -5
  194. package/pi/gsd.cjs +57 -12
  195. package/scripts/build-hooks.js +9 -0
  196. package/scripts/changeset/lint.cjs +9 -2
  197. package/scripts/changeset/serialize.cjs +5 -1
  198. package/scripts/gen-capability-matrix.cjs +1 -1
  199. package/scripts/gen-context-index.cjs +448 -0
  200. package/scripts/gen-inventory-manifest.cjs +101 -1
  201. package/scripts/gen-prompt-budget-parity-corpus.cjs +645 -0
  202. package/scripts/gen-registry.cjs +39 -15
  203. package/scripts/gen-section-manifest.cjs +638 -0
  204. package/scripts/generate-package-identity.cjs +4 -2
  205. package/scripts/lint-allow-test-rule-refs.allowlist.json +17 -31
  206. package/scripts/lint-compiled-artifact-sync.cjs +6 -1
  207. package/scripts/lint-docs-command-form.cjs +195 -0
  208. package/scripts/lint-docs-required.cjs +9 -1
  209. package/scripts/lint-emitted-drift-ack.cjs +215 -20
  210. package/scripts/lint-example-parser-parity.cjs +395 -0
  211. package/scripts/lint-test-file-count.allowlist.json +27 -1
  212. package/scripts/mutation-matrix.cjs +13 -0
  213. package/scripts/prompt-injection-scan.sh +27 -6
  214. package/scripts/registry-schema.cjs +323 -94
  215. package/scripts/run-tests.cjs +3 -2
  216. package/scripts/validate-registry.cjs +10 -6
  217. package/skills/gsd-autonomous/SKILL.md +1 -1
  218. package/skills/gsd-execute-phase/SKILL.md +1 -1
  219. package/skills/gsd-mempalace-capture/SKILL.md +1 -1
  220. package/skills/gsd-new-milestone/SKILL.md +1 -1
  221. package/skills/gsd-plan-phase/SKILL.md +2 -2
  222. package/vscode/package.json +1 -1
  223. package/scripts/gen-emitted-baseline.cjs +0 -145
@@ -707,7 +707,6 @@ const VALID_CONVERTER_NAMES = new Set([
707
707
  'convertClaudeCommandToCodebuddySkill',
708
708
  'convertClaudeCommandToCodexSkill',
709
709
  'convertClaudeCommandToCopilotSkill',
710
- 'convertClaudeCommandToCursorCommand',
711
710
  'convertClaudeCommandToCursorSkill',
712
711
  'convertClaudeCommandToKiloSkill',
713
712
  'convertClaudeCommandToKimiSkill',
@@ -1075,7 +1074,18 @@ function validateArtifactLayout(capId, layout) {
1075
1074
  errors.push(ctx + '.' + scope + ' must be an array');
1076
1075
  } else {
1077
1076
  for (let i = 0; i < arr.length; i++) {
1078
- errors.push(...validateArtifactKindEntry(capId, arr[i], 'artifactLayout.' + scope + '[' + i + ']'));
1077
+ const entry = arr[i];
1078
+ errors.push(...validateArtifactKindEntry(capId, entry, 'artifactLayout.' + scope + '[' + i + ']'));
1079
+ if (
1080
+ scope === 'local' &&
1081
+ typeof entry === 'object' && entry !== null && !Array.isArray(entry) &&
1082
+ Object.prototype.hasOwnProperty.call(entry, 'home')
1083
+ ) {
1084
+ errors.push(
1085
+ ctx + '.local[' + i + '].home is not allowed; ' +
1086
+ 'local artifact layout entries must remain project-scoped',
1087
+ );
1088
+ }
1079
1089
  }
1080
1090
  }
1081
1091
  }
@@ -22,7 +22,11 @@ const { planningDir } = planningWorkspaceMod;
22
22
  const phaseLocatorMod = require("./phase-locator.cjs");
23
23
  const { findPhaseInternal } = phaseLocatorMod;
24
24
  const decisions_cjs_1 = require("./decisions.cjs");
25
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
26
+ const frontmatterMod = require("./frontmatter.cjs");
27
+ const { extractFrontmatter } = frontmatterMod;
25
28
  const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
29
+ const security_cjs_1 = require("./security.cjs");
26
30
  const ui_safety_gate_cjs_1 = require("./ui-safety-gate.cjs");
27
31
  // eslint-disable-next-line @typescript-eslint/no-require-imports
28
32
  const verifyModule = require("./verify.cjs");
@@ -849,9 +853,47 @@ function buildPredicateDeps() {
849
853
  stdout: r.stdout,
850
854
  stderr: r.stderr,
851
855
  signal: r.signal,
852
- timedOut: r.signal === 'SIGTERM',
856
+ timedOut: r.timedOut,
853
857
  };
854
858
  },
859
+ findPhaseArtifact(phaseDir, artifactSuffix) {
860
+ if (!node_fs_1.default.existsSync(phaseDir))
861
+ return null;
862
+ if (artifactSuffix === '.' ||
863
+ artifactSuffix === '..' ||
864
+ artifactSuffix.includes('\0') ||
865
+ node_path_1.default.basename(artifactSuffix) !== artifactSuffix ||
866
+ node_path_1.default.win32.basename(artifactSuffix) !== artifactSuffix) {
867
+ return null;
868
+ }
869
+ const directPath = (0, security_cjs_1.validatePath)(artifactSuffix, phaseDir);
870
+ if (directPath.safe && node_fs_1.default.existsSync(directPath.resolved) && node_fs_1.default.statSync(directPath.resolved).isFile()) {
871
+ return directPath.resolved;
872
+ }
873
+ const planningPath = (0, security_cjs_1.validatePath)(node_path_1.default.join('.planning', artifactSuffix), phaseDir);
874
+ if (planningPath.safe && node_fs_1.default.existsSync(planningPath.resolved) && node_fs_1.default.statSync(planningPath.resolved).isFile()) {
875
+ return planningPath.resolved;
876
+ }
877
+ try {
878
+ const files = node_fs_1.default.readdirSync(phaseDir);
879
+ for (const f of files) {
880
+ if (f.endsWith('-' + artifactSuffix) || f === artifactSuffix) {
881
+ const candidate = (0, security_cjs_1.validatePath)(f, phaseDir);
882
+ if (candidate.safe && node_fs_1.default.statSync(candidate.resolved).isFile())
883
+ return candidate.resolved;
884
+ }
885
+ }
886
+ }
887
+ catch { /* ignore */ }
888
+ return null;
889
+ },
890
+ readFrontmatter(filePath) {
891
+ const content = (0, shell_command_projection_cjs_1.platformReadSync)(filePath);
892
+ if (content === null)
893
+ throw new Error(`predicate artifact disappeared before it could be read: ${filePath}`);
894
+ const parsed = extractFrontmatter(content, filePath);
895
+ return parsed;
896
+ }
855
897
  };
856
898
  }
857
899
  /** Parse `--flag value` pairs from an args array into a map (last write wins). */
@@ -331,6 +331,30 @@ exports.INIT_COMMAND_ALIASES = [
331
331
  "subcommand": "phase-op",
332
332
  "mutation": false
333
333
  },
334
+ {
335
+ "canonical": "init.code-review",
336
+ "aliases": [
337
+ "init code-review"
338
+ ],
339
+ "subcommand": "code-review",
340
+ "mutation": false
341
+ },
342
+ {
343
+ "canonical": "init.review",
344
+ "aliases": [
345
+ "init review"
346
+ ],
347
+ "subcommand": "review",
348
+ "mutation": false
349
+ },
350
+ {
351
+ "canonical": "init.discuss-phase-assumptions",
352
+ "aliases": [
353
+ "init discuss-phase-assumptions"
354
+ ],
355
+ "subcommand": "discuss-phase-assumptions",
356
+ "mutation": false
357
+ },
334
358
  {
335
359
  "canonical": "init.todos",
336
360
  "aliases": [
@@ -371,6 +395,54 @@ exports.INIT_COMMAND_ALIASES = [
371
395
  "subcommand": "manager",
372
396
  "mutation": false
373
397
  },
398
+ {
399
+ "canonical": "init.complete-milestone",
400
+ "aliases": [
401
+ "init complete-milestone"
402
+ ],
403
+ "subcommand": "complete-milestone",
404
+ "mutation": false
405
+ },
406
+ {
407
+ "canonical": "init.autonomous",
408
+ "aliases": [
409
+ "init autonomous"
410
+ ],
411
+ "subcommand": "autonomous",
412
+ "mutation": false
413
+ },
414
+ {
415
+ "canonical": "init.docs-update",
416
+ "aliases": [
417
+ "init docs-update"
418
+ ],
419
+ "subcommand": "docs-update",
420
+ "mutation": false
421
+ },
422
+ {
423
+ "canonical": "init.update",
424
+ "aliases": [
425
+ "init update"
426
+ ],
427
+ "subcommand": "update",
428
+ "mutation": false
429
+ },
430
+ {
431
+ "canonical": "init.transition",
432
+ "aliases": [
433
+ "init transition"
434
+ ],
435
+ "subcommand": "transition",
436
+ "mutation": false
437
+ },
438
+ {
439
+ "canonical": "init.debug",
440
+ "aliases": [
441
+ "init debug"
442
+ ],
443
+ "subcommand": "debug",
444
+ "mutation": false
445
+ },
374
446
  {
375
447
  "canonical": "init.new-workspace",
376
448
  "aliases": [
@@ -762,21 +762,28 @@ function cmdCommit(cwd, message, files, raw, amend, noVerify) {
762
762
  if (branchName) {
763
763
  const currentBranch = (0, shell_command_projection_cjs_1.execGit)(['rev-parse', '--abbrev-ref', 'HEAD'], { cwd });
764
764
  if (currentBranch.exitCode === 0 && currentBranch.stdout.trim() !== branchName) {
765
- // #2539: the #1278 intent is to CREATE the phase/milestone branch
765
+ // #2539/#3079: the #1278 intent is to CREATE the phase/milestone branch
766
766
  // before the FIRST commit on it — not to force-switch an already-
767
767
  // checked-out working branch onto a DIFFERENT existing branch. The
768
- // prior fallback to a bare `git checkout <branch>` silently switched
769
- // the whole working tree onto an existing unrelated branch in the same
770
- // call that then committed (the only trace was a reflog entry). So:
771
- // create-if-absent only. If the resolved branch already exists and the
772
- // tree is on some other branch, do NOT switch — but never silently: log
773
- // the resolution so the operator sees that the phase branch was
774
- // resolved and deliberately not switched to (#2539 AC2: an auto-
775
- // checkout mid-commit must never happen silently).
776
- const create = (0, shell_command_projection_cjs_1.execGit)(['checkout', '-b', branchName], { cwd });
777
- if (create.exitCode !== 0) {
778
- // `git checkout -b` fails (non-zero) when the branch already exists.
779
- // The operator is on the branch they intend to be on; commit there.
768
+ // prior `git checkout -b` both created AND switched (silently moving
769
+ // HEAD), which resurrected merged-and-deleted phase branches (#3079).
770
+ // Now: create-if-absent WITHOUT switching, using `git branch` instead
771
+ // of `git checkout -b`. The commit always lands on the current branch.
772
+ // If the resolved branch already exists, log the resolution so the
773
+ // operator sees that the phase branch was resolved and deliberately
774
+ // not switched to (#2539 AC2: an auto-checkout mid-commit must never
775
+ // happen silently).
776
+ const verify = (0, shell_command_projection_cjs_1.execGit)(['rev-parse', '--verify', `refs/heads/${branchName}`], { cwd });
777
+ if (verify.exitCode !== 0) {
778
+ // Branch does not exist — create it WITHOUT switching.
779
+ const create = (0, shell_command_projection_cjs_1.execGit)(['branch', branchName], { cwd });
780
+ if (create.exitCode !== 0) {
781
+ process.stderr.write(`Warning: could not create ${branchingStrategy} branch "${branchName}" ` +
782
+ `(${create.stderr.trim()}); committing on the current branch "${currentBranch.stdout.trim()}".\n`);
783
+ }
784
+ }
785
+ else {
786
+ // Branch already exists — do NOT switch, commit on current branch.
780
787
  process.stderr.write(`Warning: resolved ${branchingStrategy} branch "${branchName}" already exists; ` +
781
788
  `committing on the current branch "${currentBranch.stdout.trim()}" instead of switching.\n`);
782
789
  }
@@ -817,11 +824,10 @@ function cmdCommit(cwd, message, files, raw, amend, noVerify) {
817
824
  // exit is a real I/O failure, not a missing file.
818
825
  const rmResult = (0, shell_command_projection_cjs_1.execGit)(['rm', '--cached', '--ignore-unmatch', file], { cwd });
819
826
  if (rmResult.exitCode !== 0) {
820
- const rmErr = rmResult.error;
821
827
  stagingFailures.push({
822
828
  file,
823
829
  error: rmResult.stderr || rmResult.stdout,
824
- timed_out: rmResult.signal === 'SIGTERM' && rmErr?.code === 'ETIMEDOUT',
830
+ timed_out: (0, shell_command_projection_cjs_1.isSpawnTimeout)(rmResult),
825
831
  });
826
832
  }
827
833
  }
@@ -834,17 +840,13 @@ function cmdCommit(cwd, message, files, raw, amend, noVerify) {
834
840
  stagedPaths.push(file);
835
841
  }
836
842
  else {
837
- // `SpawnResultOutput.error` is typed `Error | null`; widen to the errno
838
- // shape by ANNOTATION rather than assertion — `Error` is assignable to
839
- // `NodeJS.ErrnoException` (its extra fields are optional), so an `as`
840
- // cast here trips no-unnecessary-type-assertion.
841
- const addErr = addResult.error;
842
843
  stagingFailures.push({
843
844
  file,
844
845
  error: addResult.stderr || addResult.stdout,
845
- // The projection exposes a timeout distinctly (#2608 AC5); this is the
846
- // same SIGTERM+ETIMEDOUT idiom worktree-safety.cts uses.
847
- timed_out: addResult.signal === 'SIGTERM' && addErr?.code === 'ETIMEDOUT',
846
+ // The projection exposes a timeout distinctly (#2608 AC5); this uses
847
+ // the shared isSpawnTimeout predicate (shell-command-projection.cts)
848
+ // also used by worktree-safety.cts and worktree-base-ref.cts (#3050).
849
+ timed_out: (0, shell_command_projection_cjs_1.isSpawnTimeout)(addResult),
848
850
  });
849
851
  }
850
852
  }
@@ -1015,11 +1017,10 @@ function cmdCommitToSubrepo(cwd, message, files, raw) {
1015
1017
  stagedRelPaths.push(relativePath);
1016
1018
  }
1017
1019
  else {
1018
- const addErr = addResult.error;
1019
1020
  subStagingFailures.push({
1020
1021
  file,
1021
1022
  error: addResult.stderr || addResult.stdout,
1022
- timed_out: addResult.signal === 'SIGTERM' && addErr?.code === 'ETIMEDOUT',
1023
+ timed_out: (0, shell_command_projection_cjs_1.isSpawnTimeout)(addResult),
1023
1024
  });
1024
1025
  }
1025
1026
  }
@@ -0,0 +1,136 @@
1
+ 'use strict';
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.COMMONJS_MARKER_CONTENT = exports.COMMONJS_MARKER = void 0;
7
+ exports.markerPathFor = markerPathFor;
8
+ exports.classifyMarker = classifyMarker;
9
+ exports.ensureCommonJsMarker = ensureCommonJsMarker;
10
+ exports.removeCommonJsMarker = removeCommonJsMarker;
11
+ /**
12
+ * CommonJS module-type marker — single source of truth (#2544).
13
+ *
14
+ * GSD stages its own hook scripts and native plugin adapters as `.js` files.
15
+ * Node resolves a `.js` file's module type by walking up for the nearest
16
+ * `package.json`, so an ambient `"type": "module"` above the install location
17
+ * makes every one of those scripts fail with `require is not defined`. GSD
18
+ * pins them to CommonJS by writing a minimal `{"type":"commonjs"}` marker.
19
+ *
20
+ * Two rules govern that marker, and this module exists so both are enforced in
21
+ * exactly one place:
22
+ *
23
+ * 1. **Write only where GSD owns the contents.** The marker belongs in the
24
+ * directories GSD fills with its own `.js` files (`hooks/`, and the
25
+ * `nativePlugin.dir` for the runtimes that declare one) — never at the
26
+ * runtime's shared config root, which on OpenCode and Kilo is documented,
27
+ * user-writable territory for declaring local-plugin npm dependencies.
28
+ *
29
+ * 2. **Never overwrite a file GSD did not write.** Before #2544 the install
30
+ * path wrote the marker unconditionally while the uninstall path already
31
+ * compared content before unlinking. That asymmetry is the defect: the
32
+ * discipline existed in the codebase, it was simply not applied on the
33
+ * write side. `classifyMarker` is now the shared predicate behind both
34
+ * `ensureCommonJsMarker` and `removeCommonJsMarker`, so install and
35
+ * uninstall cannot drift apart again.
36
+ *
37
+ * Ownership is decided by exact content match against the marker GSD itself
38
+ * writes — the same test the uninstall path has always used.
39
+ */
40
+ const node_fs_1 = __importDefault(require("node:fs"));
41
+ const node_path_1 = __importDefault(require("node:path"));
42
+ /** The exact marker content GSD writes (and the only content it will remove). */
43
+ exports.COMMONJS_MARKER = '{"type":"commonjs"}';
44
+ /** File bytes written to disk — the marker plus a trailing newline. */
45
+ exports.COMMONJS_MARKER_CONTENT = `${exports.COMMONJS_MARKER}\n`;
46
+ /** The marker path for a directory. */
47
+ function markerPathFor(dir) {
48
+ return node_path_1.default.join(dir, 'package.json');
49
+ }
50
+ /**
51
+ * Classify the package.json in `dir` by ownership.
52
+ *
53
+ * Fails CLOSED: a file that exists but cannot be read is reported `foreign`,
54
+ * never `absent`. Reporting it absent would license the overwrite this module
55
+ * exists to prevent. (Same posture as the unreadable-config branch in
56
+ * capability-command-router.cjs: present-but-unreadable never downgrades to
57
+ * the permissive answer.)
58
+ */
59
+ function classifyMarker(dir) {
60
+ const markerPath = markerPathFor(dir);
61
+ let stat;
62
+ try {
63
+ // lstat, not existsSync: existsSync follows symlinks and reports `false`
64
+ // for a DANGLING one, which would classify the path `absent` and let the
65
+ // write below follow the link and land outside the directory GSD owns.
66
+ stat = node_fs_1.default.lstatSync(markerPath);
67
+ }
68
+ catch (err) {
69
+ if (err.code === 'ENOENT')
70
+ return 'absent';
71
+ return 'foreign';
72
+ }
73
+ // Anything that is not a regular file (symlink, directory, socket) is not
74
+ // something GSD wrote, so it is never ours to overwrite or remove.
75
+ if (!stat.isFile())
76
+ return 'foreign';
77
+ try {
78
+ const content = node_fs_1.default.readFileSync(markerPath, 'utf8');
79
+ return content.trim() === exports.COMMONJS_MARKER ? 'gsd-owned' : 'foreign';
80
+ }
81
+ catch {
82
+ return 'foreign';
83
+ }
84
+ }
85
+ /**
86
+ * Write the CommonJS marker into `dir`, unless a file GSD does not own is
87
+ * already there.
88
+ *
89
+ * Creates `dir` when needed. Returns what happened so the caller can report
90
+ * it; a `preserved-foreign` result is not an error — it is the guard working.
91
+ *
92
+ * NEVER THROWS. Every other marker interaction in this module is best-effort —
93
+ * `removeCommonJsMarker` swallows unlink failures, `classifyMarker` swallows
94
+ * read failures — and the write path is the one most likely to fail on a
95
+ * locked-down config dir (`EACCES` on a read-only `hooks/`, `EROFS`, `ENOSPC`).
96
+ * Letting it throw made an unwritable marker abort the entire install with a
97
+ * raw stack trace, which is a strictly worse outcome than hooks that resolve as
98
+ * ESM: the caller can warn and continue, and does. Both the `mkdir` and the
99
+ * write are inside the guard — creating the directory is the same environmental
100
+ * hazard as writing into it.
101
+ */
102
+ function ensureCommonJsMarker(dir) {
103
+ const ownership = classifyMarker(dir);
104
+ if (ownership === 'foreign')
105
+ return 'preserved-foreign';
106
+ if (ownership === 'gsd-owned')
107
+ return 'unchanged';
108
+ try {
109
+ node_fs_1.default.mkdirSync(dir, { recursive: true });
110
+ // Exclusive create closes the gap between classifying and writing: if
111
+ // anything at all appeared at the path in between — including a symlink —
112
+ // this fails with EEXIST instead of following or overwriting it.
113
+ node_fs_1.default.writeFileSync(markerPathFor(dir), exports.COMMONJS_MARKER_CONTENT, { flag: 'wx' });
114
+ return 'written';
115
+ }
116
+ catch (err) {
117
+ if (err.code === 'EEXIST')
118
+ return 'preserved-foreign';
119
+ return 'failed';
120
+ }
121
+ }
122
+ /**
123
+ * Remove the CommonJS marker from `dir` — only when the content is exactly
124
+ * the marker GSD writes. Returns true when a file was removed.
125
+ */
126
+ function removeCommonJsMarker(dir) {
127
+ if (classifyMarker(dir) !== 'gsd-owned')
128
+ return false;
129
+ try {
130
+ node_fs_1.default.unlinkSync(markerPathFor(dir));
131
+ return true;
132
+ }
133
+ catch {
134
+ return false;
135
+ }
136
+ }
@@ -755,6 +755,7 @@ function loadConfigResolved(cwd, options = {}) {
755
755
  response_language: get('response_language') || null,
756
756
  claude_md_path: get('claude_md_path') || null,
757
757
  claude_md_assembly: (parsed['claude_md_assembly']) || null,
758
+ phase_id_convention: get('phase_id_convention') ?? null,
758
759
  };
759
760
  // ADR-857 phase 3b: federated config overlay
760
761
  try {
@@ -65,6 +65,8 @@ const SCHEMA_DEFAULTS = {
65
65
  'context_window': 200000,
66
66
  'executor.stall_detect_interval_minutes': 5,
67
67
  'executor.stall_threshold_minutes': 10,
68
+ 'planner.stall_detect_interval_minutes': 5,
69
+ 'planner.stall_threshold_minutes': 10,
68
70
  'git.create_tag': true,
69
71
  // Derived from the defaults manifest rather than restated, so the manifest
70
72
  // stays the single source of truth for the smart-zone budget (#2630).
@@ -289,10 +291,19 @@ function buildNewProjectConfig(userChoices) {
289
291
  const ud = userDefaults;
290
292
  const ch = choices;
291
293
  const hd = hardcoded;
294
+ // #2840: `runtime` is host-specific (written by the installer for whichever
295
+ // runtime's install ran last). On a machine with 2+ runtimes, it poisons every
296
+ // new project config — e.g. a Codex install's `runtime:"codex"` leaks into
297
+ // Claude Code projects, resolving agents to wrong model IDs. `resolve_model_ids`
298
+ // already has a per-install guard (#2297); `runtime` gets the same treatment
299
+ // by excluding it from the defaults spread. Projects detect the runtime from
300
+ // the install path / .gsd-runtime marker, not from a copied config key.
301
+ const safeDefaults = { ...userDefaults };
302
+ delete safeDefaults['runtime'];
292
303
  // Three-level deep merge: hardcoded <- userDefaults <- choices
293
304
  const config = {
294
305
  ...hardcoded,
295
- ...userDefaults,
306
+ ...safeDefaults,
296
307
  ...choices,
297
308
  git: {
298
309
  ...hd['git'],