@opengsd/gsd-core 1.8.0 → 1.9.1

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 (177) 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 +31 -1
  4. package/agents/gsd-code-fixer.md +107 -34
  5. package/agents/gsd-codebase-mapper.md +1 -1
  6. package/agents/gsd-debug-session-manager.md +36 -0
  7. package/agents/gsd-executor.md +20 -7
  8. package/agents/gsd-intel-updater.md +3 -3
  9. package/agents/gsd-phase-researcher.md +4 -2
  10. package/agents/gsd-plan-checker.md +20 -0
  11. package/agents/gsd-planner.md +15 -23
  12. package/agents/gsd-project-researcher.md +2 -2
  13. package/agents/gsd-ui-auditor.md +0 -40
  14. package/bin/install.js +236 -107
  15. package/commands/gsd/plan-review-convergence.md +5 -1
  16. package/gsd-core/bin/gsd-tools.cjs +882 -4
  17. package/gsd-core/bin/lib/api-coverage.cjs +22 -8
  18. package/gsd-core/bin/lib/audit.cjs +8 -8
  19. package/gsd-core/bin/lib/capability-consent.cjs +40 -1
  20. package/gsd-core/bin/lib/capability-lifecycle.cjs +58 -0
  21. package/gsd-core/bin/lib/capability-loader.cjs +23 -1
  22. package/gsd-core/bin/lib/capability-registry.cjs +1353 -132
  23. package/gsd-core/bin/lib/capability-trust.cjs +468 -33
  24. package/gsd-core/bin/lib/capability-validator.cjs +882 -6
  25. package/gsd-core/bin/lib/check-command-router.cjs +12 -2
  26. package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +15 -0
  27. package/gsd-core/bin/lib/claude-orchestration-command-router.cjs +102 -12
  28. package/gsd-core/bin/lib/claude-orchestration.cjs +125 -22
  29. package/gsd-core/bin/lib/commands.cjs +246 -18
  30. package/gsd-core/bin/lib/config-loader.cjs +200 -28
  31. package/gsd-core/bin/lib/config.cjs +90 -5
  32. package/gsd-core/bin/lib/estimate-cli.cjs +336 -0
  33. package/gsd-core/bin/lib/frontmatter.cjs +125 -15
  34. package/gsd-core/bin/lib/host-integration.cjs +215 -8
  35. package/gsd-core/bin/lib/init.cjs +44 -19
  36. package/gsd-core/bin/lib/install-engine.cjs +1 -0
  37. package/gsd-core/bin/lib/milestone.cjs +36 -9
  38. package/gsd-core/bin/lib/model-catalog.cjs +51 -1
  39. package/gsd-core/bin/lib/observability/logger.cjs +7 -2
  40. package/gsd-core/bin/lib/phase-command-router.cjs +10 -1
  41. package/gsd-core/bin/lib/phase-estimation.cjs +398 -0
  42. package/gsd-core/bin/lib/phase-id.cjs +278 -5
  43. package/gsd-core/bin/lib/phase.cjs +61 -6
  44. package/gsd-core/bin/lib/plan-drift-guard.cjs +1 -1
  45. package/gsd-core/bin/lib/plan-scan.cjs +1 -1
  46. package/gsd-core/bin/lib/planning-workspace.cjs +9 -2
  47. package/gsd-core/bin/lib/profile-output.cjs +34 -8
  48. package/gsd-core/bin/lib/project-root.cjs +48 -0
  49. package/gsd-core/bin/lib/review-lane-descriptor.cjs +927 -0
  50. package/gsd-core/bin/lib/review-lane-invocation.cjs +348 -0
  51. package/gsd-core/bin/lib/review-lane-runner.cjs +594 -0
  52. package/gsd-core/bin/lib/review-reviewer-selection.cjs +114 -32
  53. package/gsd-core/bin/lib/roadmap-parser.cjs +54 -6
  54. package/gsd-core/bin/lib/roadmap.cjs +10 -4
  55. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +31 -4
  56. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +1 -1
  57. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +140 -0
  58. package/gsd-core/bin/lib/runtime-name-policy.cjs +15 -2
  59. package/gsd-core/bin/lib/smart-entry.cjs +1 -1
  60. package/gsd-core/bin/lib/state-document.cjs +164 -20
  61. package/gsd-core/bin/lib/state-transition.cjs +28 -10
  62. package/gsd-core/bin/lib/state.cjs +141 -21
  63. package/gsd-core/bin/lib/uat-predicate.cjs +6 -4
  64. package/gsd-core/bin/lib/uat.cjs +9 -7
  65. package/gsd-core/bin/lib/ui-consideration-probe.cjs +2 -2
  66. package/gsd-core/bin/lib/unusable-input.cjs +216 -0
  67. package/gsd-core/bin/lib/validate.cjs +32 -0
  68. package/gsd-core/bin/lib/verification.cjs +51 -14
  69. package/gsd-core/bin/lib/verify.cjs +146 -22
  70. package/gsd-core/bin/lib/worktree-safety.cjs +360 -15
  71. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  72. package/gsd-core/bin/shared/config-schema.manifest.json +1 -13
  73. package/gsd-core/bin/shared/model-catalog.json +5 -0
  74. package/gsd-core/bin/shared/runtime-aliases.manifest.json +5 -0
  75. package/gsd-core/references/context-budget.md +40 -0
  76. package/gsd-core/references/gate-prompts.md +6 -3
  77. package/gsd-core/references/model-profile-resolution.md +64 -13
  78. package/gsd-core/references/offer-next.md +88 -0
  79. package/gsd-core/references/planning-config.md +2 -1
  80. package/gsd-core/references/reviewer-instances.md +28 -21
  81. package/gsd-core/references/runtime-aware-dispatch.md +42 -0
  82. package/gsd-core/references/ui-consideration-probe.md +2 -2
  83. package/gsd-core/references/worktree-branch-check.md +4 -4
  84. package/gsd-core/templates/summary-minimal.md +4 -0
  85. package/gsd-core/templates/summary-standard.md +4 -0
  86. package/gsd-core/templates/summary.md +7 -0
  87. package/gsd-core/workflows/ai-integration-phase.md +4 -4
  88. package/gsd-core/workflows/audit-fix.md +4 -0
  89. package/gsd-core/workflows/audit-milestone.md +8 -0
  90. package/gsd-core/workflows/autonomous.md +19 -15
  91. package/gsd-core/workflows/check-todos.md +2 -2
  92. package/gsd-core/workflows/code-review-fix.md +14 -6
  93. package/gsd-core/workflows/code-review.md +93 -21
  94. package/gsd-core/workflows/debug.md +10 -2
  95. package/gsd-core/workflows/diagnose-issues.md +4 -0
  96. package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -4
  97. package/gsd-core/workflows/discuss-phase/modes/auto.md +0 -6
  98. package/gsd-core/workflows/discuss-phase-assumptions.md +15 -9
  99. package/gsd-core/workflows/discuss-phase.md +2 -2
  100. package/gsd-core/workflows/docs-update.md +8 -0
  101. package/gsd-core/workflows/eval-review.md +1 -1
  102. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +4 -0
  103. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +160 -0
  104. package/gsd-core/workflows/execute-phase.md +85 -115
  105. package/gsd-core/workflows/execute-plan.md +5 -4
  106. package/gsd-core/workflows/explore.md +4 -0
  107. package/gsd-core/workflows/extract-learnings.md +21 -0
  108. package/gsd-core/workflows/help/modes/full.md +3 -3
  109. package/gsd-core/workflows/import.md +4 -1
  110. package/gsd-core/workflows/ingest-docs.md +4 -0
  111. package/gsd-core/workflows/map-codebase.md +13 -6
  112. package/gsd-core/workflows/new-milestone.md +10 -2
  113. package/gsd-core/workflows/new-project.md +11 -4
  114. package/gsd-core/workflows/next.md +5 -2
  115. package/gsd-core/workflows/plan-phase.md +42 -46
  116. package/gsd-core/workflows/plan-review-convergence.md +18 -14
  117. package/gsd-core/workflows/progress.md +1 -1
  118. package/gsd-core/workflows/quick.md +14 -3
  119. package/gsd-core/workflows/review.md +146 -575
  120. package/gsd-core/workflows/scan.md +9 -1
  121. package/gsd-core/workflows/secure-phase.md +10 -2
  122. package/gsd-core/workflows/ship.md +41 -11
  123. package/gsd-core/workflows/smart-entry.md +1 -1
  124. package/gsd-core/workflows/ui-phase.md +8 -1
  125. package/gsd-core/workflows/ui-review.md +8 -1
  126. package/gsd-core/workflows/update.md +104 -5
  127. package/gsd-core/workflows/validate-phase.md +10 -2
  128. package/gsd-core/workflows/verify-work.md +8 -1
  129. package/hooks/dist/gsd-cursor-session-start.js +6 -2
  130. package/hooks/dist/gsd-cursor-stop.js +6 -2
  131. package/hooks/dist/gsd-cursor-subagent-start.js +6 -2
  132. package/hooks/dist/gsd-graphify-update.sh +9 -0
  133. package/hooks/dist/gsd-phase-boundary.sh +14 -2
  134. package/hooks/dist/gsd-prompt-guard.js +101 -2
  135. package/hooks/dist/gsd-read-guard.js +100 -2
  136. package/hooks/dist/gsd-read-injection-scanner.js +109 -2
  137. package/hooks/dist/gsd-statusline.js +9 -6
  138. package/hooks/dist/gsd-workflow-guard.js +110 -6
  139. package/hooks/dist/gsd-worktree-path-guard.js +132 -8
  140. package/hooks/dist/lib/cursor-workspace.js +74 -0
  141. package/hooks/gsd-cursor-session-start.js +6 -2
  142. package/hooks/gsd-cursor-stop.js +6 -2
  143. package/hooks/gsd-cursor-subagent-start.js +6 -2
  144. package/hooks/gsd-graphify-update.sh +9 -0
  145. package/hooks/gsd-phase-boundary.sh +14 -2
  146. package/hooks/gsd-prompt-guard.js +101 -2
  147. package/hooks/gsd-read-guard.js +100 -2
  148. package/hooks/gsd-read-injection-scanner.js +109 -2
  149. package/hooks/gsd-statusline.js +9 -6
  150. package/hooks/gsd-workflow-guard.js +110 -6
  151. package/hooks/gsd-worktree-path-guard.js +132 -8
  152. package/hooks/lib/cursor-workspace.js +74 -0
  153. package/package.json +7 -7
  154. package/pi/gsd.cjs +26 -1
  155. package/scripts/check-coverage-gate.cjs +51 -0
  156. package/scripts/check-glossary-refs.cjs +24 -0
  157. package/scripts/ci-test-scope.cjs +67 -17
  158. package/scripts/gen-adr-index.cjs +6 -4
  159. package/scripts/gen-capability-matrix.cjs +26 -2
  160. package/scripts/gen-capability-registry.cjs +132 -34
  161. package/scripts/gen-emitted-baseline.cjs +145 -0
  162. package/scripts/gen-registry.cjs +39 -15
  163. package/scripts/lint-compiled-artifact-sync.cjs +146 -0
  164. package/scripts/lint-emitted-drift-ack.cjs +149 -0
  165. package/scripts/lint-fix-has-regression-test.cjs +131 -0
  166. package/scripts/lint-resolution-provenance.cjs +9 -0
  167. package/scripts/mutation-matrix.cjs +4 -0
  168. package/scripts/prompt-injection-scan.sh +6 -0
  169. package/scripts/registry-schema.cjs +372 -94
  170. package/scripts/release-notes/conventional-title.cjs +19 -1
  171. package/scripts/release-notes/format-github-release-notes.cjs +7 -3
  172. package/scripts/validate-registry.cjs +10 -6
  173. package/scripts/workflow-size.cjs +16 -8
  174. package/skills/gsd-plan-review-convergence/SKILL.md +5 -1
  175. package/vscode/package.json +1 -1
  176. package/scripts/gen-golden-install-parity-zcode.cjs +0 -77
  177. package/scripts/update-size-baseline.cjs +0 -68
@@ -9,7 +9,7 @@
9
9
  {
10
10
  "name": "gsd-core",
11
11
  "description": "GSD Core is a meta-prompting, context engineering, and spec-driven development system for AI coding agents.",
12
- "version": "1.8.0",
12
+ "version": "1.9.1",
13
13
  "source": "./",
14
14
  "author": {
15
15
  "name": "open-gsd",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gsd-core",
3
3
  "displayName": "GSD Core",
4
- "version": "1.8.0",
4
+ "version": "1.9.1",
5
5
  "description": "GSD Core is a meta-prompting, context engineering, and spec-driven development system for AI coding agents.",
6
6
  "author": {
7
7
  "name": "open-gsd",
@@ -239,6 +239,32 @@ function runHook(hookFile, payload, opts = {}) {
239
239
  return { stdout, exitCode, timedOut: result.signal === "SIGTERM" };
240
240
  }
241
241
 
242
+ /**
243
+ * In-process check for whether context-usage warnings are disabled in project
244
+ * config. Mirrors the exact semantics of the same check inside
245
+ * hooks/gsd-context-monitor.js (introduced by #1073): an explicit
246
+ * `config.hooks.context_warnings === false` disables them; a missing or
247
+ * unparseable .planning/config.json keeps them enabled (the default).
248
+ *
249
+ * #2697: hoisting this check in-process lets the adapter SKIP the context-monitor
250
+ * spawn entirely when the user has opted out, instead of paying a full Node boot
251
+ * inside the child only to read the boolean and exit. Missing/unparseable config
252
+ * MUST behave identically to the hook (enabled) so the default path is unchanged.
253
+ *
254
+ * @param {string} cwd project working directory (the plugin's currentCwd)
255
+ * @returns {boolean} true when context warnings are explicitly disabled
256
+ */
257
+ function contextWarningsDisabled(cwd) {
258
+ try {
259
+ const configPath = path.join(cwd, '.planning', 'config.json');
260
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
261
+ return config.hooks?.context_warnings === false;
262
+ } catch {
263
+ // Missing or unparseable config → proceed with defaults (context warnings enabled).
264
+ return false;
265
+ }
266
+ }
267
+
242
268
  // ---------------------------------------------------------------------------
243
269
  // Hook output translation → OpenCode semantics
244
270
  // ---------------------------------------------------------------------------
@@ -587,7 +613,11 @@ const GsdCorePlugin = async ({ directory } = {}) => {
587
613
 
588
614
  // gsd-context-monitor.js — context usage warnings (Bash/Edit/Write/Task/...)
589
615
  // Only meaningful when a session_id is tracked (writes metrics sentinel).
590
- if (currentSessionId) {
616
+ // #2697: skip the subprocess spawn entirely when context warnings are
617
+ // explicitly disabled in project config — the hook would exit early anyway,
618
+ // so hoisting the check in-process avoids paying a Node boot per tool call.
619
+ // Missing/unparseable config = enabled (default), so the spawn still runs.
620
+ if (currentSessionId && !contextWarningsDisabled(cwd)) {
591
621
  const payload = {
592
622
  hook_event_name: "PostToolUse",
593
623
  tool_name: claudeTool,
@@ -216,9 +216,36 @@ If a finding references multiple files (in Fix section or Issue section):
216
216
 
217
217
  This agent runs as a background process that makes commits. Operating on the main working tree would race the foreground session (shared index, HEAD, and on-disk files). Instead, every instance runs in its own isolated worktree.
218
218
 
219
+ **#2825: honor `workflow.use_worktrees`.** This is the ONLY writer that hand-rolls a git worktree
220
+ inside the agent prompt; every other writer path (`/gsd:execute-phase`, `/gsd:execute-plan`,
221
+ `/gsd:quick`, `/gsd:diagnose-issues`) reads `workflow.use_worktrees` and skips isolation when it is
222
+ `false`. Read the same flag here and, when it is `false`, edit and commit in the main checkout
223
+ directly (set `wt="."`, no `reviewfix_branch`, no recovery sentinel, no `git worktree add`, and skip
224
+ the cleanup tail — there is no worktree to remove). When the flag is not `false`, the transactional
225
+ worktree path below runs unchanged. A user who explicitly opted out of worktrees must never have a
226
+ worktree created; the hand-rolled worktree also cannot run the project's gates safely (no
227
+ `node_modules`), so the opt-out is also the safe path.
228
+
219
229
  The cleanup tail (commit fixes -> remove worktree -> drop recovery sentinel) MUST be **transactional**: either all of (worktree, branch advance, sentinel) end in a clean state, or — if the process is interrupted (system restart, OOM kill) between the last commit and `git worktree remove` — a discoverable recovery sentinel is left behind so a future run, `/gsd:resume-work`, or `/gsd:progress` can complete the cleanup. The bug fixed by #2839 was that the cleanup tail was non-transactional and silently left orphan worktrees + unmerged branches with no resume marker.
220
230
 
221
231
  ```bash
232
+ # #2825: honor workflow.use_worktrees — the documented opt-out. When false,
233
+ # edit/commit in the main checkout (wt=".", no temp branch, no sentinel, no
234
+ # cleanup tail). Read the flag the same way the four sibling writer workflows
235
+ # do. NOTE: this read parses .planning/config.json directly via `node` rather
236
+ # than the gsd-tools CLI, because setup_worktree runs BEFORE the canonical
237
+ # launcher preamble is sourced — invoking the CLI here would be undefined at
238
+ # runtime and violates the runtime-launcher-parity preamble-ordering rule.
239
+ # Once the preamble is sourced (later steps), the CLI is available.
240
+ USE_WORKTREES=$(node -e '
241
+ try {
242
+ const fs = require("fs");
243
+ const p = (process.env.GSD_PROJECT_DIR || process.cwd()) + "/.planning/config.json";
244
+ const cfg = JSON.parse(fs.readFileSync(p, "utf8"));
245
+ process.stdout.write(String((cfg.workflow && cfg.workflow.use_worktrees) ?? true));
246
+ } catch { process.stdout.write("true"); }
247
+ ')
248
+
222
249
  # Derive worktree path from padded_phase (parsed from config in next step,
223
250
  # but the shell snippet below is illustrative — adapt once config is parsed).
224
251
  # In practice: parse padded_phase from config first, then run:
@@ -264,34 +291,47 @@ if [ -f "$sentinel" ]; then
264
291
  rm -f "$sentinel"
265
292
  fi
266
293
 
267
- wt=$(mktemp -d "/tmp/sv-${padded_phase}-reviewfix-XXXXXX")
268
-
269
- # Create a temp branch from the current branch tip so the worktree
270
- # attaches to that NEW branch rather than the user's currently-checked-out
271
- # branch (#2990: git refuses to check out the same branch in two
272
- # worktrees by default; the original `git worktree add "$wt" "$branch"`
273
- # failed before the agent could do any work). The temp branch shares
274
- # history with $branch up to the moment of creation, so commits made
275
- # inside the worktree fast-forward $branch on cleanup.
276
- reviewfix_branch="gsd-reviewfix/${padded_phase}-$$"
277
- git worktree add -b "$reviewfix_branch" "$wt" "$branch"
278
-
279
- # Write the recovery sentinel ONLY AFTER `git worktree add` succeeds.
280
- # Writing it before would leave a sentinel pointing at a worktree that does
281
- # not exist if `git worktree add` itself failed.
282
- node -e '
283
- const fs = require("fs");
284
- const [sentinelPath, worktree_path, branch, reviewfix_branch, padded_phase] = process.argv.slice(1);
285
- fs.writeFileSync(sentinelPath, JSON.stringify({
286
- worktree_path,
287
- branch,
288
- reviewfix_branch,
289
- padded_phase,
290
- started_at: new Date().toISOString()
291
- }, null, 2));
292
- ' "$sentinel" "$wt" "$branch" "$reviewfix_branch" "$padded_phase"
293
-
294
- cd "$wt"
294
+ # #2825: when the user opted out of worktrees, edit/commit in the main
295
+ # checkout directly — no temp branch, no sentinel, no cleanup tail. This is
296
+ # the safe path: the hand-rolled worktree has no node_modules, so it cannot
297
+ # run the project's gates, and an improvised teardown can destroy the real
298
+ # node_modules on Windows (a junction followed by rm -rf). wt="." means every
299
+ # downstream read/edit/commit lands in the main working tree, and the cleanup
300
+ # tail below is a no-op (nothing to fast-forward, no worktree to remove).
301
+ if [ "$USE_WORKTREES" = "false" ]; then
302
+ wt="."
303
+ reviewfix_branch="$branch"
304
+ echo "workflow.use_worktrees=false — editing/committing in the main checkout (no worktree)."
305
+ else
306
+ wt=$(mktemp -d "/tmp/sv-${padded_phase}-reviewfix-XXXXXX")
307
+
308
+ # Create a temp branch from the current branch tip so the worktree
309
+ # attaches to that NEW branch rather than the user's currently-checked-out
310
+ # branch (#2990: git refuses to check out the same branch in two
311
+ # worktrees by default; the original `git worktree add "$wt" "$branch"`
312
+ # failed before the agent could do any work). The temp branch shares
313
+ # history with $branch up to the moment of creation, so commits made
314
+ # inside the worktree fast-forward $branch on cleanup.
315
+ reviewfix_branch="gsd-reviewfix/${padded_phase}-$$"
316
+ git worktree add -b "$reviewfix_branch" "$wt" "$branch"
317
+
318
+ # Write the recovery sentinel ONLY AFTER `git worktree add` succeeds.
319
+ # Writing it before would leave a sentinel pointing at a worktree that does
320
+ # not exist if `git worktree add` itself failed.
321
+ node -e '
322
+ const fs = require("fs");
323
+ const [sentinelPath, worktree_path, branch, reviewfix_branch, padded_phase] = process.argv.slice(1);
324
+ fs.writeFileSync(sentinelPath, JSON.stringify({
325
+ worktree_path,
326
+ branch,
327
+ reviewfix_branch,
328
+ padded_phase,
329
+ started_at: new Date().toISOString()
330
+ }, null, 2));
331
+ ' "$sentinel" "$wt" "$branch" "$reviewfix_branch" "$padded_phase"
332
+
333
+ cd "$wt"
334
+ fi
295
335
  ```
296
336
 
297
337
  Concrete steps:
@@ -305,9 +345,18 @@ Concrete steps:
305
345
 
306
346
  **If `git worktree add` fails**, surface the error and exit — do not force-remove the path, as another concurrent run may be holding it. Do not write the sentinel (the worktree does not exist). Do not delete `$reviewfix_branch` either; if `-b` failed, no temp branch was created.
307
347
 
308
- **Cleanup tail (transactional, ALWAYS — even on failure):** After writing REVIEW-FIX.md and before returning to the orchestrator, run the cleanup in this exact order:
348
+ **Cleanup tail (transactional, ALWAYS — even on failure — when a worktree was created):** After writing REVIEW-FIX.md and before returning to the orchestrator, run the cleanup in this exact order. (When `workflow.use_worktrees` is `false`, no worktree was created — the cleanup is a no-op and the bash below early-exits.)
309
349
 
310
350
  ```bash
351
+ # #2825: when worktrees were disabled, there is nothing to clean up — the
352
+ # agent edited/committed on $branch directly in the main checkout (wt=".",
353
+ # reviewfix_branch==$branch, no sentinel, no temp worktree). Skip the whole
354
+ # tail; the four steps below are all no-ops or harmful (e.g. `git worktree
355
+ # remove "."` ) in that mode.
356
+ if [ "$USE_WORKTREES" = "false" ]; then
357
+ exit 0
358
+ fi
359
+
311
360
  # Step 1 (#2990): fast-forward $branch to capture the commits the agent
312
361
  # made on $reviewfix_branch. Run from the main repo (not $wt) — the user's
313
362
  # checkout owns $branch. --ff-only ensures we never silently drop or
@@ -354,7 +403,7 @@ fi
354
403
  rm -f "$sentinel"
355
404
  ```
356
405
 
357
- This cleanup is unconditional — register it mentally as a finally-block obligation. If the agent exits early (config error, no findings, etc.), still run the cleanup tail in order (fast-forward → worktree remove → temp branch delete → sentinel rm) before exit. The sentinel must NEVER be removed before `git worktree remove` succeeds. The temp branch must NEVER be deleted while the fast-forward is in a diverged state.
406
+ This cleanup is unconditional when a worktree was created — register it mentally as a finally-block obligation. If the agent exits early (config error, no findings, etc.), still run the cleanup tail in order (fast-forward → worktree remove → temp branch delete → sentinel rm) before exit. (When `workflow.use_worktrees` is `false`, no worktree exists and the bash above early-exits before these steps.) The sentinel must NEVER be removed before `git worktree remove` succeeds. The temp branch must NEVER be deleted while the fast-forward is in a diverged state.
358
407
  </step>
359
408
 
360
409
  <step name="load_context">
@@ -456,7 +505,7 @@ For each finding in sorted order:
456
505
 
457
506
  **If verification passed:**
458
507
 
459
- Use `gsd-tools query commit` with conventional format (message first, then every staged file path):
508
+ Use `gsd_run query commit` with conventional format (message first, then every staged file path):
460
509
  ```bash
461
510
  _GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
462
511
  gsd_run query commit \
@@ -587,9 +636,33 @@ _Iteration: {N}_
587
636
 
588
637
  <critical_rules>
589
638
 
590
- **ALWAYS run inside the isolated worktree** — set up via `branch=$(git branch --show-current)` + `wt=$(mktemp -d "/tmp/sv-${padded_phase}-reviewfix-XXXXXX")` + `git worktree add -b "$reviewfix_branch" "$wt" "$branch"` at the very start (see `setup_worktree` step). Using `mktemp` ensures concurrent runs do not collide. Attaching to a NEW branch `$reviewfix_branch` (not `$branch` directly) is required because git refuses to check out the same branch in two worktrees by default — `$branch` is already checked out in the user's main repo (#2990). Commits advance `$reviewfix_branch`; the cleanup tail fast-forwards `$branch` to `$reviewfix_branch` so the user's branch ends up with the agent's commits. Every file read, edit, and commit must happen inside `$wt`. Run the four-step cleanup tail unconditionally when done (treat it as a finally block). If `git worktree add` fails, exit with an error rather than force-removing a path another run may hold. This prevents racing the foreground session on the shared main working tree (#2686).
591
-
592
- **ALWAYS run the transactional cleanup tail in order** (#2839, #2990): the cleanup is four steps with strict ordering. (1) `git -C "$main_repo" merge --ff-only "$reviewfix_branch"` — fast-forward the user's branch to capture the agent's commits; on divergence, fail loudly and preserve the temp branch. (2) `git worktree remove "$wt" --force`. (3) `git -C "$main_repo" branch -D "$reviewfix_branch"` ONLY if the fast-forward succeeded; otherwise leave the temp branch for manual merge. (4) `rm -f "$sentinel"` (the recovery sentinel at `${phase_dir}/.review-fix-recovery-pending.json`). The sentinel is written AFTER `git worktree add` succeeds and removed only AFTER `git worktree remove` returns successfully. The temp branch is deleted only when the fast-forward succeeded. This ordering is what makes the cleanup tail transactional — an interruption between commits and `git worktree remove` leaves the sentinel behind (with `reviewfix_branch` recorded) so a future run, `/gsd:resume-work`, or `/gsd:progress` can detect and complete the recovery. Reversing the order recreates the orphan-worktree bug.
639
+ **ALWAYS run inside the isolated worktree** — set up via `branch=$(git branch --show-current)` + `wt=$(mktemp -d "/tmp/sv-${padded_phase}-reviewfix-XXXXXX")` + `git worktree add -b "$reviewfix_branch" "$wt" "$branch"` at the very start (see `setup_worktree` step). Using `mktemp` ensures concurrent runs do not collide. Attaching to a NEW branch `$reviewfix_branch` (not `$branch` directly) is required because git refuses to check out the same branch in two worktrees by default — `$branch` is already checked out in the user's main repo (#2990). Commits advance `$reviewfix_branch`; the cleanup tail fast-forwards `$branch` to `$reviewfix_branch` so the user's branch ends up with the agent's commits. Every file read, edit, and commit must happen inside `$wt`. Run the four-step cleanup tail when done (treat it as a finally block) — but only when a worktree was actually created; when `workflow.use_worktrees` is `false` the cleanup early-exits (no worktree to remove). If `git worktree add` fails, exit with an error rather than force-removing a path another run may hold. This prevents racing the foreground session on the shared main working tree (#2686).
640
+
641
+ **#2825 — honor `workflow.use_worktrees`.** Before creating a worktree, read the
642
+ `workflow.use_worktrees` config flag (the documented opt-out — same key the four sibling writer
643
+ workflows honor). `setup_worktree` reads it via `node` directly from `.planning/config.json`
644
+ (because that step runs BEFORE the canonical gsd_run launcher preamble is sourced; later steps may
645
+ use `gsd_run query config-get workflow.use_worktrees`). When it is `false`, do NOT create a worktree
646
+ — edit and commit in the main checkout directly (`wt="."`, no temp branch, no sentinel, no cleanup
647
+ tail). A user who opted out of worktrees must
648
+ never have one created. See the `setup_worktree` step for the gated bash.
649
+
650
+ **NEVER `rm -rf` a possible reparse point** (#2825). On Windows, `node_modules` inside the worktree
651
+ may be a junction/reparse point whose target is the REAL `node_modules` in the main checkout — and
652
+ `rm -rf` follows the link and deletes the target's contents (silent, misdiagnosable data loss). Do
653
+ NOT improvise a `node_modules` teardown. The worktree has no `node_modules` by design; if you need
654
+ the project's gates, run them in the main checkout after the fast-forward, OR leave the worktree's
655
+ dependency handling to `git worktree remove` (which does not recurse into a separately-managed
656
+ link). Never use `rm -rf` (or `2>/dev/null || rm -rf || true`) as a fallback for removing a path
657
+ that might be a reparse point — on failure, STOP and surface the error rather than falling through
658
+ to a destructive remove.
659
+
660
+ **Record where verification ran** (#2825). The REVIEW-FIX.md verification section must state whether
661
+ the gates ran in the main checkout or the isolated worktree, so a reader can tell whether the numbers
662
+ are reproducible from the tree they are looking at (a worktree-env run is not reproducible from the
663
+ main checkout after teardown).
664
+
665
+ **ALWAYS run the transactional cleanup tail in order when a worktree was created** (#2839, #2990; skipped — bash early-exits — when `workflow.use_worktrees` is `false`): the cleanup is four steps with strict ordering. (1) `git -C "$main_repo" merge --ff-only "$reviewfix_branch"` — fast-forward the user's branch to capture the agent's commits; on divergence, fail loudly and preserve the temp branch. (2) `git worktree remove "$wt" --force`. (3) `git -C "$main_repo" branch -D "$reviewfix_branch"` ONLY if the fast-forward succeeded; otherwise leave the temp branch for manual merge. (4) `rm -f "$sentinel"` (the recovery sentinel at `${phase_dir}/.review-fix-recovery-pending.json`). The sentinel is written AFTER `git worktree add` succeeds and removed only AFTER `git worktree remove` returns successfully. The temp branch is deleted only when the fast-forward succeeded. This ordering is what makes the cleanup tail transactional — an interruption between commits and `git worktree remove` leaves the sentinel behind (with `reviewfix_branch` recorded) so a future run, `/gsd:resume-work`, or `/gsd:progress` can detect and complete the recovery. Reversing the order recreates the orphan-worktree bug.
593
666
 
594
667
  **ALWAYS use the Write tool to create files** — never use `Bash(cat << 'EOF')` or heredoc commands for file creation.
595
668
 
@@ -175,7 +175,7 @@ Write document(s) to `.planning/codebase/` using the templates below.
175
175
  **Document naming:** UPPERCASE.md (e.g., STACK.md, ARCHITECTURE.md)
176
176
 
177
177
  **Template filling:**
178
- 1. Replace `[YYYY-MM-DD]` with the date provided in your prompt (the `Today's date:` line). NEVER guess or infer the date — always use the exact date from the prompt.
178
+ 1. Set the `**Analysis Date:**` line, the `*... analysis: ...*` footer, and any `<!-- refreshed: ... -->` header to the date provided in your prompt (the `Today's date:` line), overwriting whatever date is already there. NEVER guess or infer the date — always use the exact date from the prompt.
179
179
  2. Replace `[Placeholder text]` with findings from exploration
180
180
  3. If something is not found, use "Not detected" or "Not applicable"
181
181
  4. Always include file paths with backticks
@@ -310,6 +310,41 @@ If user selects 3: proceed to Step 4 with fix = "not applied (guardrail rejected
310
310
 
311
311
  Read the resolved (or current) debug file to extract final Resolution values.
312
312
 
313
+ **Commit before returning a terminal summary (#2568).** This agent owns the terminal path —
314
+ it applies fixes, archives to `resolved/`, and returns the summary — but carried no commit
315
+ step, so `commit_docs` was never consulted on the normal `/gsd:debug` flow and session docs
316
+ were left untracked. Do this for **both** terminal shapes below, and **NOT** for
317
+ `CONTINUE_REQUIRED` above: that shape is non-terminal, and committing there would strand a
318
+ half-finished session looking done, exactly as fabricating a terminal summary would.
319
+ `CHECKPOINT REACHED` (Step 3d) likewise does not commit — it pauses for user input and loops
320
+ back to Step 3.
321
+
322
+ 1. **In-session fix code.** If a fix was applied during this session and its code changes are
323
+ still uncommitted, commit them first. Stage **specific files only** — the files the fix
324
+ touched. Do this rather than `git add -A`, which would sweep unrelated working-tree
325
+ changes into a debug commit. Guard on staged content: `gsd-debugger.md`'s
326
+ `archive_session` step may already have committed this fix on the confirmed-checkpoint
327
+ path, and a bare `git commit` with nothing staged exits non-zero and would abort this
328
+ step before the summary is returned:
329
+ ```bash
330
+ git add <files the fix touched>
331
+ git diff --cached --quiet || git commit -m "fix: {brief description}"
332
+ ```
333
+ 2. **Session doc.** Commit via the CLI, which already gates on `commit_docs` and returns
334
+ `skipped_commit_docs_false` when disabled — call it unconditionally rather than
335
+ re-checking the config here, so the policy lives in one place. `query commit` treats an
336
+ empty diff as `nothing_to_commit` and exits 0, so a second call after
337
+ `archive_session` already committed the doc is a safe no-op. The canonical `gsd_run` preamble is
338
+ established once in Step 2 and is the single definition this agent carries (repo
339
+ invariant: exactly one preamble per agent file, before its first call):
340
+ ```bash
341
+ # resolved session — path spelled literally; this agent receives `slug` and
342
+ # `debug_file_path`, NOT a `debug_dir` variable (see <session_parameters>).
343
+ gsd_run query commit "docs(debug): resolve {slug} session" --files .planning/debug/resolved/{slug}.md
344
+ # abandoned session (checkpoint retained for `/gsd:debug continue {slug}`)
345
+ gsd_run query commit "docs(debug): checkpoint {slug} session" --files {debug_file_path}
346
+ ```
347
+
313
348
  Return compact summary (terminal — investigation resolved):
314
349
 
315
350
  ```markdown
@@ -349,5 +384,6 @@ If the session was abandoned by user choice, return (terminal — user stopped):
349
384
  - [ ] TDD gate applied when tdd_mode=true and ROOT CAUSE FOUND
350
385
  - [ ] Loop continues until DEBUG COMPLETE, ABANDONED, or user stops
351
386
  - [ ] Non-terminal `CONTINUE_REQUIRED` (not a fabricated terminal summary) returned when the manager's own turn/context budget is exhausted mid-investigation
387
+ - [ ] Session doc (and any uncommitted fix code from this session) committed before a terminal summary, respecting `commit_docs` — and NOT committed on the non-terminal `CONTINUE_REQUIRED` path
352
388
  - [ ] Compact summary returned (at most 2K tokens)
353
389
  </success_criteria>
@@ -495,11 +495,11 @@ if [ -f .git ]; then # worktree
495
495
  echo "DO NOT use 'git update-ref' to rewind the protected branch — surface as blocker (#2924)." >&2
496
496
  exit 1
497
497
  fi
498
- # Positive allow-list: HEAD must be on the canonical Claude Code worktree-agent
499
- # branch namespace (`worktree-agent-<id>`). This catches feature/* and any other
500
- # arbitrary branch that the deny-list would silently allow (#2924).
501
- if ! echo "$ACTUAL_BRANCH" | grep -Eq '^worktree-agent-[A-Za-z0-9._/-]+$'; then
502
- echo "FATAL: refusing to commit — worktree HEAD '$ACTUAL_BRANCH' is not in the worktree-agent-* namespace." >&2
498
+ # Positive allow-list: HEAD must be on a per-agent branch (`agent-<id>` or
499
+ # legacy `worktree-agent-<id>`). This catches feature/* and any other
500
+ # arbitrary branch that the deny-list would silently allow (#2924, #1995).
501
+ if ! echo "$ACTUAL_BRANCH" | grep -Eq '^(worktree-)?agent-[A-Za-z0-9._/-]+$'; then
502
+ echo "FATAL: refusing to commit — worktree HEAD '$ACTUAL_BRANCH' is not in the agent-* / worktree-agent-* namespace." >&2
503
503
  echo "Agent commits must live on per-agent branches; surface as blocker (#2924)." >&2
504
504
  exit 1
505
505
  fi
@@ -638,7 +638,16 @@ This file is the canonical output of this step. The orchestrator reads `.plannin
638
638
 
639
639
  **Use template:** @~/.claude/gsd-core/templates/summary.md
640
640
 
641
- **Frontmatter:** phase, plan, subsystem, tags, dependency graph (requires/provides/affects), tech-stack (added/patterns), key-files (created/modified), decisions, metrics (duration, completed date), status (`status: complete` — required so the audit-open scanner recognises the summary as done).
641
+ **Frontmatter:** phase, plan, subsystem, tags, dependency graph (requires/provides/affects), tech-stack (added/patterns), key-files (created/modified), decisions, metrics (duration, completed date), status (`status: complete` — required so the audit-open scanner recognises the summary as done), and `actuals` (#2632).
642
+
643
+ **`actuals` (required when the plan carried an `estimate`):** record what the phase ACTUALLY cost, on the SAME scale the estimate used — `estimateTokens` (chars/4) over the realized diff, NOT a harness token count. Mixing scales measures the measurement methods, not the miss.
644
+ ```yaml
645
+ actuals:
646
+ tokens: 74000 # chars/4 over the files you actually changed
647
+ tasks: 5 # tasks completed
648
+ commits: 7 # commits made
649
+ ```
650
+ These pair with the plan's `estimate` to calibrate future estimates (ADR-2629). Do not round to look closer to the estimate — a flattering number corrupts every later projection.
642
651
 
643
652
  **Title:** `# Phase [X] Plan [Y]: [Name] Summary`
644
653
 
@@ -780,7 +789,7 @@ gsd_run query commit "docs({phase}-{plan}): complete [plan-name] plan" --files \
780
789
  Separate from per-task commits — captures execution results only.
781
790
 
782
791
  **Handling the SDK return envelope (#3678):** `gsd-tools query commit` returns
783
- one of three shapes:
792
+ one of these shapes:
784
793
 
785
794
  - `{committed: true, hash, reason: 'committed'}` — commit succeeded; record
786
795
  the hash in the completion format.
@@ -793,6 +802,10 @@ one of three shapes:
793
802
  success path.** Record "skipped (.planning gitignored)" and move on.
794
803
  - `{committed: false, reason: 'nothing_to_commit' | 'commit_failed', ...}` —
795
804
  no-op / genuine failure; surface in the completion notes.
805
+ - `{committed: false, reason: 'staging_failed' | 'staging_timeout', file, error}` —
806
+ `git add` itself failed (#2608), e.g. an unwritable index. Nothing committed,
807
+ index rolled back. Surface `file` + `error` (git's stderr); do not retry — a
808
+ retry hits the same cause.
796
809
 
797
810
  **Do not fall back to raw `git add` / `git commit` / `git add -f`** when the
798
811
  SDK returns `skipped: true`. The SDK's skip is the user's deliberate choice
@@ -123,7 +123,7 @@ All JSON files include a `_meta` object with `updated_at` (ISO timestamp) and `v
123
123
  }
124
124
  ```
125
125
 
126
- **exports constraint:** Array of ACTUAL exported symbol names extracted from `module.exports` or `export` statements. MUST be real identifiers (e.g., `"configLoad"`, `"stateUpdate"`), NOT descriptions (e.g., `"config operations"`). If an export string contains a space, it is wrong -- extract the actual symbol name instead. Use `gsd-tools intel extract-exports <file>` to get accurate exports.
126
+ **exports constraint:** Array of ACTUAL exported symbol names extracted from `module.exports` or `export` statements. MUST be real identifiers (e.g., `"configLoad"`, `"stateUpdate"`), NOT descriptions (e.g., `"config operations"`). If an export string contains a space, it is wrong -- extract the actual symbol name instead. Use `gsd_run intel extract-exports <file>` to get accurate exports.
127
127
 
128
128
  Types: `entry-point`, `module`, `config`, `test`, `script`, `type-def`, `style`, `template`, `data`.
129
129
 
@@ -255,7 +255,7 @@ gsd_run intel patch-meta .planning/intel/arch-decisions.json
255
255
 
256
256
  ### Step 6.5: Self-Check
257
257
 
258
- Run: `gsd-tools intel validate`
258
+ Run: `gsd_run intel validate`
259
259
 
260
260
  Review the output:
261
261
 
@@ -267,7 +267,7 @@ This step is MANDATORY -- do not skip it.
267
267
 
268
268
  ### Step 7: Snapshot
269
269
 
270
- Run: `gsd-tools intel snapshot`
270
+ Run: `gsd_run intel snapshot`
271
271
 
272
272
  This writes `.last-refresh.json` with accurate timestamps and hashes. Do NOT write `.last-refresh.json` manually.
273
273
  </execution_flow>
@@ -32,6 +32,8 @@ Spawned by `/gsd:plan-phase` (integrated) or `/gsd:plan-phase --research-phase <
32
32
 
33
33
  **Package name provenance rule:** A package name discovered via WebSearch, training data, or any non-authoritative source must be tagged `[ASSUMED]` regardless of whether `npm view` confirms it exists on the registry. Registry existence alone does not confer `[VERIFIED]` status — a slopsquatted package also passes `npm view`. Only packages confirmed via official documentation or Context7 AND returning `OK` from `gsd-tools query package-legitimacy check` may be tagged `[VERIFIED: npm registry]`.
34
34
 
35
+ **In-repo value provenance rule:** A claim about an in-repo *discrete value* — an enum, a schema or type union, an error code, a status constant, or a filesystem path — may be tagged `[VERIFIED: …]` only if you opened the source-of-truth file with `Read` **this session**. A codebase `grep` is not sufficient on its own: it confirms a string occurs, not that you read the definition. Cite the path **and line range** (`[VERIFIED: src/types/order.ts:14-22]`), and quote the values **verbatim** in RESEARCH.md beside the claim — paraphrase is forbidden. The quote is what makes the tag checkable — a citation with no quote beside it does not earn `[VERIFIED]`, however precise the line range looks. Every value appearing in a code example or skeleton must also appear in that verbatim quote; a value that does not is `[ASSUMED]`. For a filesystem path, cite the line in the script that creates it, not the location you expect it to occupy. Training memory and a web search are not substitutes for reading the file — a discrete value that merely looks right fails at the executor's `parse()`/typecheck, the most expensive place to discover it.
36
+
35
37
  Claims tagged `[ASSUMED]` signal to the planner and discuss-phase that the information needs user confirmation before becoming a locked decision. Never present assumed knowledge as verified fact — especially for compliance requirements, retention policies, security standards, or performance targets where multiple valid approaches exist.
36
38
  </role>
37
39
 
@@ -136,7 +138,7 @@ For each item where `fetch` is present, invoke the MCP tool matching `fetch.prov
136
138
  | `exa` | `mcp__exa__web_search_exa` with `fetch.query` |
137
139
  | `tavily` | `mcp__tavily__search` with `fetch.query` |
138
140
  | `perplexity` | `mcp__perplexity__*` (use the appropriate perplexity MCP tool for the query) |
139
- | `brave` | `gsd-tools query websearch "<fetch.query>"` (Brave-backed) or built-in `WebSearch` |
141
+ | `brave` | `gsd_run query websearch "<fetch.query>"` (Brave-backed) or built-in `WebSearch` |
140
142
  | `firecrawl` | `mcp__firecrawl__scrape` with url (scrape kind) or `mcp__firecrawl__search` |
141
143
  | `websearch` | built-in `WebSearch` tool |
142
144
  | `webfetch` | built-in `WebFetch` tool |
@@ -707,7 +709,7 @@ docker info 2>/dev/null | head -3
707
709
 
708
710
  ## Step 3: Execute Research Protocol
709
711
 
710
- For each domain, use the `<tool_strategy>` seam (Steps A–D): build questions JSON, call `gsd-tools query research-plan`, run the indicated provider per item, then cache each digest. Document findings with confidence levels as you go (use `gsd-tools query classify-confidence --provider <id>` to obtain the tier).
712
+ For each domain, use the `<tool_strategy>` seam (Steps A–D): build questions JSON, call `gsd_run query research-plan`, run the indicated provider per item, then cache each digest. Document findings with confidence levels as you go (use `gsd_run query classify-confidence --provider <id>` to obtain the tier).
711
713
 
712
714
  ## Step 4: Validation Architecture Research (if nyquist_validation enabled)
713
715
 
@@ -252,6 +252,19 @@ issue:
252
252
  1. Count tasks per plan
253
253
  2. Estimate files modified per plan
254
254
  3. Check against thresholds
255
+ 4. **Smart-zone estimate check (#2631, ADR-2629).** For each plan carrying an `estimate` block, run the
256
+ `estimate-check --calibrated` verb against its `estimate.tokens` (the `--calibrated` flag is required —
257
+ the plan's figure already has the factor applied, and omitting it would square the correction) (invoked in Step 1 below, after the launcher
258
+ preamble). The verb reads `workflow.smart_zone_tokens` and applies the project's calibration. Report
259
+ one line per plan: plan id, estimated tokens, the budget, and — when `over_budget` is true — the
260
+ returned `recommendation`, which names how many slices the phase should become.
261
+
262
+ **Over budget is a WARNING, never a blocker** (ADR-2629 Decision 5). Recommend re-slicing into a tracer
263
+ plus expansion slices; never fail the check on it. Report `estimate.confidence` alongside: `low` means
264
+ fewer than 3 completed phases carry actuals, so the figure is not yet calibrated for this project — say
265
+ so rather than presenting it as precise, and weigh the task/file thresholds above more heavily.
266
+
267
+ A plan with no `estimate` block is not a defect; the field is optional and additive.
255
268
 
256
269
  **Thresholds:**
257
270
  | Metric | Target | Warning | Blocker |
@@ -706,6 +719,13 @@ gsd_run query phase.list-plans "$phase_number"
706
719
  gsd_run query phase.list-artifacts "$phase_number" --type research
707
720
  gsd_run query roadmap.get-phase "$phase_number"
708
721
  gsd_run query phase.list-artifacts "$phase_number" --type summary
722
+
723
+ # Smart-zone estimate check (#2631) — advisory, never fails the check.
724
+ for plan in "${phase_dir:-$PHASE_DIR}"/*-PLAN.md; do
725
+ [ -f "$plan" ] || continue # unmatched glob leaves the literal pattern — skip it
726
+ EST=$(sed -n '/^estimate:/,/^[a-z_]*:/p' "$plan" | grep -o 'tokens: *[0-9]*' | head -1 | grep -o '[0-9]*')
727
+ [ -n "$EST" ] && gsd_run query estimate-check --tokens "$EST" --calibrated 2>/dev/null || true
728
+ done
709
729
  ```
710
730
 
711
731
  **Extract:** Phase goal, requirements (decompose goal), locked decisions, deferred ideas.
@@ -288,30 +288,15 @@ See @~/.claude/gsd-core/references/planner-guidance.md for dependency graph buil
288
288
 
289
289
  <scope_estimation>
290
290
 
291
- ## Context Budget Rules
291
+ ## Sizing and the Estimate Block
292
292
 
293
- Plans should complete within ~50% context (not 80%). No context anxiety, quality maintained start to finish, room for unexpected complexity.
293
+ Full rules: @~/.claude/gsd-core/references/context-budget.md (Phase Sizing). Read before sizing.
294
294
 
295
- **Each plan: 2-3 tasks maximum.**
296
-
297
- | Context Weight | Tasks/Plan | Context/Task | Total |
298
- |----------------|------------|--------------|-------|
299
- | Light (CRUD, config) | 3 | ~10-15% | ~30-45% |
300
- | Medium (auth, payments) | 2 | ~20-30% | ~40-50% |
301
- | Heavy (migrations, multi-subsystem) | 1-2 | ~30-40% | ~30-50% |
302
-
303
- ## Split Signals
304
-
305
- **ALWAYS split if:**
306
- - More than 3 tasks
307
- - Multiple subsystems (DB + API + UI = separate plans)
308
- - Any task with >5 file modifications
309
- - Checkpoint + implementation in same plan
310
- - Discovery + implementation in same plan
311
-
312
- **CONSIDER splitting:** >5 files total, natural semantic boundaries, context cost estimate exceeds 40% for a single plan. See `<planner_authority_limits>` for prohibited split reasons.
313
-
314
- See @~/.claude/gsd-core/references/planner-guidance.md for Granularity Calibration table (Coarse/Standard/Fine plans-per-phase).
295
+ - **2-3 tasks per plan.** **ALWAYS split if:** >3 tasks, multiple subsystems, or any task touching >5 files.
296
+ - **Emit `estimate`**: run `estimate-calibration`; `tokens` = raw projection x factor, `raw_tokens` = that
297
+ projection before the factor (calibration measures actual/raw), `confidence` verbatim — derived from
298
+ sample count, never self-rated.
299
+ - **Over the smart-zone budget?** Re-slice: tracer + expansion slices. Advisory, never a block.
315
300
 
316
301
  </scope_estimation>
317
302
 
@@ -331,6 +316,12 @@ autonomous: true # false if plan has checkpoints
331
316
  requirements: [] # REQUIRED — Requirement IDs from ROADMAP this plan addresses. MUST NOT be empty.
332
317
  user_setup: [] # Human-required setup (omit if empty)
333
318
 
319
+ estimate: # Projected execution cost (see Estimate Emission)
320
+ tokens: 60000 # calibrated projection
321
+ raw_tokens: 30000 # pre-factor projection
322
+ tasks: 3 # task count the projection assumes
323
+ confidence: low # low | med | high — DERIVED from sample count, never self-rated
324
+
334
325
  must_haves:
335
326
  truths: [] # Observable behaviors
336
327
  artifacts: [] # Files that must exist
@@ -412,6 +403,7 @@ Create `.planning/phases/XX-name/{padded_phase}-{plan}-SUMMARY.md` when done
412
403
  | `autonomous` | Yes | `true` if no checkpoints |
413
404
  | `requirements` | Yes | **MUST** list requirement IDs from ROADMAP. Every roadmap requirement ID MUST appear in at least one plan. |
414
405
  | `user_setup` | No | Human-required setup items |
406
+ | `estimate` | No | Projected cost `{tokens, tasks, confidence}`. See Estimate Emission. |
415
407
  | `must_haves` | Yes | Goal-backward verification criteria |
416
408
 
417
409
  Wave numbers are pre-computed during planning. Execute-phase reads `wave` directly from frontmatter.
@@ -734,7 +726,7 @@ Read the most recent milestone retrospective and cross-milestone trends. Extract
734
726
  </step>
735
727
 
736
728
  <step name="inject_global_learnings">
737
- If `features.global_learnings` is `true`: run `gsd-tools query learnings.query --tag <tag> --limit 5` once per tag from PLAN.md frontmatter `tags` (or use the single most specific keyword). The handler matches one `--tag` at a time. Prefix matches with `[Prior learning from <project>]` as weak priors. Project-local decisions take precedence. Skip silently if disabled or no matches.
729
+ If `features.global_learnings` is `true`: run `gsd_run query learnings.query --tag <tag> --limit 5` once per tag from PLAN.md frontmatter `tags` (or use the single most specific keyword). The handler matches one `--tag` at a time. Prefix matches with `[Prior learning from <project>]` as weak priors. Project-local decisions take precedence. Skip silently if disabled or no matches.
738
730
  </step>
739
731
 
740
732
  <step name="gather_phase_context">
@@ -102,7 +102,7 @@ For each item where `fetch` is present, invoke the MCP tool matching `fetch.prov
102
102
  | `exa` | `mcp__exa__web_search_exa` with `fetch.query` |
103
103
  | `tavily` | `mcp__tavily__search` with `fetch.query` |
104
104
  | `perplexity` | `mcp__perplexity__*` (use the appropriate perplexity MCP tool for the query) |
105
- | `brave` | `gsd-tools query websearch "<fetch.query>"` (Brave-backed) or built-in `WebSearch` |
105
+ | `brave` | `gsd_run query websearch "<fetch.query>"` (Brave-backed) or built-in `WebSearch` |
106
106
  | `firecrawl` | `mcp__firecrawl__scrape` with url (scrape kind) or `mcp__firecrawl__search` |
107
107
  | `websearch` | built-in `WebSearch` tool |
108
108
  | `webfetch` | built-in `WebFetch` tool |
@@ -490,7 +490,7 @@ Orchestrator provides: project name/description, research mode, project context,
490
490
 
491
491
  ## Step 3: Execute Research
492
492
 
493
- For each domain, use the `<tool_strategy>` seam (Steps A–D): build questions JSON, call `gsd-tools query research-plan`, run the indicated provider per item, then cache each digest. Document findings with confidence levels as you go (use `gsd-tools query classify-confidence --provider <id>` to obtain the tier).
493
+ For each domain, use the `<tool_strategy>` seam (Steps A–D): build questions JSON, call `gsd_run query research-plan`, run the indicated provider per item, then cache each digest. Document findings with confidence levels as you go (use `gsd_run query classify-confidence --provider <id>` to obtain the tier).
494
494
 
495
495
  ## Step 4: Quality Check
496
496
 
@@ -104,46 +104,6 @@ This gate runs unconditionally on every audit. The .gitignore ensures screenshot
104
104
 
105
105
  </gitignore_gate>
106
106
 
107
- <playwright_mcp_approach>
108
-
109
- ## Automated Screenshot Capture via Playwright-MCP (preferred when available)
110
-
111
- Before attempting the CLI screenshot approach, check whether `mcp__playwright__*`
112
- tools are available in this session. If they are, use them instead of the CLI approach:
113
-
114
- ```
115
- # Preferred: Playwright-MCP automated verification
116
- # 1. Navigate to the component URL
117
- mcp__playwright__navigate(url="http://localhost:3000")
118
-
119
- # 2. Take desktop screenshot
120
- mcp__playwright__screenshot(name="desktop", width=1440, height=900)
121
-
122
- # 3. Take mobile screenshot
123
- mcp__playwright__screenshot(name="mobile", width=375, height=812)
124
-
125
- # 4. For specific components listed in UI-SPEC.md, navigate to each
126
- # component route and capture targeted screenshots for comparison
127
- # against the spec's stated dimensions, colors, and layout.
128
-
129
- # 5. Compare screenshots against UI-SPEC.md requirements:
130
- # - Dimensions: Is component X width 70vw as specified?
131
- # - Color: Is the accent color applied only on declared elements?
132
- # - Layout: Are spacing values within the declared spacing scale?
133
- # Report any visual discrepancies as automated findings.
134
- ```
135
-
136
- **When Playwright-MCP is available:**
137
- - Use it for all screenshot capture (skip the CLI approach below)
138
- - Each UI checkpoint from UI-SPEC.md can be verified automatically
139
- - Discrepancies are reported as pillar findings with screenshot evidence
140
- - Items requiring subjective judgment are flagged as `needs_human_review: true`
141
-
142
- **When Playwright-MCP is NOT available:** fall back to the CLI screenshot approach
143
- below. Behavior is unchanged from the standard code-only audit path.
144
-
145
- </playwright_mcp_approach>
146
-
147
107
  <screenshot_approach>
148
108
 
149
109
  ## Screenshot Capture (CLI only — no MCP, no persistent browser)