gsd-pi 2.78.1-dev.e9d88a536 → 2.78.1-dev.eccf86e27

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 (212) hide show
  1. package/README.md +5 -7
  2. package/dist/help-text.js +1 -1
  3. package/dist/resource-loader.js +6 -1
  4. package/dist/resources/.managed-resources-content-hash +1 -1
  5. package/dist/resources/extensions/gsd/auto/detect-stuck.js +41 -5
  6. package/dist/resources/extensions/gsd/auto/loop.js +235 -36
  7. package/dist/resources/extensions/gsd/auto/phases.js +14 -7
  8. package/dist/resources/extensions/gsd/auto/session.js +36 -0
  9. package/dist/resources/extensions/gsd/auto-dispatch.js +49 -4
  10. package/dist/resources/extensions/gsd/auto-post-unit.js +26 -12
  11. package/dist/resources/extensions/gsd/auto-worktree.js +185 -201
  12. package/dist/resources/extensions/gsd/auto.js +139 -49
  13. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +1 -1
  14. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +26 -20
  15. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +67 -55
  16. package/dist/resources/extensions/gsd/crash-recovery.js +160 -47
  17. package/dist/resources/extensions/gsd/db/auto-workers.js +227 -0
  18. package/dist/resources/extensions/gsd/db/command-queue.js +105 -0
  19. package/dist/resources/extensions/gsd/db/milestone-leases.js +210 -0
  20. package/dist/resources/extensions/gsd/db/runtime-kv.js +91 -0
  21. package/dist/resources/extensions/gsd/db/unit-dispatches.js +322 -0
  22. package/dist/resources/extensions/gsd/db-writer.js +96 -16
  23. package/dist/resources/extensions/gsd/delegation-policy.js +155 -0
  24. package/dist/resources/extensions/gsd/docs/COORDINATION.md +42 -0
  25. package/dist/resources/extensions/gsd/doctor-proactive.js +4 -0
  26. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +22 -6
  27. package/dist/resources/extensions/gsd/doctor.js +12 -2
  28. package/dist/resources/extensions/gsd/gsd-db.js +355 -3
  29. package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
  30. package/dist/resources/extensions/gsd/guided-flow.js +116 -26
  31. package/dist/resources/extensions/gsd/interrupted-session.js +18 -15
  32. package/dist/resources/extensions/gsd/metrics.js +287 -1
  33. package/dist/resources/extensions/gsd/paths.js +79 -8
  34. package/dist/resources/extensions/gsd/prompts/complete-slice.md +4 -4
  35. package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -3
  36. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +8 -1
  37. package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +22 -7
  38. package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +6 -2
  39. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -1
  40. package/dist/resources/extensions/gsd/state.js +21 -6
  41. package/dist/resources/extensions/gsd/templates/project.md +10 -0
  42. package/dist/resources/extensions/gsd/workflow-mcp.js +2 -2
  43. package/dist/resources/extensions/gsd/workspace.js +59 -0
  44. package/dist/resources/extensions/gsd/worktree-resolver.js +79 -2
  45. package/dist/resources/extensions/gsd/write-intercept.js +3 -3
  46. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  47. package/dist/web/standalone/.next/BUILD_ID +1 -1
  48. package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
  49. package/dist/web/standalone/.next/build-manifest.json +2 -2
  50. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  51. package/dist/web/standalone/.next/required-server-files.json +1 -1
  52. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  61. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/index.html +1 -1
  69. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
  76. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  77. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  78. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  79. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  80. package/dist/web/standalone/server.js +1 -1
  81. package/package.json +1 -1
  82. package/packages/mcp-server/README.md +2 -11
  83. package/packages/mcp-server/dist/remote-questions.d.ts +27 -0
  84. package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -1
  85. package/packages/mcp-server/dist/remote-questions.js +28 -0
  86. package/packages/mcp-server/dist/remote-questions.js.map +1 -1
  87. package/packages/mcp-server/dist/server.d.ts +28 -0
  88. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  89. package/packages/mcp-server/dist/server.js +94 -4
  90. package/packages/mcp-server/dist/server.js.map +1 -1
  91. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  92. package/packages/mcp-server/src/mcp-server.test.ts +226 -0
  93. package/packages/mcp-server/src/remote-questions.test.ts +103 -0
  94. package/packages/mcp-server/src/remote-questions.ts +35 -0
  95. package/packages/mcp-server/src/server.ts +129 -6
  96. package/packages/mcp-server/src/workflow-tools.ts +1 -1
  97. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  98. package/src/resources/extensions/gsd/auto/detect-stuck.ts +37 -5
  99. package/src/resources/extensions/gsd/auto/loop.ts +263 -41
  100. package/src/resources/extensions/gsd/auto/phases.ts +15 -7
  101. package/src/resources/extensions/gsd/auto/session.ts +40 -0
  102. package/src/resources/extensions/gsd/auto-dispatch.ts +63 -4
  103. package/src/resources/extensions/gsd/auto-post-unit.ts +27 -12
  104. package/src/resources/extensions/gsd/auto-worktree.ts +218 -225
  105. package/src/resources/extensions/gsd/auto.ts +166 -43
  106. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +1 -1
  107. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +26 -21
  108. package/src/resources/extensions/gsd/bootstrap/tests/write-gate-basepath.test.ts +103 -0
  109. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +80 -55
  110. package/src/resources/extensions/gsd/crash-recovery.ts +177 -43
  111. package/src/resources/extensions/gsd/db/auto-workers.ts +273 -0
  112. package/src/resources/extensions/gsd/db/command-queue.ts +149 -0
  113. package/src/resources/extensions/gsd/db/milestone-leases.ts +274 -0
  114. package/src/resources/extensions/gsd/db/runtime-kv.ts +127 -0
  115. package/src/resources/extensions/gsd/db/unit-dispatches.ts +446 -0
  116. package/src/resources/extensions/gsd/db-writer.ts +113 -17
  117. package/src/resources/extensions/gsd/delegation-policy.ts +197 -0
  118. package/src/resources/extensions/gsd/docs/COORDINATION.md +42 -0
  119. package/src/resources/extensions/gsd/doctor-proactive.ts +4 -0
  120. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +24 -6
  121. package/src/resources/extensions/gsd/doctor.ts +10 -2
  122. package/src/resources/extensions/gsd/gsd-db.ts +354 -3
  123. package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
  124. package/src/resources/extensions/gsd/guided-flow.ts +152 -26
  125. package/src/resources/extensions/gsd/interrupted-session.ts +19 -12
  126. package/src/resources/extensions/gsd/metrics.ts +321 -1
  127. package/src/resources/extensions/gsd/paths.ts +67 -8
  128. package/src/resources/extensions/gsd/prompts/complete-slice.md +4 -4
  129. package/src/resources/extensions/gsd/prompts/execute-task.md +3 -3
  130. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +8 -1
  131. package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +22 -7
  132. package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +6 -2
  133. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -1
  134. package/src/resources/extensions/gsd/state.ts +44 -6
  135. package/src/resources/extensions/gsd/templates/project.md +10 -0
  136. package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +14 -14
  137. package/src/resources/extensions/gsd/tests/auto-loop-no-copy-artifacts.test.ts +72 -0
  138. package/src/resources/extensions/gsd/tests/auto-loop-symlink-worktree.test.ts +190 -0
  139. package/src/resources/extensions/gsd/tests/auto-session-scope.test.ts +331 -0
  140. package/src/resources/extensions/gsd/tests/auto-workers.test.ts +105 -0
  141. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +176 -0
  142. package/src/resources/extensions/gsd/tests/command-queue.test.ts +141 -0
  143. package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +203 -0
  144. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +169 -59
  145. package/src/resources/extensions/gsd/tests/db-writer-path-containment.test.ts +152 -0
  146. package/src/resources/extensions/gsd/tests/db-writer-root-artifact.test.ts +221 -0
  147. package/src/resources/extensions/gsd/tests/db-writer-scope.test.ts +230 -0
  148. package/src/resources/extensions/gsd/tests/delegation-policy.test.ts +151 -0
  149. package/src/resources/extensions/gsd/tests/detect-stuck-respects-retry.test.ts +173 -0
  150. package/src/resources/extensions/gsd/tests/dispatch-backgroundable-annotation.test.ts +55 -0
  151. package/src/resources/extensions/gsd/tests/draft-promotion.test.ts +3 -23
  152. package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +193 -0
  153. package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound-corrections.test.ts +246 -0
  154. package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound.test.ts +218 -0
  155. package/src/resources/extensions/gsd/tests/gsd-db-failed-open-restore.test.ts +117 -0
  156. package/src/resources/extensions/gsd/tests/gsd-db-workspace-scope.test.ts +226 -0
  157. package/src/resources/extensions/gsd/tests/gsd-root-canonical.test.ts +66 -0
  158. package/src/resources/extensions/gsd/tests/gsd-root-home-guard.test.ts +68 -5
  159. package/src/resources/extensions/gsd/tests/guided-flow-prompt-consolidation.test.ts +4 -4
  160. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +22 -12
  161. package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +24 -10
  162. package/src/resources/extensions/gsd/tests/integration/doctor-runtime.test.ts +35 -23
  163. package/src/resources/extensions/gsd/tests/integration/workspace-collapse-integration.test.ts +369 -0
  164. package/src/resources/extensions/gsd/tests/interrupted-session-auto.test.ts +72 -25
  165. package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +72 -25
  166. package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +9 -6
  167. package/src/resources/extensions/gsd/tests/metrics-atomic-merge.test.ts +222 -0
  168. package/src/resources/extensions/gsd/tests/metrics-lock-hardening.test.ts +400 -0
  169. package/src/resources/extensions/gsd/tests/metrics-lock-not-acquired.test.ts +141 -0
  170. package/src/resources/extensions/gsd/tests/metrics-lock-retry-sleep.test.ts +287 -0
  171. package/src/resources/extensions/gsd/tests/metrics-prune-cache-invalidation.test.ts +149 -0
  172. package/src/resources/extensions/gsd/tests/metrics-scope.test.ts +378 -0
  173. package/src/resources/extensions/gsd/tests/milestone-leases.test.ts +152 -0
  174. package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +329 -0
  175. package/src/resources/extensions/gsd/tests/parallel-milestone-isolation.test.ts +106 -0
  176. package/src/resources/extensions/gsd/tests/path-cache-decoupled.test.ts +209 -0
  177. package/src/resources/extensions/gsd/tests/path-normalization-unified.test.ts +175 -0
  178. package/src/resources/extensions/gsd/tests/paths-cache.test.ts +170 -0
  179. package/src/resources/extensions/gsd/tests/paused-session-via-db.test.ts +119 -0
  180. package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +120 -0
  181. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +58 -0
  182. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +3 -17
  183. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +150 -7
  184. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +138 -16
  185. package/src/resources/extensions/gsd/tests/resume-missing-worktree-warning.test.ts +209 -0
  186. package/src/resources/extensions/gsd/tests/runtime-kv.test.ts +120 -0
  187. package/src/resources/extensions/gsd/tests/skipped-validation-completion.test.ts +133 -28
  188. package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +17 -0
  189. package/src/resources/extensions/gsd/tests/stuck-state-via-db.test.ts +134 -0
  190. package/src/resources/extensions/gsd/tests/sync-layer-scope.test.ts +434 -0
  191. package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +162 -0
  192. package/src/resources/extensions/gsd/tests/teardown-cleanup-parity.test.ts +98 -0
  193. package/src/resources/extensions/gsd/tests/teardown-failure-clears-registry.test.ts +186 -0
  194. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +1 -1
  195. package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +247 -0
  196. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +41 -1
  197. package/src/resources/extensions/gsd/tests/validator-scope-parity.test.ts +239 -0
  198. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -2
  199. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +9 -15
  200. package/src/resources/extensions/gsd/tests/workspace.test.ts +196 -0
  201. package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +35 -35
  202. package/src/resources/extensions/gsd/tests/write-gate.test.ts +94 -71
  203. package/src/resources/extensions/gsd/tests/write-intercept.test.ts +1 -1
  204. package/src/resources/extensions/gsd/workflow-mcp.ts +2 -2
  205. package/src/resources/extensions/gsd/workspace.ts +95 -0
  206. package/src/resources/extensions/gsd/worktree-resolver.ts +78 -2
  207. package/src/resources/extensions/gsd/write-intercept.ts +3 -3
  208. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +0 -213
  209. package/src/resources/extensions/gsd/tests/auto-stale-lock-self-kill.test.ts +0 -87
  210. package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +0 -159
  211. /package/dist/web/standalone/.next/static/{oZGTPvJBQX_IDKKnuV8Bt → Y5UeGFkXTYM9WIQOWHkot}/_buildManifest.js +0 -0
  212. /package/dist/web/standalone/.next/static/{oZGTPvJBQX_IDKKnuV8Bt → Y5UeGFkXTYM9WIQOWHkot}/_ssgManifest.js +0 -0
@@ -8,6 +8,13 @@ Your goal is **not** to center the discussion on tech stack trivia, naming conve
8
8
 
9
9
  ## Interview Protocol
10
10
 
11
+ ### Read project shape
12
+
13
+ Before your first question round, read `.gsd/PROJECT.md` and look for `## Project Shape` → `**Complexity:**`. The verdict is either **`simple`** or **`complex`** (default to `complex` if the section is missing or unclear).
14
+
15
+ - **`simple`** — favor 1–2 plain-text rounds, write the slice context fast. Skip parallel-research investigation.
16
+ - **`complex`** — full investigation with structured 3–4-option questions.
17
+
11
18
  ### Before your first question round
12
19
 
13
20
  Do a lightweight targeted investigation so your questions are grounded in reality:
@@ -24,7 +31,7 @@ Do **not** go deep — just enough that your questions reflect what's actually t
24
31
 
25
32
  **Never fabricate or simulate user input.** Never generate fake transcript markers like `[User]`, `[Human]`, or `User:`. Ask one question round, then wait for the user's actual response before continuing.
26
33
 
27
- **If `{{structuredQuestionsAvailable}}` is `true`:** Ask **1–3 questions per round** using `ask_user_questions`. **Call `ask_user_questions` exactly once per turn — never make multiple calls with the same or overlapping questions. Wait for the user's response before asking the next round.**
34
+ **If `{{structuredQuestionsAvailable}}` is `true`:** Ask **1–3 questions per round** using `ask_user_questions`. In **`complex`** mode, each multi-choice question MUST present **3 or 4 concrete, researched options** plus a final **"Other — let me discuss"** option; options must be grounded in the investigation above (codebase signals, library docs, prior `.gsd/` artifacts), not generic placeholders. In **`simple`** mode, 2 options is fine. Binary wrap-up gates are exempt from the 3-or-4 rule. **Call `ask_user_questions` exactly once per turn — never make multiple calls with the same or overlapping questions. Wait for the user's response before asking the next round.**
28
35
  **If `{{structuredQuestionsAvailable}}` is `false`:** Ask **1–3 questions per round** in plain text. Number them and wait for the user's response before asking the next round.
29
36
  Keep each question focused on one of:
30
37
  - **UX and user-facing behaviour** — what does the user see, click, trigger, or experience?
@@ -208,10 +208,17 @@ export async function getActiveMilestoneId(basePath) {
208
208
  * Legacy filesystem parsing is available only through an explicit opt-in for
209
209
  * tests/recovery flows; runtime must not silently infer state from markdown.
210
210
  */
211
- export async function deriveState(basePath) {
212
- // Return cached result if within the TTL window for the same basePath
211
+ export async function deriveState(basePath, opts) {
212
+ // Use the canonical project root (when provided) as the cache key so that
213
+ // two calls with different basePath strings (e.g. worktree path vs project
214
+ // root) but the same canonical .gsd/ share a single cache entry. The same
215
+ // key is used for both the lookup AND the write below — keying lookup on
216
+ // canonical-root while writing on basePath would silently return stale
217
+ // results across path-form alternation.
218
+ const cacheKey = opts?.projectRootForReads ?? basePath;
219
+ // Return cached result if within the TTL window for the same cacheKey
213
220
  if (_stateCache &&
214
- _stateCache.basePath === basePath &&
221
+ _stateCache.basePath === cacheKey &&
215
222
  Date.now() - _stateCache.timestamp < CACHE_TTL_MS) {
216
223
  return _stateCache.result;
217
224
  }
@@ -230,7 +237,7 @@ export async function deriveState(basePath) {
230
237
  if (wasDbOpenAttempted()) {
231
238
  logWarning("state", "DB unavailable — using explicit legacy filesystem state derivation");
232
239
  }
233
- result = await _deriveStateImpl(basePath);
240
+ result = await _deriveStateImpl(basePath, opts);
234
241
  _telemetry.markdownDeriveCount++;
235
242
  }
236
243
  else {
@@ -252,7 +259,7 @@ export async function deriveState(basePath) {
252
259
  }
253
260
  stopTimer({ phase: result.phase, milestone: result.activeMilestone?.id });
254
261
  debugCount("deriveStateCalls");
255
- _stateCache = { basePath, result, timestamp: Date.now() };
262
+ _stateCache = { basePath: cacheKey, result, timestamp: Date.now() };
256
263
  return result;
257
264
  }
258
265
  /**
@@ -689,7 +696,15 @@ export async function deriveStateFromDb(basePath) {
689
696
  // LEGACY: Filesystem-based state derivation for unmigrated projects.
690
697
  // DB-backed projects use deriveStateFromDb() above. Target: extract to
691
698
  // state-legacy.ts when all projects are DB-backed.
692
- export async function _deriveStateImpl(basePath) {
699
+ export async function _deriveStateImpl(basePath, opts) {
700
+ // When the caller supplies a canonical project root for reads (e.g.
701
+ // s.canonicalProjectRoot from auto-mode), route all artifact reads through
702
+ // it. This prevents the worktree-local empty `.gsd/` from being consulted
703
+ // when the canonical state lives at the project root (or via a `.gsd`
704
+ // symlink into the external state dir).
705
+ if (opts?.projectRootForReads) {
706
+ basePath = opts.projectRootForReads;
707
+ }
693
708
  const diskIds = findMilestoneIds(basePath);
694
709
  const customOrder = loadQueueOrder(basePath);
695
710
  const milestoneIds = sortByQueueOrder(diskIds, customOrder);
@@ -11,6 +11,16 @@
11
11
 
12
12
  {{theOneThingThatMustWorkEvenIfEverythingElseIsCut}}
13
13
 
14
+ ## Project Shape
15
+
16
+ <!-- Drives questioning depth in downstream stages. `simple` → short plain-text
17
+ rounds, fast PROJECT/CONTEXT/REQUIREMENTS writes. `complex` → researched
18
+ 3–4-option questions with an "Other — let me discuss" hatch.
19
+ Default to `complex` when uncertain. -->
20
+
21
+ - **Complexity:** {{simple | complex}}
22
+ - **Why:** {{one-line rationale citing the signals that decided it}}
23
+
14
24
  ## Current State
15
25
 
16
26
  {{whatHasBeenBuiltSoFar — what works, what exists, what's deployed}}
@@ -292,9 +292,9 @@ export function getRequiredWorkflowToolsForAutoUnit(unitType) {
292
292
  case "execute-task":
293
293
  case "execute-task-simple":
294
294
  case "reactive-execute":
295
- return ["gsd_complete_task"];
295
+ return ["gsd_task_complete"];
296
296
  case "complete-slice":
297
- return ["gsd_complete_slice"];
297
+ return ["gsd_slice_complete"];
298
298
  case "replan-slice":
299
299
  return ["gsd_replan_slice"];
300
300
  case "reassess-roadmap":
@@ -0,0 +1,59 @@
1
+ // GSD-2 + Workspace handle: single source of truth for path resolution per milestone
2
+ import { join, resolve } from "node:path";
3
+ import { resolveGsdPathContract, normalizeRealPath } from "./paths.js";
4
+ import { isGsdWorktreePath, resolveWorktreeProjectRoot } from "./worktree-root.js";
5
+ function tryRealpath(p) {
6
+ return normalizeRealPath(p);
7
+ }
8
+ /**
9
+ * Create an immutable GsdWorkspace handle from a raw base path.
10
+ * Resolves both the project root and (when applicable) the worktree root,
11
+ * normalizes them via realpath, and freezes the result.
12
+ */
13
+ export function createWorkspace(rawBasePath) {
14
+ const resolvedBase = resolve(rawBasePath);
15
+ const isWorktree = isGsdWorktreePath(resolvedBase);
16
+ const projectRootRaw = resolveWorktreeProjectRoot(resolvedBase);
17
+ const projectRoot = tryRealpath(resolve(projectRootRaw));
18
+ const worktreeRoot = isWorktree ? tryRealpath(resolvedBase) : null;
19
+ // Derive a canonical base from the already-realpath-normalized paths so that
20
+ // resolveGsdPathContract always receives a canonical path. Using the raw
21
+ // resolvedBase here can produce a non-canonical projectGsd when the input
22
+ // path contains symlinks, causing contract.projectGsd to diverge from the
23
+ // realpath-normalized projectRoot / identityKey.
24
+ const canonicalBase = isWorktree ? (worktreeRoot ?? resolvedBase) : projectRoot;
25
+ const contract = Object.freeze(resolveGsdPathContract(canonicalBase));
26
+ const identityKey = tryRealpath(projectRoot);
27
+ const mode = isWorktree ? "worktree" : "project";
28
+ const workspace = Object.freeze({
29
+ projectRoot,
30
+ worktreeRoot,
31
+ mode,
32
+ contract,
33
+ identityKey,
34
+ lockRoot: projectRoot,
35
+ });
36
+ return workspace;
37
+ }
38
+ /**
39
+ * Bind a milestoneId to a workspace, producing an immutable MilestoneScope
40
+ * with path-returning closures that resolve via the authoritative projectGsd.
41
+ *
42
+ * All milestone-content paths route to contract.projectGsd (canonical),
43
+ * since that is the authoritative source of truth regardless of worktree mode.
44
+ */
45
+ export function scopeMilestone(workspace, milestoneId) {
46
+ const { contract } = workspace;
47
+ const gsd = contract.projectGsd;
48
+ const scope = Object.freeze({
49
+ workspace,
50
+ milestoneId,
51
+ contextFile: () => join(gsd, "milestones", milestoneId, `${milestoneId}-CONTEXT.md`),
52
+ roadmapFile: () => join(gsd, "milestones", milestoneId, `${milestoneId}-ROADMAP.md`),
53
+ stateFile: () => join(gsd, "STATE.md"),
54
+ dbPath: () => contract.projectDb,
55
+ milestoneDir: () => join(gsd, "milestones", milestoneId),
56
+ metaJson: () => join(gsd, `${milestoneId}-META.json`),
57
+ });
58
+ return scope;
59
+ }
@@ -1,3 +1,4 @@
1
+ // GSD-2 — WorktreeResolver: encapsulates worktree path state and merge/exit lifecycle.
1
2
  /**
2
3
  * WorktreeResolver — encapsulates worktree path state and merge/exit lifecycle.
3
4
  *
@@ -20,7 +21,20 @@ import { emitJournalEvent } from "./journal.js";
20
21
  import { emitWorktreeCreated, emitWorktreeMerged } from "./worktree-telemetry.js";
21
22
  import { getCollapseCadence, getMilestoneResquash, resquashMilestoneOnMain } from "./slice-cadence.js";
22
23
  import { loadEffectiveGSDPreferences } from "./preferences.js";
23
- import { resolveWorktreeProjectRoot } from "./worktree-root.js";
24
+ import { resolveWorktreeProjectRoot, normalizeWorktreePathForCompare } from "./worktree-root.js";
25
+ import { claimMilestoneLease, releaseMilestoneLease } from "./db/milestone-leases.js";
26
+ // ─── Path Comparison Helper ────────────────────────────────────────────────
27
+ /**
28
+ * Compare two paths for physical identity, tolerating trailing slashes,
29
+ * symlink differences, and case variations on case-insensitive volumes.
30
+ *
31
+ * Used in place of string `===` / `!==` wherever one operand may be
32
+ * realpath-normalised (e.g. from the workspace registry) and the other
33
+ * may not be (e.g. a raw caller-supplied basePath).
34
+ */
35
+ function isSamePath(a, b) {
36
+ return normalizeWorktreePathForCompare(a) === normalizeWorktreePathForCompare(b);
37
+ }
24
38
  // ─── Path Helpers ──────────────────────────────────────────────────────────
25
39
  /**
26
40
  * Resolve the project root from session path state.
@@ -98,6 +112,69 @@ export class WorktreeResolver {
98
112
  });
99
113
  return;
100
114
  }
115
+ // Phase B: claim a milestone lease before any worktree mutation. Two
116
+ // workers cannot enter the same milestone concurrently. Best-effort:
117
+ // skip if no worker registered (single-worker fallback) or DB
118
+ // unavailable; reuse existing lease if we already hold it on this
119
+ // milestone (re-entry within the same session).
120
+ if (this.s.workerId) {
121
+ if (this.s.currentMilestoneId === milestoneId && this.s.milestoneLeaseToken !== null) {
122
+ // Already held — no-op, the heartbeat in loop.ts refreshes TTL.
123
+ }
124
+ else {
125
+ // If we held a different milestone, release it first so other
126
+ // workers don't have to wait for TTL.
127
+ if (this.s.currentMilestoneId && this.s.currentMilestoneId !== milestoneId && this.s.milestoneLeaseToken !== null) {
128
+ try {
129
+ releaseMilestoneLease(this.s.workerId, this.s.currentMilestoneId, this.s.milestoneLeaseToken);
130
+ }
131
+ catch (err) {
132
+ debugLog("WorktreeResolver", {
133
+ action: "enterMilestone",
134
+ milestoneId,
135
+ releasePriorLeaseError: err instanceof Error ? err.message : String(err),
136
+ });
137
+ }
138
+ this.s.milestoneLeaseToken = null;
139
+ }
140
+ try {
141
+ const claim = claimMilestoneLease(this.s.workerId, milestoneId);
142
+ if (claim.ok) {
143
+ this.s.milestoneLeaseToken = claim.token;
144
+ debugLog("WorktreeResolver", {
145
+ action: "enterMilestone",
146
+ milestoneId,
147
+ leaseAcquired: true,
148
+ fencingToken: claim.token,
149
+ expiresAt: claim.expiresAt,
150
+ });
151
+ }
152
+ else {
153
+ // Lease held by another worker — fail loud so the user can
154
+ // see the conflict instead of silently double-running.
155
+ const msg = `Milestone ${milestoneId} is held by worker ${claim.byWorker} until ${claim.expiresAt}.`;
156
+ debugLog("WorktreeResolver", {
157
+ action: "enterMilestone",
158
+ milestoneId,
159
+ leaseHeldByOther: claim.byWorker,
160
+ expiresAt: claim.expiresAt,
161
+ });
162
+ ctx.notify(`${msg} Another auto-mode worker is active. Stop it before entering ${milestoneId}.`, "error");
163
+ return;
164
+ }
165
+ }
166
+ catch (err) {
167
+ // DB unavailable or other error — log and fall through to the
168
+ // pre-Phase-B single-worker behavior so a fresh project before
169
+ // DB init still works.
170
+ debugLog("WorktreeResolver", {
171
+ action: "enterMilestone",
172
+ milestoneId,
173
+ leaseError: err instanceof Error ? err.message : String(err),
174
+ });
175
+ }
176
+ }
177
+ }
101
178
  // Resolve the project root for worktree operations via shared helper.
102
179
  // Handles the case where originalBasePath is falsy and basePath is itself
103
180
  // a worktree path — prevents double-nested worktree paths (#3729).
@@ -454,7 +531,7 @@ export class WorktreeResolver {
454
531
  // roadmap triggers bare teardown which deletes the branch and orphans all
455
532
  // milestone commits (#1573).
456
533
  let roadmapPath = this.deps.resolveMilestoneFile(originalBase, milestoneId, "ROADMAP");
457
- if (!roadmapPath && this.s.basePath !== originalBase) {
534
+ if (!roadmapPath && !isSamePath(this.s.basePath, originalBase)) {
458
535
  roadmapPath = this.deps.resolveMilestoneFile(this.s.basePath, milestoneId, "ROADMAP");
459
536
  if (roadmapPath) {
460
537
  debugLog("WorktreeResolver", {
@@ -85,9 +85,9 @@ function matchesBlockedPattern(path) {
85
85
  * Directs the agent to use engine tool calls instead.
86
86
  */
87
87
  export const BLOCKED_WRITE_ERROR = `Direct writes to .gsd/STATE.md and .gsd/gsd.db are blocked. Use engine tool calls instead:
88
- - To complete a task: call gsd_complete_task(milestone_id, slice_id, task_id, summary)
89
- - To complete a slice: call gsd_complete_slice(milestone_id, slice_id, summary, uat_result)
90
- - To save a decision: call gsd_save_decision(scope, decision, choice, rationale)
88
+ - To complete a task: call gsd_task_complete(milestone_id, slice_id, task_id, summary)
89
+ - To complete a slice: call gsd_slice_complete(milestone_id, slice_id, summary, uat_result)
90
+ - To save a decision: call gsd_decision_save(scope, decision, choice, rationale)
91
91
  - To start a task: call gsd_start_task(milestone_id, slice_id, task_id)
92
92
  - To record verification: call gsd_record_verification(milestone_id, slice_id, task_id, evidence)
93
93
  - To report a blocker: call gsd_report_blocker(milestone_id, slice_id, task_id, description)`;