gsd-pi 2.48.0-dev.ced2eca → 2.49.0-dev.9e177e9

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 (249) hide show
  1. package/dist/headless-ui.js +12 -2
  2. package/dist/headless.js +29 -13
  3. package/dist/resources/extensions/gsd/auto/infra-errors.js +1 -0
  4. package/dist/resources/extensions/gsd/auto/phases.js +11 -11
  5. package/dist/resources/extensions/gsd/auto/resolve.js +2 -2
  6. package/dist/resources/extensions/gsd/auto/run-unit.js +2 -2
  7. package/dist/resources/extensions/gsd/auto/session.js +4 -0
  8. package/dist/resources/extensions/gsd/auto-artifact-paths.js +8 -10
  9. package/dist/resources/extensions/gsd/auto-dashboard.js +6 -3
  10. package/dist/resources/extensions/gsd/auto-dispatch.js +33 -21
  11. package/dist/resources/extensions/gsd/auto-post-unit.js +17 -24
  12. package/dist/resources/extensions/gsd/auto-prompts.js +102 -21
  13. package/dist/resources/extensions/gsd/auto-recovery.js +62 -184
  14. package/dist/resources/extensions/gsd/auto-start.js +4 -31
  15. package/dist/resources/extensions/gsd/auto-timers.js +2 -2
  16. package/dist/resources/extensions/gsd/auto-verification.js +4 -7
  17. package/dist/resources/extensions/gsd/auto-worktree.js +257 -113
  18. package/dist/resources/extensions/gsd/auto.js +7 -5
  19. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +89 -0
  20. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +8 -1
  21. package/dist/resources/extensions/gsd/branch-patterns.js +13 -0
  22. package/dist/resources/extensions/gsd/doctor-checks.js +5 -1234
  23. package/dist/resources/extensions/gsd/doctor-engine-checks.js +168 -0
  24. package/dist/resources/extensions/gsd/doctor-environment.js +28 -7
  25. package/dist/resources/extensions/gsd/doctor-git-checks.js +405 -0
  26. package/dist/resources/extensions/gsd/doctor-global-checks.js +74 -0
  27. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +600 -0
  28. package/dist/resources/extensions/gsd/doctor.js +9 -1
  29. package/dist/resources/extensions/gsd/extension-manifest.json +1 -1
  30. package/dist/resources/extensions/gsd/git-service.js +9 -10
  31. package/dist/resources/extensions/gsd/gsd-db.js +124 -1
  32. package/dist/resources/extensions/gsd/guided-flow-queue.js +10 -11
  33. package/dist/resources/extensions/gsd/markdown-renderer.js +33 -5
  34. package/dist/resources/extensions/gsd/preferences-types.js +2 -1
  35. package/dist/resources/extensions/gsd/preferences-validation.js +39 -0
  36. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +27 -8
  37. package/dist/resources/extensions/gsd/prompts/complete-slice.md +9 -8
  38. package/dist/resources/extensions/gsd/prompts/execute-task.md +16 -13
  39. package/dist/resources/extensions/gsd/prompts/forensics.md +12 -5
  40. package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +32 -0
  41. package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  42. package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
  43. package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
  44. package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  45. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  46. package/dist/resources/extensions/gsd/prompts/plan-slice.md +8 -3
  47. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +3 -0
  48. package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  49. package/dist/resources/extensions/gsd/repo-identity.js +29 -0
  50. package/dist/resources/extensions/gsd/roadmap-slices.js +2 -2
  51. package/dist/resources/extensions/gsd/session-forensics.js +6 -11
  52. package/dist/resources/extensions/gsd/session-lock.js +67 -56
  53. package/dist/resources/extensions/gsd/state.js +34 -7
  54. package/dist/resources/extensions/gsd/templates/milestone-summary.md +8 -0
  55. package/dist/resources/extensions/gsd/templates/plan.md +16 -0
  56. package/dist/resources/extensions/gsd/templates/roadmap.md +13 -0
  57. package/dist/resources/extensions/gsd/templates/slice-summary.md +9 -0
  58. package/dist/resources/extensions/gsd/templates/task-plan.md +24 -0
  59. package/dist/resources/extensions/gsd/tools/plan-slice.js +14 -1
  60. package/dist/resources/extensions/gsd/tools/validate-milestone.js +3 -3
  61. package/dist/resources/extensions/gsd/verdict-parser.js +84 -0
  62. package/dist/resources/extensions/gsd/worktree-resolver.js +24 -0
  63. package/dist/resources/extensions/gsd/worktree.js +3 -2
  64. package/dist/resources/extensions/remote-questions/config.js +3 -5
  65. package/dist/resources/extensions/search-the-web/native-search.js +8 -3
  66. package/dist/resources/extensions/search-the-web/tool-search.js +19 -2
  67. package/dist/resources/skills/github-workflows/references/gh/SKILL.md +22 -1
  68. package/dist/web/standalone/.next/BUILD_ID +1 -1
  69. package/dist/web/standalone/.next/app-path-routes-manifest.json +19 -19
  70. package/dist/web/standalone/.next/build-manifest.json +3 -3
  71. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  72. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  73. package/dist/web/standalone/.next/required-server-files.json +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  75. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/index.html +1 -1
  91. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app-paths-manifest.json +19 -19
  98. package/dist/web/standalone/.next/server/chunks/229.js +2 -2
  99. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  100. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  102. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  103. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  104. package/dist/web/standalone/.next/static/chunks/4024.7c75ac378de0f2b5.js +9 -0
  105. package/dist/web/standalone/.next/static/chunks/{webpack-0a4cd455ec4197d2.js → webpack-2473ce2c3879fff4.js} +1 -1
  106. package/dist/web/standalone/server.js +1 -1
  107. package/package.json +1 -1
  108. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  109. package/packages/pi-agent-core/dist/agent-loop.js +4 -1
  110. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  111. package/packages/pi-agent-core/src/agent-loop.ts +4 -1
  112. package/packages/pi-ai/dist/providers/openai-codex-responses.js +39 -10
  113. package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  114. package/packages/pi-ai/src/providers/openai-codex-responses.ts +39 -8
  115. package/packages/pi-coding-agent/dist/core/blob-store.d.ts.map +1 -1
  116. package/packages/pi-coding-agent/dist/core/blob-store.js +8 -3
  117. package/packages/pi-coding-agent/dist/core/blob-store.js.map +1 -1
  118. package/packages/pi-coding-agent/dist/core/discovery-cache.d.ts.map +1 -1
  119. package/packages/pi-coding-agent/dist/core/discovery-cache.js +9 -2
  120. package/packages/pi-coding-agent/dist/core/discovery-cache.js.map +1 -1
  121. package/packages/pi-coding-agent/dist/core/retry-handler.js +1 -1
  122. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  123. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  124. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -32
  125. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  126. package/packages/pi-coding-agent/dist/modes/rpc/jsonl.d.ts.map +1 -1
  127. package/packages/pi-coding-agent/dist/modes/rpc/jsonl.js +5 -0
  128. package/packages/pi-coding-agent/dist/modes/rpc/jsonl.js.map +1 -1
  129. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  130. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +0 -1
  131. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
  132. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
  133. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  134. package/packages/pi-coding-agent/package.json +1 -1
  135. package/packages/pi-coding-agent/src/core/blob-store.ts +6 -3
  136. package/packages/pi-coding-agent/src/core/discovery-cache.ts +9 -2
  137. package/packages/pi-coding-agent/src/core/retry-handler.ts +1 -1
  138. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +7 -32
  139. package/packages/pi-coding-agent/src/modes/rpc/jsonl.ts +6 -0
  140. package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +0 -2
  141. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -2
  142. package/pkg/package.json +1 -1
  143. package/src/resources/extensions/gsd/auto/infra-errors.ts +1 -0
  144. package/src/resources/extensions/gsd/auto/phases.ts +10 -11
  145. package/src/resources/extensions/gsd/auto/resolve.ts +3 -3
  146. package/src/resources/extensions/gsd/auto/run-unit.ts +2 -2
  147. package/src/resources/extensions/gsd/auto/session.ts +5 -0
  148. package/src/resources/extensions/gsd/auto/types.ts +13 -0
  149. package/src/resources/extensions/gsd/auto-artifact-paths.ts +19 -21
  150. package/src/resources/extensions/gsd/auto-dashboard.ts +5 -2
  151. package/src/resources/extensions/gsd/auto-dispatch.ts +39 -21
  152. package/src/resources/extensions/gsd/auto-loop.ts +1 -1
  153. package/src/resources/extensions/gsd/auto-post-unit.ts +18 -28
  154. package/src/resources/extensions/gsd/auto-prompts.ts +113 -19
  155. package/src/resources/extensions/gsd/auto-recovery.ts +65 -199
  156. package/src/resources/extensions/gsd/auto-start.ts +7 -27
  157. package/src/resources/extensions/gsd/auto-timers.ts +2 -2
  158. package/src/resources/extensions/gsd/auto-verification.ts +4 -7
  159. package/src/resources/extensions/gsd/auto-worktree.ts +305 -108
  160. package/src/resources/extensions/gsd/auto.ts +11 -10
  161. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +93 -0
  162. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +8 -0
  163. package/src/resources/extensions/gsd/branch-patterns.ts +16 -0
  164. package/src/resources/extensions/gsd/doctor-checks.ts +5 -1291
  165. package/src/resources/extensions/gsd/doctor-engine-checks.ts +182 -0
  166. package/src/resources/extensions/gsd/doctor-environment.ts +30 -7
  167. package/src/resources/extensions/gsd/doctor-git-checks.ts +415 -0
  168. package/src/resources/extensions/gsd/doctor-global-checks.ts +84 -0
  169. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +626 -0
  170. package/src/resources/extensions/gsd/doctor.ts +9 -1
  171. package/src/resources/extensions/gsd/extension-manifest.json +1 -1
  172. package/src/resources/extensions/gsd/git-service.ts +7 -15
  173. package/src/resources/extensions/gsd/gsd-db.ts +150 -2
  174. package/src/resources/extensions/gsd/guided-flow-queue.ts +11 -12
  175. package/src/resources/extensions/gsd/markdown-renderer.ts +37 -4
  176. package/src/resources/extensions/gsd/preferences-types.ts +5 -1
  177. package/src/resources/extensions/gsd/preferences-validation.ts +37 -0
  178. package/src/resources/extensions/gsd/prompts/complete-milestone.md +27 -8
  179. package/src/resources/extensions/gsd/prompts/complete-slice.md +9 -8
  180. package/src/resources/extensions/gsd/prompts/execute-task.md +16 -13
  181. package/src/resources/extensions/gsd/prompts/forensics.md +12 -5
  182. package/src/resources/extensions/gsd/prompts/gate-evaluate.md +32 -0
  183. package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  184. package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
  185. package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
  186. package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  187. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  188. package/src/resources/extensions/gsd/prompts/plan-slice.md +8 -3
  189. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +3 -0
  190. package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  191. package/src/resources/extensions/gsd/repo-identity.ts +28 -0
  192. package/src/resources/extensions/gsd/roadmap-slices.ts +2 -2
  193. package/src/resources/extensions/gsd/session-forensics.ts +6 -11
  194. package/src/resources/extensions/gsd/session-lock.ts +92 -64
  195. package/src/resources/extensions/gsd/state.ts +38 -5
  196. package/src/resources/extensions/gsd/templates/milestone-summary.md +8 -0
  197. package/src/resources/extensions/gsd/templates/plan.md +16 -0
  198. package/src/resources/extensions/gsd/templates/roadmap.md +13 -0
  199. package/src/resources/extensions/gsd/templates/slice-summary.md +9 -0
  200. package/src/resources/extensions/gsd/templates/task-plan.md +24 -0
  201. package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +2 -2
  202. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +35 -0
  203. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +1 -81
  204. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
  205. package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
  206. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +9 -12
  207. package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +115 -1
  208. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +65 -1
  209. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +50 -0
  210. package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +189 -0
  211. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +156 -0
  212. package/src/resources/extensions/gsd/tests/git-service.test.ts +49 -0
  213. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
  214. package/src/resources/extensions/gsd/tests/infra-error.test.ts +12 -2
  215. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +39 -0
  216. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
  217. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  218. package/src/resources/extensions/gsd/tests/quality-gates.test.ts +347 -0
  219. package/src/resources/extensions/gsd/tests/queue-completed-milestone-perf.test.ts +155 -0
  220. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +2 -1
  221. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +32 -0
  222. package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +26 -0
  223. package/src/resources/extensions/gsd/tests/run-uat.test.ts +20 -16
  224. package/src/resources/extensions/gsd/tests/session-lock-transient-read.test.ts +223 -0
  225. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +44 -4
  226. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +1 -1
  227. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +2 -1
  228. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +0 -16
  229. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +67 -0
  230. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +1 -1
  231. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +204 -0
  232. package/src/resources/extensions/gsd/tools/plan-slice.ts +16 -0
  233. package/src/resources/extensions/gsd/tools/validate-milestone.ts +3 -3
  234. package/src/resources/extensions/gsd/types.ts +30 -0
  235. package/src/resources/extensions/gsd/verdict-parser.ts +95 -0
  236. package/src/resources/extensions/gsd/verification-gate.ts +0 -2
  237. package/src/resources/extensions/gsd/worktree-resolver.ts +31 -0
  238. package/src/resources/extensions/gsd/worktree.ts +3 -2
  239. package/src/resources/extensions/remote-questions/config.ts +3 -5
  240. package/src/resources/extensions/search-the-web/native-search.ts +8 -3
  241. package/src/resources/extensions/search-the-web/tool-search.ts +22 -2
  242. package/src/resources/skills/github-workflows/references/gh/SKILL.md +22 -1
  243. package/dist/resources/extensions/gsd/auto-worktree-sync.js +0 -191
  244. package/dist/resources/extensions/gsd/resource-version.js +0 -97
  245. package/dist/web/standalone/.next/static/chunks/4024.11ca5c01938e5948.js +0 -9
  246. package/src/resources/extensions/gsd/auto-worktree-sync.ts +0 -234
  247. package/src/resources/extensions/gsd/resource-version.ts +0 -101
  248. /package/dist/web/standalone/.next/static/{PTL5V00OW8q4-092tUQKx → vNN0h0emdEi8l_npi8poE}/_buildManifest.js +0 -0
  249. /package/dist/web/standalone/.next/static/{PTL5V00OW8q4-092tUQKx → vNN0h0emdEi8l_npi8poE}/_ssgManifest.js +0 -0
@@ -17,6 +17,30 @@ skills_used:
17
17
 
18
18
  {{description}}
19
19
 
20
+ ## Failure Modes
21
+
22
+ <!-- Q5: What breaks when dependencies fail? OMIT ENTIRELY for tasks with no external dependencies. -->
23
+
24
+ | Dependency | On error | On timeout | On malformed response |
25
+ |------------|----------|-----------|----------------------|
26
+ | {{dependency}} | {{errorStrategy}} | {{timeoutStrategy}} | {{malformedStrategy}} |
27
+
28
+ ## Load Profile
29
+
30
+ <!-- Q6: What breaks at 10x load? OMIT ENTIRELY for tasks with no shared resources or scaling concerns. -->
31
+
32
+ - **Shared resources**: {{sharedResources — DB connections, caches, rate limiters, or none}}
33
+ - **Per-operation cost**: {{perOpCost — N API calls, M DB queries, K bytes, or trivial}}
34
+ - **10x breakpoint**: {{whatBreaksFirst — pool exhaustion, rate limit, memory, or N/A}}
35
+
36
+ ## Negative Tests
37
+
38
+ <!-- Q7: What negative tests prove robustness? OMIT ENTIRELY for trivial tasks. -->
39
+
40
+ - **Malformed inputs**: {{malformedInputTests — empty string, null, oversized, wrong type}}
41
+ - **Error paths**: {{errorPathTests — network timeout, auth failure, 5xx, invalid JSON}}
42
+ - **Boundary conditions**: {{boundaryTests — empty list, max length, zero, off-by-one}}
43
+
20
44
  ## Steps
21
45
 
22
46
  1. {{step}}
@@ -116,7 +116,7 @@ test("auto-timers.ts idle watchdog catch calls resolveAgentEndCancelled", () =>
116
116
  // Check that resolveAgentEndCancelled is called near this catch
117
117
  const catchRegion = source.slice(Math.max(0, idleCatchIdx - 200), idleCatchIdx + 200);
118
118
  assert.ok(
119
- catchRegion.includes("resolveAgentEndCancelled()"),
119
+ catchRegion.includes("resolveAgentEndCancelled("),
120
120
  "idle watchdog catch block must call resolveAgentEndCancelled",
121
121
  );
122
122
  });
@@ -129,7 +129,7 @@ test("auto-timers.ts hard timeout catch calls resolveAgentEndCancelled", () => {
129
129
  assert.ok(hardCatchIdx > -1, "hard timeout catch block must exist");
130
130
  const catchRegion = source.slice(Math.max(0, hardCatchIdx - 200), hardCatchIdx + 200);
131
131
  assert.ok(
132
- catchRegion.includes("resolveAgentEndCancelled()"),
132
+ catchRegion.includes("resolveAgentEndCancelled("),
133
133
  "hard timeout catch block must call resolveAgentEndCancelled",
134
134
  );
135
135
  });
@@ -1745,6 +1745,41 @@ test("resolveAgentEndCancelled prevents orphaned promise after abort path", asyn
1745
1745
  assert.equal(result.status, "cancelled");
1746
1746
  });
1747
1747
 
1748
+ test("resolveAgentEndCancelled with errorContext passes it through to resolved promise", async () => {
1749
+ _resetPendingResolve();
1750
+
1751
+ const { _setCurrentResolve } = await import("../auto/resolve.js");
1752
+
1753
+ const p = new Promise<UnitResult>((r) => {
1754
+ _setCurrentResolve(r);
1755
+ });
1756
+
1757
+ resolveAgentEndCancelled({ message: "test timeout", category: "timeout", isTransient: true });
1758
+
1759
+ const resolved = await p;
1760
+ assert.equal(resolved.status, "cancelled");
1761
+ assert.ok(resolved.errorContext, "errorContext must be present");
1762
+ assert.equal(resolved.errorContext!.category, "timeout");
1763
+ assert.equal(resolved.errorContext!.message, "test timeout");
1764
+ assert.equal(resolved.errorContext!.isTransient, true);
1765
+ });
1766
+
1767
+ test("resolveAgentEndCancelled without args produces no errorContext field", async () => {
1768
+ _resetPendingResolve();
1769
+
1770
+ const { _setCurrentResolve } = await import("../auto/resolve.js");
1771
+
1772
+ const p = new Promise<UnitResult>((r) => {
1773
+ _setCurrentResolve(r);
1774
+ });
1775
+
1776
+ resolveAgentEndCancelled();
1777
+
1778
+ const resolved = await p;
1779
+ assert.equal(resolved.status, "cancelled");
1780
+ assert.equal(resolved.errorContext, undefined, "errorContext must not be present when no args passed");
1781
+ });
1782
+
1748
1783
  // ─── #1571: artifact verification retry ──────────────────────────────────────
1749
1784
 
1750
1785
  test("autoLoop re-iterates when postUnitPreVerification returns retry (#1571)", async () => {
@@ -10,7 +10,6 @@ import {
10
10
  verifyExpectedArtifact,
11
11
  diagnoseExpectedArtifact,
12
12
  buildLoopRemediationSteps,
13
- selfHealRuntimeRecords,
14
13
  hasImplementationArtifacts,
15
14
  } from "../auto-recovery.ts";
16
15
  import { parseRoadmap, parsePlan } from "../parsers-legacy.ts";
@@ -112,7 +111,7 @@ test("resolveExpectedArtifactPath returns correct path for all slice-level types
112
111
 
113
112
  const uatResult = resolveExpectedArtifactPath("run-uat", "M001/S01", base);
114
113
  assert.ok(uatResult);
115
- assert.ok(uatResult!.includes("UAT-RESULT"));
114
+ assert.ok(uatResult!.includes("UAT"));
116
115
  });
117
116
 
118
117
  // ─── diagnoseExpectedArtifact ─────────────────────────────────────────────
@@ -572,85 +571,6 @@ test("verifyExpectedArtifact plan-slice fails after deleting a rendered task pla
572
571
  }
573
572
  });
574
573
 
575
- // ─── selfHealRuntimeRecords — worktree base path (#769) ──────────────────
576
-
577
- test("selfHealRuntimeRecords clears stale dispatched records (#769)", async (t) => {
578
- // selfHealRuntimeRecords now only clears stale dispatched records (>1h).
579
- // No completedKeySet parameter — deriveState is sole authority.
580
- const worktreeBase = makeTmpBase();
581
- const mainBase = makeTmpBase();
582
- t.after(() => {
583
- cleanup(worktreeBase);
584
- cleanup(mainBase);
585
- });
586
-
587
- const { writeUnitRuntimeRecord, readUnitRuntimeRecord } = await import("../unit-runtime.ts");
588
-
589
- // Write a stale runtime record in the worktree .gsd/runtime/units/
590
- writeUnitRuntimeRecord(worktreeBase, "run-uat", "M001/S01", Date.now() - 7200_000, {
591
- phase: "dispatched",
592
- });
593
-
594
- // Verify the runtime record exists before heal
595
- const before = readUnitRuntimeRecord(worktreeBase, "run-uat", "M001/S01");
596
- assert.ok(before, "runtime record should exist before heal");
597
-
598
- // Mock ExtensionContext with minimal notify
599
- const notifications: string[] = [];
600
- const mockCtx = {
601
- ui: { notify: (msg: string) => { notifications.push(msg); } },
602
- } as any;
603
-
604
- // Call selfHeal with worktreeBase — should clear the stale record
605
- await selfHealRuntimeRecords(worktreeBase, mockCtx);
606
-
607
- // The stale record should be cleared
608
- const after = readUnitRuntimeRecord(worktreeBase, "run-uat", "M001/S01");
609
- assert.equal(after, null, "runtime record should be cleared after heal");
610
- assert.ok(notifications.some(n => n.includes("Self-heal")), "should emit self-heal notification");
611
-
612
- // Write a stale record at mainBase
613
- writeUnitRuntimeRecord(mainBase, "run-uat", "M001/S01", Date.now() - 7200_000, {
614
- phase: "dispatched",
615
- });
616
- await selfHealRuntimeRecords(mainBase, mockCtx);
617
-
618
- // The record at mainBase should also be cleared by the stale timeout (>1h)
619
- const afterMain = readUnitRuntimeRecord(mainBase, "run-uat", "M001/S01");
620
- assert.equal(afterMain, null, "stale record at main base should be cleared by timeout");
621
- });
622
-
623
- // ─── #1625: selfHealRuntimeRecords on resume clears paused-session leftovers ──
624
-
625
- test("selfHealRuntimeRecords clears recently-paused dispatched records on resume (#1625)", async (t) => {
626
- // When pauseAuto closes out a unit but clearUnitRuntimeRecord silently fails
627
- // (e.g. permission error), selfHealRuntimeRecords on resume should still
628
- // clean up stale dispatched records that are >1h old.
629
- const base = makeTmpBase();
630
- t.after(() => cleanup(base));
631
-
632
- const { writeUnitRuntimeRecord, readUnitRuntimeRecord } = await import("../unit-runtime.ts");
633
-
634
- // Simulate a record left behind after a pause — aged >1h to be considered stale
635
- writeUnitRuntimeRecord(base, "execute-task", "M001/S01/T01", Date.now() - 3700_000, {
636
- phase: "dispatched",
637
- });
638
-
639
- const before = readUnitRuntimeRecord(base, "execute-task", "M001/S01/T01");
640
- assert.ok(before, "dispatched record should exist before resume heal");
641
- assert.equal(before!.phase, "dispatched");
642
-
643
- const notifications: string[] = [];
644
- const mockCtx = {
645
- ui: { notify: (msg: string) => { notifications.push(msg); } },
646
- } as any;
647
-
648
- await selfHealRuntimeRecords(base, mockCtx);
649
-
650
- const after = readUnitRuntimeRecord(base, "execute-task", "M001/S01/T01");
651
- assert.equal(after, null, "stale dispatched record should be cleared on resume (#1625)");
652
- });
653
-
654
574
  // ─── #793: invalidateAllCaches unblocks skip-loop ─────────────────────────
655
575
  // When the skip-loop breaker fires, it must call invalidateAllCaches() (not
656
576
  // just invalidateStateCache()) to clear path/parse caches that deriveState
@@ -125,9 +125,9 @@ console.log('\n=== complete-slice: schema v6 migration ===');
125
125
 
126
126
  const adapter = _getAdapter()!;
127
127
 
128
- // Verify schema version is current (v10 after M001 planning migrations)
128
+ // Verify schema version is current (v12 after quality gates table)
129
129
  const versionRow = adapter.prepare('SELECT MAX(version) as v FROM schema_version').get();
130
- assertEq(versionRow?.['v'], 11, 'schema version should be 11');
130
+ assertEq(versionRow?.['v'], 12, 'schema version should be 12');
131
131
 
132
132
  // Verify slices table has full_summary_md and full_uat_md columns
133
133
  const cols = adapter.prepare("PRAGMA table_info(slices)").all();
@@ -109,9 +109,9 @@ console.log('\n=== complete-task: schema v5 migration ===');
109
109
 
110
110
  const adapter = _getAdapter()!;
111
111
 
112
- // Verify schema version is current (v11 after state machine migration)
112
+ // Verify schema version is current (v12 after quality gates table)
113
113
  const versionRow = adapter.prepare('SELECT MAX(version) as v FROM schema_version').get();
114
- assertEq(versionRow?.['v'], 11, 'schema version should be 11');
114
+ assertEq(versionRow?.['v'], 12, 'schema version should be 12');
115
115
 
116
116
  // Verify all 4 new tables exist
117
117
  const tables = adapter.prepare(
@@ -48,35 +48,32 @@ test("#2313: completed-units.json should not be blindly wiped to [] on milestone
48
48
  // ─── Bug 2: metrics.json should be in the sync file lists ──────────────────
49
49
 
50
50
  test("#2313: syncStateToProjectRoot should sync metrics.json", () => {
51
- const syncSrcPath = join(import.meta.dirname, "..", "auto-worktree-sync.ts");
51
+ const syncSrcPath = join(import.meta.dirname, "..", "auto-worktree.ts");
52
52
  const syncSrc = readFileSync(syncSrcPath, "utf-8");
53
53
 
54
54
  // syncStateToProjectRoot should copy metrics.json from worktree to project root
55
55
  assert.ok(
56
56
  syncSrc.includes("metrics.json"),
57
- "auto-worktree-sync.ts should reference metrics.json for sync",
57
+ "auto-worktree.ts should reference metrics.json for sync",
58
58
  );
59
59
  });
60
60
 
61
- test("#2313: syncWorktreeStateBack should include metrics.json in root files list", () => {
61
+ test("#2313: syncWorktreeStateBack should include metrics.json in ROOT_STATE_FILES", () => {
62
62
  const autoWorktreeSrcPath = join(import.meta.dirname, "..", "auto-worktree.ts");
63
63
  const autoWorktreeSrc = readFileSync(autoWorktreeSrcPath, "utf-8");
64
64
 
65
- // Find the rootFiles array in syncWorktreeStateBack
66
- const syncBackIdx = autoWorktreeSrc.indexOf("syncWorktreeStateBack");
67
- assert.ok(syncBackIdx !== -1, "syncWorktreeStateBack exists");
65
+ // Find the ROOT_STATE_FILES constant (single source of truth for both sync directions)
66
+ const constIdx = autoWorktreeSrc.indexOf("ROOT_STATE_FILES");
67
+ assert.ok(constIdx !== -1, "ROOT_STATE_FILES constant exists");
68
68
 
69
- const rootFilesIdx = autoWorktreeSrc.indexOf("rootFiles", syncBackIdx);
70
- assert.ok(rootFilesIdx !== -1, "rootFiles list exists in syncWorktreeStateBack");
71
-
72
- // Get the rootFiles array content
73
- const arrayStart = autoWorktreeSrc.indexOf("[", rootFilesIdx);
69
+ // Get the array content
70
+ const arrayStart = autoWorktreeSrc.indexOf("[", constIdx);
74
71
  const arrayEnd = autoWorktreeSrc.indexOf("]", arrayStart);
75
72
  const rootFilesBlock = autoWorktreeSrc.slice(arrayStart, arrayEnd);
76
73
 
77
74
  assert.ok(
78
75
  rootFilesBlock.includes("metrics.json"),
79
- "metrics.json should be in syncWorktreeStateBack rootFiles list",
76
+ "metrics.json should be in ROOT_STATE_FILES list",
80
77
  );
81
78
  });
82
79
 
@@ -15,7 +15,7 @@ import assert from 'node:assert/strict';
15
15
  * - Report formatting
16
16
  */
17
17
 
18
- import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
18
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync, utimesSync } from "node:fs";
19
19
  import { join, dirname } from "node:path";
20
20
  import { tmpdir } from "node:os";
21
21
 
@@ -102,6 +102,120 @@ describe('doctor-environment', async () => {
102
102
  assert.deepStrictEqual(depsCheck!.status, "ok", "existing node_modules is ok");
103
103
  });
104
104
 
105
+ // ── Stale Dependencies: marker file check (#1974) ──────────────────
106
+ console.log("\n=== env: npm marker file newer than lockfile → ok (#1974) ===");
107
+ {
108
+ // Simulate the exact bug scenario:
109
+ // 1. node_modules dir mtime is old (no entries added/removed recently)
110
+ // 2. package-lock.json mtime is recent (npm rewrote it)
111
+ // 3. node_modules/.package-lock.json mtime is between dir and lockfile
112
+ // (npm wrote it during the same install that rewrote the lockfile)
113
+ //
114
+ // The bug: code compares lockfile mtime vs dir mtime → false positive warning
115
+ // The fix: compare lockfile mtime vs marker file mtime → correctly ok
116
+ const dir = createProjectDir({
117
+ "package.json": JSON.stringify({ name: "test" }),
118
+ });
119
+ mkdirSync(join(dir, "node_modules"), { recursive: true });
120
+
121
+ // Simulate the exact bug: npm install with "up to date" rewrites the
122
+ // lockfile and the marker, but no packages are added/removed so the
123
+ // directory mtime should be old. We write the marker first (which
124
+ // bumps dir mtime), then force the dir mtime back to the past.
125
+ //
126
+ // Timeline: dir(T-120s) < lockfile(T-5s) ≈ marker(T-5s)
127
+ // Bug: code compares lockfile vs dir → false positive stale warning
128
+ // Fix: code compares lockfile vs marker → correctly reports ok
129
+ const dirTime = new Date(Date.now() - 120_000);
130
+ const installTime = new Date(Date.now() - 5_000);
131
+
132
+ // Write marker file (this bumps dir mtime as a side effect)
133
+ writeFileSync(join(dir, "node_modules", ".package-lock.json"), "{}");
134
+ utimesSync(join(dir, "node_modules", ".package-lock.json"), installTime, installTime);
135
+
136
+ // Force dir mtime back to the past — simulates no top-level entries changed
137
+ utimesSync(join(dir, "node_modules"), dirTime, dirTime);
138
+
139
+ // Lockfile written at install time (same as marker, or slightly after)
140
+ writeFileSync(join(dir, "package-lock.json"), "{}");
141
+ utimesSync(join(dir, "package-lock.json"), installTime, installTime);
142
+
143
+ cleanups.push(dir);
144
+ const results = runEnvironmentChecks(dir);
145
+ const depsCheck = results.find(r => r.name === "dependencies");
146
+ assert.ok(depsCheck !== undefined, "dependencies check runs");
147
+ assert.equal(depsCheck!.status, "ok", "npm marker newer than lockfile → not stale");
148
+ }
149
+
150
+ console.log("\n=== env: yarn marker file newer than lockfile → ok (#1974) ===");
151
+ {
152
+ const dir = createProjectDir({
153
+ "package.json": JSON.stringify({ name: "test" }),
154
+ });
155
+ mkdirSync(join(dir, "node_modules"), { recursive: true });
156
+
157
+ const dirTime = new Date(Date.now() - 120_000);
158
+ const installTime = new Date(Date.now() - 5_000);
159
+
160
+ writeFileSync(join(dir, "node_modules", ".yarn-integrity"), "{}");
161
+ utimesSync(join(dir, "node_modules", ".yarn-integrity"), installTime, installTime);
162
+ utimesSync(join(dir, "node_modules"), dirTime, dirTime);
163
+
164
+ writeFileSync(join(dir, "yarn.lock"), "");
165
+ utimesSync(join(dir, "yarn.lock"), installTime, installTime);
166
+
167
+ cleanups.push(dir);
168
+ const results = runEnvironmentChecks(dir);
169
+ const depsCheck = results.find(r => r.name === "dependencies");
170
+ assert.ok(depsCheck !== undefined, "dependencies check runs");
171
+ assert.equal(depsCheck!.status, "ok", "yarn marker newer than lockfile → not stale");
172
+ }
173
+
174
+ console.log("\n=== env: pnpm marker file newer than lockfile → ok (#1974) ===");
175
+ {
176
+ const dir = createProjectDir({
177
+ "package.json": JSON.stringify({ name: "test" }),
178
+ });
179
+ mkdirSync(join(dir, "node_modules"), { recursive: true });
180
+
181
+ const dirTime = new Date(Date.now() - 120_000);
182
+ const installTime = new Date(Date.now() - 5_000);
183
+
184
+ writeFileSync(join(dir, "node_modules", ".modules.yaml"), "{}");
185
+ utimesSync(join(dir, "node_modules", ".modules.yaml"), installTime, installTime);
186
+ utimesSync(join(dir, "node_modules"), dirTime, dirTime);
187
+
188
+ writeFileSync(join(dir, "pnpm-lock.yaml"), "");
189
+ utimesSync(join(dir, "pnpm-lock.yaml"), installTime, installTime);
190
+
191
+ cleanups.push(dir);
192
+ const results = runEnvironmentChecks(dir);
193
+ const depsCheck = results.find(r => r.name === "dependencies");
194
+ assert.ok(depsCheck !== undefined, "dependencies check runs");
195
+ assert.equal(depsCheck!.status, "ok", "pnpm marker newer than lockfile → not stale");
196
+ }
197
+
198
+ console.log("\n=== env: no marker file falls back to dir mtime → stale warning (#1974) ===");
199
+ {
200
+ // No marker file exists, lockfile newer than dir → should still warn
201
+ const dir = createProjectDir({
202
+ "package.json": JSON.stringify({ name: "test" }),
203
+ });
204
+ mkdirSync(join(dir, "node_modules"), { recursive: true });
205
+
206
+ const past = new Date(Date.now() - 60_000);
207
+ utimesSync(join(dir, "node_modules"), past, past);
208
+
209
+ writeFileSync(join(dir, "package-lock.json"), "{}");
210
+ // No marker file written — fallback to dir mtime comparison
211
+
212
+ cleanups.push(dir);
213
+ const results = runEnvironmentChecks(dir);
214
+ const depsCheck = results.find(r => r.name === "dependencies");
215
+ assert.ok(depsCheck !== undefined, "dependencies check runs");
216
+ assert.equal(depsCheck!.status, "warning", "no marker + lockfile newer → stale warning");
217
+ }
218
+
105
219
  // ── Env File Check ─────────────────────────────────────────────────
106
220
  test('env: .env.example without .env detected', () => {
107
221
  const dir = createProjectDir({
@@ -15,6 +15,7 @@ import { tmpdir } from "node:os";
15
15
  import test from "node:test";
16
16
  import assert from "node:assert/strict";
17
17
  import { runGSDDoctor } from "../doctor.ts";
18
+ import { closeDatabase } from "../gsd-db.ts";
18
19
 
19
20
  function makeTmp(name: string): string {
20
21
  const dir = join(tmpdir(), `doctor-fixlevel-${name}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
@@ -112,6 +113,70 @@ test("fixLevel:all — no reconciliation issue codes are reported", async (t) =>
112
113
  assert.ok(roadmapContent.includes("- [ ] **S01"), "roadmap should remain unchecked");
113
114
  });
114
115
 
116
+ test("legacy roadmap fallback: future slices are treated as pending, active slice is not", async (t) => {
117
+ const tmp = makeTmp("legacy-pending-fallback");
118
+ t.after(() => {
119
+ try { closeDatabase(); } catch { /* noop */ }
120
+ rmSync(tmp, { recursive: true, force: true });
121
+ });
122
+
123
+ // Force the legacy parser branch.
124
+ try { closeDatabase(); } catch { /* noop */ }
125
+
126
+ const gsd = join(tmp, ".gsd");
127
+ const m = join(gsd, "milestones", "M001");
128
+ const s01 = join(m, "slices", "S01", "tasks");
129
+ mkdirSync(s01, { recursive: true });
130
+
131
+ writeFileSync(join(m, "M001-ROADMAP.md"), `# M001: Test
132
+
133
+ ## Slices
134
+
135
+ - [x] **S01: Done Slice** \`risk:low\` \`depends:[]\`
136
+ > Done
137
+ - [ ] **S02: Active Slice** \`risk:medium\` \`depends:[S01]\`
138
+ > In progress
139
+ - [ ] **S03: Future Slice** \`risk:low\` \`depends:[S02]\`
140
+ > Later
141
+ - [ ] **S04: Future Slice Two** \`risk:low\` \`depends:[S03]\`
142
+ > Later
143
+ `);
144
+
145
+ writeFileSync(join(m, "slices", "S01", "S01-PLAN.md"), `# S01: Done Slice
146
+
147
+ **Goal:** done
148
+
149
+ ## Tasks
150
+
151
+ - [x] **T01: Done task** \`est:5m\`
152
+ `);
153
+
154
+ // Active slice exists in state/registry but has no directory yet — this should
155
+ // still be reported as a real error, while future untouched slices should be skipped.
156
+ const report = await runGSDDoctor(tmp, { scope: "M001" });
157
+ const missingSliceDirUnits = report.issues
158
+ .filter(i => i.code === "missing_slice_dir")
159
+ .map(i => i.unitId)
160
+ .sort();
161
+
162
+ assert.deepStrictEqual(
163
+ missingSliceDirUnits,
164
+ ["M001/S02"],
165
+ "legacy fallback should only report the active slice, not future unstarted slices",
166
+ );
167
+
168
+ const missingTasksDirUnits = report.issues
169
+ .filter(i => i.code === "missing_tasks_dir")
170
+ .map(i => i.unitId)
171
+ .sort();
172
+
173
+ assert.deepStrictEqual(
174
+ missingTasksDirUnits,
175
+ [],
176
+ "future slices without directories should be skipped before missing_tasks_dir checks",
177
+ );
178
+ });
179
+
115
180
  test("fixLevel:all — delimiter_in_title still fixable", async (t) => {
116
181
  const tmp = makeTmp("delimiter-fix");
117
182
  t.after(() => rmSync(tmp, { recursive: true, force: true }));
@@ -141,7 +206,6 @@ test("fixLevel:all — delimiter_in_title still fixable", async (t) => {
141
206
 
142
207
  const report = await runGSDDoctor(tmp, { fix: true });
143
208
 
144
- const delimiterIssues = report.issues.filter(i => i.code === "delimiter_in_title");
145
209
  // The milestone-level delimiter is auto-fixed, but the report may or may not include it
146
210
  // depending on whether it was fixed successfully. Just verify it ran without crashing.
147
211
  assert.ok(report.issues !== undefined, "doctor produces a report");
@@ -145,6 +145,56 @@ describe('doctor-git', async () => {
145
145
  } else {
146
146
  }
147
147
 
148
+ // ─── Test 1b: Orphaned worktree fix when cwd is inside worktree (#1946) ──
149
+ // Reproduces the deadlock: if process.cwd() is inside the orphaned worktree,
150
+ // the doctor must chdir out before removing it — not skip the removal.
151
+ if (process.platform !== "win32") {
152
+ console.log("\n=== orphaned_auto_worktree (cwd inside worktree) ===");
153
+ {
154
+ const dir = createRepoWithCompletedMilestone();
155
+ cleanups.push(dir);
156
+
157
+ // Create worktree with milestone/M001 branch under .gsd/worktrees/
158
+ mkdirSync(join(dir, ".gsd", "worktrees"), { recursive: true });
159
+ run("git worktree add -b milestone/M001 .gsd/worktrees/M001", dir);
160
+
161
+ const wtPath = realpathSync(join(dir, ".gsd", "worktrees", "M001"));
162
+
163
+ // Simulate the deadlock: set cwd inside the orphaned worktree
164
+ const previousCwd = process.cwd();
165
+ process.chdir(wtPath);
166
+ try {
167
+ const fixed = await runGSDDoctor(dir, { fix: true, isolationMode: "worktree" });
168
+
169
+ // The fix must NOT skip removal — it should chdir out and remove
170
+ assert.ok(
171
+ !fixed.fixesApplied.some(f => f.includes("skipped removing worktree")),
172
+ "does NOT skip removal when cwd is inside worktree",
173
+ );
174
+ assert.ok(
175
+ fixed.fixesApplied.some(f => f.includes("removed orphaned worktree")),
176
+ "removes orphaned worktree even when cwd was inside it",
177
+ );
178
+
179
+ // Verify worktree is gone
180
+ const wtList = run("git worktree list", dir);
181
+ assert.ok(!wtList.includes("milestone/M001"), "worktree removed after fix with cwd inside");
182
+
183
+ // Verify cwd was moved out (should be basePath, not still inside worktree)
184
+ const newCwd = process.cwd();
185
+ assert.ok(
186
+ !newCwd.startsWith(wtPath),
187
+ "cwd moved out of worktree after fix",
188
+ );
189
+ } finally {
190
+ // Restore cwd — the worktree dir may be gone, so chdir to previousCwd
191
+ try { process.chdir(previousCwd); } catch { process.chdir(dir); }
192
+ }
193
+ }
194
+ } else {
195
+ console.log("\n=== orphaned_auto_worktree (cwd inside worktree — skipped on Windows) ===");
196
+ }
197
+
148
198
  // ─── Test 2: Stale milestone branch detection & fix ────────────────
149
199
  // Skip on Windows: git branch glob matching and path resolution
150
200
  // behave differently in Windows temp dirs.