@opengsd/gsd-pi 1.5.0-dev.4fb2d460 → 1.5.0-dev.c4c8885f

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 (234) hide show
  1. package/dist/resources/.managed-resources-content-hash +1 -1
  2. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +114 -36
  3. package/dist/resources/extensions/gsd/activity-log.js +1 -1
  4. package/dist/resources/extensions/gsd/auto/orchestrator.js +16 -10
  5. package/dist/resources/extensions/gsd/auto-artifact-paths.js +2 -2
  6. package/dist/resources/extensions/gsd/auto-dispatch.js +53 -15
  7. package/dist/resources/extensions/gsd/auto-model-selection.js +28 -1
  8. package/dist/resources/extensions/gsd/auto-post-unit.js +4 -3
  9. package/dist/resources/extensions/gsd/auto-prompts.js +29 -117
  10. package/dist/resources/extensions/gsd/auto-start.js +53 -30
  11. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +12 -2
  12. package/dist/resources/extensions/gsd/auto-timers.js +9 -2
  13. package/dist/resources/extensions/gsd/auto.js +97 -31
  14. package/dist/resources/extensions/gsd/compat/compat-marker.js +44 -0
  15. package/dist/resources/extensions/gsd/config-overlay.js +7 -2
  16. package/dist/resources/extensions/gsd/db/engine.js +33 -0
  17. package/dist/resources/extensions/gsd/db-adapter.js +30 -3
  18. package/dist/resources/extensions/gsd/docs/preferences-reference.md +3 -3
  19. package/dist/resources/extensions/gsd/journal.js +1 -1
  20. package/dist/resources/extensions/gsd/markdown-renderer.js +34 -91
  21. package/dist/resources/extensions/gsd/metrics.js +2 -2
  22. package/dist/resources/extensions/gsd/paths.js +70 -49
  23. package/dist/resources/extensions/gsd/post-unit-hooks.js +27 -0
  24. package/dist/resources/extensions/gsd/preferences-models.js +66 -15
  25. package/dist/resources/extensions/gsd/preferences-validation.js +66 -10
  26. package/dist/resources/extensions/gsd/rule-registry.js +106 -10
  27. package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +1 -10
  28. package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +3 -24
  29. package/dist/resources/extensions/gsd/state-reconciliation/index.js +10 -0
  30. package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -5
  31. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +16 -10
  32. package/dist/resources/extensions/gsd/tools/validate-milestone.js +3 -8
  33. package/dist/resources/extensions/gsd/uat-dispatch.js +94 -0
  34. package/dist/resources/extensions/gsd/uok/audit-toggle.js +31 -2
  35. package/dist/resources/extensions/gsd/uok/audit.js +3 -0
  36. package/dist/resources/extensions/gsd/uok/gate-runner.js +22 -30
  37. package/dist/resources/extensions/gsd/uok/gitops.js +18 -15
  38. package/dist/resources/extensions/gsd/uok/kernel.js +120 -46
  39. package/dist/resources/extensions/gsd/uok/model-policy.js +17 -14
  40. package/dist/resources/extensions/gsd/workflow-event-ledger.js +1 -1
  41. package/dist/resources/extensions/gsd/workflow-logger.js +1 -1
  42. package/dist/resources/extensions/gsd/workflow-projections.js +9 -26
  43. package/dist/resources/extensions/gsd/workspace.js +5 -14
  44. package/dist/resources/extensions/shared/gsd-browser-cli.js +29 -0
  45. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  46. package/dist/web/standalone/.next/BUILD_ID +1 -1
  47. package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
  48. package/dist/web/standalone/.next/build-manifest.json +2 -2
  49. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  50. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  51. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  59. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  67. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  68. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  69. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  70. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  71. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  72. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  73. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  74. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  75. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  76. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  77. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  78. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  79. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  80. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  81. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  82. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
  83. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  84. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  85. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  86. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  87. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  88. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  89. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  90. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  91. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  92. package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
  93. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  94. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  95. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  96. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  97. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  98. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  99. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  100. package/dist/web/standalone/.next/server/app/index.html +1 -1
  101. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
  108. package/dist/web/standalone/.next/server/chunks/{1128.js → 1991.js} +1 -1
  109. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  110. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  112. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  113. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  114. package/dist/worktree-cli-create.d.ts +10 -0
  115. package/dist/worktree-cli-create.js +11 -0
  116. package/dist/worktree-cli-format.d.ts +12 -0
  117. package/dist/worktree-cli-format.js +38 -0
  118. package/dist/worktree-cli-plan.d.ts +16 -0
  119. package/dist/worktree-cli-plan.js +19 -0
  120. package/dist/worktree-cli-session.d.ts +13 -0
  121. package/dist/worktree-cli-session.js +18 -0
  122. package/dist/worktree-cli-status.d.ts +35 -0
  123. package/dist/worktree-cli-status.js +54 -0
  124. package/dist/worktree-cli.d.ts +1 -16
  125. package/dist/worktree-cli.js +28 -122
  126. package/integrations/hermes/open_gsd_hermes/gsd_client.py +1 -1
  127. package/integrations/hermes/pyproject.toml +1 -1
  128. package/package.json +1 -1
  129. package/packages/cloud-mcp-gateway/package.json +2 -2
  130. package/packages/contracts/package.json +1 -1
  131. package/packages/daemon/dist/orchestrator-agent.d.ts +21 -0
  132. package/packages/daemon/dist/orchestrator-agent.d.ts.map +1 -0
  133. package/packages/daemon/dist/orchestrator-agent.js +113 -0
  134. package/packages/daemon/dist/orchestrator-agent.js.map +1 -0
  135. package/packages/daemon/dist/orchestrator-tools.d.ts +12 -0
  136. package/packages/daemon/dist/orchestrator-tools.d.ts.map +1 -0
  137. package/packages/daemon/dist/orchestrator-tools.js +137 -0
  138. package/packages/daemon/dist/orchestrator-tools.js.map +1 -0
  139. package/packages/daemon/dist/orchestrator.d.ts +4 -56
  140. package/packages/daemon/dist/orchestrator.d.ts.map +1 -1
  141. package/packages/daemon/dist/orchestrator.js +13 -303
  142. package/packages/daemon/dist/orchestrator.js.map +1 -1
  143. package/packages/daemon/dist/orchestrator.test.js +11 -1
  144. package/packages/daemon/dist/orchestrator.test.js.map +1 -1
  145. package/packages/daemon/package.json +4 -4
  146. package/packages/gsd-agent-core/package.json +5 -5
  147. package/packages/gsd-agent-modes/package.json +7 -7
  148. package/packages/mcp-server/package.json +4 -4
  149. package/packages/native/package.json +1 -1
  150. package/packages/pi-agent-core/package.json +1 -1
  151. package/packages/pi-ai/package.json +1 -1
  152. package/packages/pi-coding-agent/package.json +7 -7
  153. package/packages/pi-tui/package.json +2 -2
  154. package/packages/rpc-client/package.json +2 -2
  155. package/pkg/package.json +1 -1
  156. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +146 -33
  157. package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +25 -0
  158. package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +93 -0
  159. package/src/resources/extensions/gsd/activity-log.ts +1 -1
  160. package/src/resources/extensions/gsd/auto/orchestrator.ts +17 -11
  161. package/src/resources/extensions/gsd/auto-artifact-paths.ts +2 -1
  162. package/src/resources/extensions/gsd/auto-dispatch.ts +59 -14
  163. package/src/resources/extensions/gsd/auto-model-selection.ts +33 -1
  164. package/src/resources/extensions/gsd/auto-post-unit.ts +4 -2
  165. package/src/resources/extensions/gsd/auto-prompts.ts +53 -113
  166. package/src/resources/extensions/gsd/auto-start.ts +57 -28
  167. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +12 -2
  168. package/src/resources/extensions/gsd/auto-timers.ts +9 -2
  169. package/src/resources/extensions/gsd/auto.ts +103 -30
  170. package/src/resources/extensions/gsd/compat/compat-marker.ts +47 -0
  171. package/src/resources/extensions/gsd/config-overlay.ts +7 -1
  172. package/src/resources/extensions/gsd/db/engine.ts +36 -0
  173. package/src/resources/extensions/gsd/db-adapter.ts +29 -3
  174. package/src/resources/extensions/gsd/docs/preferences-reference.md +3 -3
  175. package/src/resources/extensions/gsd/journal.ts +1 -1
  176. package/src/resources/extensions/gsd/markdown-renderer.ts +52 -103
  177. package/src/resources/extensions/gsd/metrics.ts +2 -2
  178. package/src/resources/extensions/gsd/paths.ts +87 -48
  179. package/src/resources/extensions/gsd/post-unit-hooks.ts +32 -0
  180. package/src/resources/extensions/gsd/preferences-models.ts +74 -20
  181. package/src/resources/extensions/gsd/preferences-types.ts +24 -1
  182. package/src/resources/extensions/gsd/preferences-validation.ts +77 -9
  183. package/src/resources/extensions/gsd/rule-registry.ts +111 -9
  184. package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +1 -9
  185. package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +2 -22
  186. package/src/resources/extensions/gsd/state-reconciliation/index.ts +14 -0
  187. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +41 -2
  188. package/src/resources/extensions/gsd/tests/auto-post-unit-artifact-diagnostic.test.ts +48 -1
  189. package/src/resources/extensions/gsd/tests/auto-start-project-milestone-reconcile.test.ts +29 -1
  190. package/src/resources/extensions/gsd/tests/auto-timeout-recovery-supervisor-inflight-guard.test.ts +159 -0
  191. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +3 -1
  192. package/src/resources/extensions/gsd/tests/compat-marker.test.ts +85 -0
  193. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +45 -0
  194. package/src/resources/extensions/gsd/tests/db-adapter.test.ts +39 -0
  195. package/src/resources/extensions/gsd/tests/db-writable-probe.test.ts +66 -0
  196. package/src/resources/extensions/gsd/tests/flat-phase-renderer.test.ts +22 -1
  197. package/src/resources/extensions/gsd/tests/flat-phase-validation-path.test.ts +72 -32
  198. package/src/resources/extensions/gsd/tests/integration/paths.test.ts +88 -1
  199. package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +2 -1
  200. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +0 -1
  201. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +269 -0
  202. package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +8 -1
  203. package/src/resources/extensions/gsd/tests/read-uat-gate-verdict.test.ts +54 -0
  204. package/src/resources/extensions/gsd/tests/reassess-handler.test.ts +49 -1
  205. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +95 -1
  206. package/src/resources/extensions/gsd/tests/sibling-model-fallbacks.test.ts +189 -0
  207. package/src/resources/extensions/gsd/tests/single-field-model-thinking.test.ts +156 -0
  208. package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +6 -1
  209. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +32 -0
  210. package/src/resources/extensions/gsd/tests/supervisor-model-swap-inflight-guard.test.ts +181 -0
  211. package/src/resources/extensions/gsd/tests/supervisor-model-swap-thinking-clamp.test.ts +122 -0
  212. package/src/resources/extensions/gsd/tests/uat-dispatch.test.ts +211 -0
  213. package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +39 -0
  214. package/src/resources/extensions/gsd/tests/uok-kernel-path.test.ts +226 -2
  215. package/src/resources/extensions/gsd/tests/uok-model-policy.test.ts +6 -0
  216. package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +213 -2
  217. package/src/resources/extensions/gsd/tools/complete-milestone.ts +9 -5
  218. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +19 -8
  219. package/src/resources/extensions/gsd/tools/validate-milestone.ts +13 -9
  220. package/src/resources/extensions/gsd/types.ts +23 -4
  221. package/src/resources/extensions/gsd/uat-dispatch.ts +176 -0
  222. package/src/resources/extensions/gsd/uok/audit-toggle.ts +32 -2
  223. package/src/resources/extensions/gsd/uok/audit.ts +2 -0
  224. package/src/resources/extensions/gsd/uok/gate-runner.ts +26 -36
  225. package/src/resources/extensions/gsd/uok/gitops.ts +21 -18
  226. package/src/resources/extensions/gsd/uok/kernel.ts +169 -48
  227. package/src/resources/extensions/gsd/uok/model-policy.ts +20 -17
  228. package/src/resources/extensions/gsd/workflow-event-ledger.ts +1 -1
  229. package/src/resources/extensions/gsd/workflow-logger.ts +1 -1
  230. package/src/resources/extensions/gsd/workflow-projections.ts +9 -24
  231. package/src/resources/extensions/gsd/workspace.ts +5 -21
  232. package/src/resources/extensions/shared/gsd-browser-cli.ts +29 -0
  233. /package/dist/web/standalone/.next/static/{gXoWec4UNqcR0tkuHwYCr → NPMNi2HV8BuK3baQsEUhk}/_buildManifest.js +0 -0
  234. /package/dist/web/standalone/.next/static/{gXoWec4UNqcR0tkuHwYCr → NPMNi2HV8BuK3baQsEUhk}/_ssgManifest.js +0 -0
@@ -38,9 +38,10 @@ import {
38
38
  resolveSlicePath,
39
39
  resolveTaskFile,
40
40
  resolveTasksDir,
41
+ targetMilestoneFile,
42
+ targetSliceFile,
41
43
  gsdProjectionRoot,
42
44
  gsdRoot,
43
- buildMilestoneFileName,
44
45
  buildTaskFileName,
45
46
  buildSliceFileName,
46
47
  } from "./paths.js";
@@ -52,9 +53,6 @@ import { readCompatMarker, writeCompatMarker, computeProjectionSha } from "./com
52
53
  import type { RiskLevel } from "./types.js";
53
54
  import {
54
55
  phaseDirName,
55
- planFileName,
56
- milestoneIdToPhaseNum,
57
- sliceIdToPlanNum,
58
56
  derivePhaseSlug,
59
57
  } from "./layout-policy.js";
60
58
 
@@ -176,23 +174,6 @@ function sanitizeInlineRoadmapText(value: string | null | undefined): string {
176
174
  .trim();
177
175
  }
178
176
 
179
- function resolveRoadmapProjectionPath(basePath: string, milestoneId: string): string {
180
- const phaseNum = milestoneIdToPhaseNum(milestoneId);
181
- const existing = resolveMilestonePath(basePath, milestoneId);
182
- const legacyBase = legacyMilestonesDir(basePath);
183
- const isLegacyLayout = existing
184
- ? existing.startsWith(legacyBase + "/") || existing.startsWith(legacyBase + "\\")
185
- : isLegacyMilestonesLayout(basePath);
186
- const phaseDir = existing ?? join(
187
- isLegacyLayout ? legacyBase : milestonesDir(basePath),
188
- isLegacyLayout ? milestoneId : canonicalPhaseDirName(milestoneId, getMilestone(milestoneId)?.title),
189
- );
190
- const roadmapFileName = isLegacyLayout
191
- ? `${milestoneId}-ROADMAP.md`
192
- : `${String(phaseNum).padStart(2, "0")}-ROADMAP.md`;
193
- return join(phaseDir, roadmapFileName);
194
- }
195
-
196
177
  function isMilestoneFlatPhaseLayout(basePath: string, milestoneId: string): boolean {
197
178
  const existing = resolveMilestonePath(basePath, milestoneId);
198
179
  const legacyBase = legacyMilestonesDir(basePath);
@@ -600,7 +581,7 @@ export async function renderRoadmapFromDb(
600
581
  "projection",
601
582
  `renderRoadmapFromDb skipped unplanned milestone ${milestoneId} (zero slices, empty vision) — refusing to write a stub ROADMAP`,
602
583
  );
603
- const absPath = resolveRoadmapProjectionPath(basePath, milestoneId);
584
+ const absPath = targetMilestoneFile(basePath, milestoneId, "ROADMAP", milestone.title);
604
585
  if (existsSync(absPath)) {
605
586
  const artifactPath = toArtifactPath(absPath, basePath);
606
587
  unlinkSync(absPath);
@@ -614,7 +595,7 @@ export async function renderRoadmapFromDb(
614
595
  return { skipped: "unplanned-milestone" };
615
596
  }
616
597
 
617
- const absPath = resolveRoadmapProjectionPath(basePath, milestoneId);
598
+ const absPath = targetMilestoneFile(basePath, milestoneId, "ROADMAP", milestone.title);
618
599
  const artifactPath = toArtifactPath(absPath, basePath);
619
600
  const content = renderRoadmapMarkdown(milestone, slices);
620
601
 
@@ -655,25 +636,20 @@ export async function renderMilestoneArtifactsFromDb(
655
636
  const artifacts = getMilestoneScopedArtifacts(milestoneId);
656
637
  if (artifacts.length === 0) return false;
657
638
 
658
- const phaseDir = resolveMilestonePath(basePath, milestoneId) ??
659
- join(
660
- milestonesDir(basePath),
661
- canonicalPhaseDirName(milestoneId, getMilestone(milestoneId)?.title),
662
- );
663
- mkdirSync(phaseDir, { recursive: true });
664
-
665
- const legacyBase = legacyMilestonesDir(basePath);
666
- const isLegacy = phaseDir.startsWith(legacyBase + "/") || phaseDir.startsWith(legacyBase + "\\");
639
+ const milestone = getMilestone(milestoneId);
667
640
 
668
641
  let wrote = false;
669
642
  for (const artifact of artifacts) {
670
643
  if (artifact.artifact_type === "ROADMAP") continue;
671
644
  if (!artifact.full_content.trim()) continue;
672
645
 
673
- const fileName = isLegacy
674
- ? `${milestoneId}-${artifact.artifact_type}.md`
675
- : buildMilestoneFileName(milestoneId, artifact.artifact_type);
676
- const absPath = join(phaseDir, fileName);
646
+ const absPath = targetMilestoneFile(
647
+ basePath,
648
+ milestoneId,
649
+ artifact.artifact_type,
650
+ milestone?.title,
651
+ );
652
+ mkdirSync(dirname(absPath), { recursive: true });
677
653
  const artifactPath = toArtifactPath(absPath, basePath);
678
654
  await writeAndStore(absPath, artifactPath, artifact.full_content, {
679
655
  artifact_type: artifact.artifact_type,
@@ -824,22 +800,12 @@ export async function renderSliceSummary(
824
800
  return false; // No slice data — skip silently
825
801
  }
826
802
 
827
- // Flat-phase: resolve or create the phase dir, since resolveSlicePath only
828
- // finds existing dirs. In flat-phase the slice "path" is the phase dir.
829
- let slicePath = resolveSlicePath(basePath, milestoneId, sliceId);
830
- if (!slicePath) {
831
- const mDir = milestonesDir(basePath);
832
- const dirName = canonicalPhaseDirName(milestoneId, getMilestone(milestoneId)?.title);
833
- slicePath = join(mDir, dirName);
834
- mkdirSync(slicePath, { recursive: true });
835
- }
836
-
803
+ const milestoneTitle = getMilestone(milestoneId)?.title;
837
804
  let wrote = false;
838
805
 
839
- // Write SUMMARY — flat-phase: NN-MM-SUMMARY.md inside phase dir
840
806
  if (slice.full_summary_md) {
841
- const summaryName = planFileName(milestoneIdToPhaseNum(milestoneId), sliceIdToPlanNum(sliceId), "SUMMARY");
842
- const summaryAbs = join(slicePath, summaryName);
807
+ const summaryAbs = targetSliceFile(basePath, milestoneId, sliceId, "SUMMARY", milestoneTitle);
808
+ mkdirSync(dirname(summaryAbs), { recursive: true });
843
809
  const summaryArtifact = toArtifactPath(summaryAbs, basePath);
844
810
 
845
811
  await writeAndStore(summaryAbs, summaryArtifact, slice.full_summary_md, {
@@ -850,10 +816,9 @@ export async function renderSliceSummary(
850
816
  wrote = true;
851
817
  }
852
818
 
853
- // Write UAT — flat-phase: NN-MM-UAT.md inside phase dir
854
819
  if (slice.full_uat_md) {
855
- const uatName = planFileName(milestoneIdToPhaseNum(milestoneId), sliceIdToPlanNum(sliceId), "UAT");
856
- const uatAbs = join(slicePath, uatName);
820
+ const uatAbs = targetSliceFile(basePath, milestoneId, sliceId, "UAT", milestoneTitle);
821
+ mkdirSync(dirname(uatAbs), { recursive: true });
857
822
  const uatArtifact = toArtifactPath(uatAbs, basePath);
858
823
 
859
824
  await writeAndStore(uatAbs, uatArtifact, slice.full_uat_md, {
@@ -1080,7 +1045,7 @@ function detectStaleRendersImpl(basePath: string): StaleEntry[] {
1080
1045
  // TODO(flat-phase): roadmap checkbox parsing may not match flat-phase
1081
1046
  // roadmap format, causing false-positive drift loops. Skip during transition.
1082
1047
  /*
1083
- const roadmapPath = resolveRoadmapProjectionPath(basePath, milestone.id);
1048
+ const roadmapPath = targetMilestoneFile(basePath, milestone.id, "ROADMAP", milestone.title);
1084
1049
  if (existsSync(roadmapPath)) {
1085
1050
  try {
1086
1051
  const parsed = parseProjectionByIdentity(roadmapPath, parseRoadmap) as ReturnType<typeof parseRoadmap>;
@@ -1172,36 +1137,28 @@ function detectStaleRendersImpl(basePath: string): StaleEntry[] {
1172
1137
  }
1173
1138
  }
1174
1139
 
1175
- // Check missing slice summary/UAT files
1140
+ // Check missing slice summary/UAT files. Use the same target helper as
1141
+ // renderSliceSummary so detection and repair agree on flat-phase vs legacy
1142
+ // output locations.
1176
1143
  const sliceRow = getSlice(milestone.id, slice.id);
1177
1144
  if (sliceRow && sliceRow.status === "complete") {
1178
- // Use the SAME path construction as renderSliceSummary (planFileName format)
1179
- // so the detector and repair always agree on the file location.
1180
- const slicePath = resolveSlicePath(basePath, milestone.id, slice.id);
1181
- if (slicePath) {
1182
- const phaseNum = milestoneIdToPhaseNum(milestone.id);
1183
- const planNum = sliceIdToPlanNum(slice.id);
1184
-
1185
- if (sliceRow.full_summary_md) {
1186
- const summaryName = planFileName(phaseNum, planNum, "SUMMARY");
1187
- const summaryAbsPath = join(slicePath, summaryName);
1188
- if (!existsSync(summaryAbsPath)) {
1189
- stale.push({
1190
- path: summaryAbsPath,
1191
- reason: `${slice.id} is complete with summary in DB but SUMMARY.md missing on disk`,
1192
- });
1193
- }
1145
+ if (sliceRow.full_summary_md) {
1146
+ const summaryAbsPath = targetSliceFile(basePath, milestone.id, slice.id, "SUMMARY", milestone.title);
1147
+ if (!existsSync(summaryAbsPath)) {
1148
+ stale.push({
1149
+ path: summaryAbsPath,
1150
+ reason: `${slice.id} is complete with summary in DB but SUMMARY.md missing on disk`,
1151
+ });
1194
1152
  }
1153
+ }
1195
1154
 
1196
- if (sliceRow.full_uat_md) {
1197
- const uatName = planFileName(phaseNum, planNum, "UAT");
1198
- const uatAbsPath = join(slicePath, uatName);
1199
- if (!existsSync(uatAbsPath)) {
1200
- stale.push({
1201
- path: uatAbsPath,
1202
- reason: `${slice.id} is complete with UAT in DB but UAT.md missing on disk`,
1203
- });
1204
- }
1155
+ if (sliceRow.full_uat_md) {
1156
+ const uatAbsPath = targetSliceFile(basePath, milestone.id, slice.id, "UAT", milestone.title);
1157
+ if (!existsSync(uatAbsPath)) {
1158
+ stale.push({
1159
+ path: uatAbsPath,
1160
+ reason: `${slice.id} is complete with UAT in DB but UAT.md missing on disk`,
1161
+ });
1205
1162
  }
1206
1163
  }
1207
1164
  }
@@ -1257,12 +1214,14 @@ export async function renderReplanFromDb(
1257
1214
  sliceId: string,
1258
1215
  replanData: ReplanData,
1259
1216
  ): Promise<{ replanPath: string; content: string }> {
1260
- // Flat-phase: replan file lives in the phase dir
1261
- const slicePath = resolveSlicePath(basePath, milestoneId, sliceId)
1262
- ?? join(milestonesDir(basePath), canonicalPhaseDirName(milestoneId, getMilestone(milestoneId)?.title));
1263
- const phaseNum = milestoneIdToPhaseNum(milestoneId);
1264
- const planNum = sliceIdToPlanNum(sliceId);
1265
- const absPath = join(slicePath, planFileName(phaseNum, planNum, "REPLAN"));
1217
+ const absPath = targetSliceFile(
1218
+ basePath,
1219
+ milestoneId,
1220
+ sliceId,
1221
+ "REPLAN",
1222
+ getMilestone(milestoneId)?.title,
1223
+ );
1224
+ mkdirSync(dirname(absPath), { recursive: true });
1266
1225
  const artifactPath = toArtifactPath(absPath, basePath);
1267
1226
 
1268
1227
  const lines: string[] = [];
@@ -1299,24 +1258,14 @@ export async function renderAssessmentFromDb(
1299
1258
  sliceId: string,
1300
1259
  assessmentData: AssessmentData,
1301
1260
  ): Promise<{ assessmentPath: string; content: string }> {
1302
- // Flat-phase: the assessment file lives in the phase dir, NOT in a slices/SID/
1303
- // subdir. resolveSlicePath() detects a slices/SID/ dir unconditionally, so a
1304
- // stray slices/SID/ created by an earlier planning step would send this write
1305
- // to a hybrid path (wrong dir, flat filename) that verification cannot find.
1306
- // Guard with the same legacy-base check relSlicePath() uses so flat-phase
1307
- // milestones always target the phase dir.
1308
- const mDir = resolveMilestonePath(basePath, milestoneId);
1309
- const legacyBase = legacyMilestonesDir(basePath);
1310
- const isLegacyLayout = mDir
1311
- ? mDir.startsWith(legacyBase + "/") || mDir.startsWith(legacyBase + "\\")
1312
- : false;
1313
- const fallbackDir = join(milestonesDir(basePath), canonicalPhaseDirName(milestoneId, getMilestone(milestoneId)?.title));
1314
- const slicePath = isLegacyLayout
1315
- ? (resolveSlicePath(basePath, milestoneId, sliceId) ?? fallbackDir)
1316
- : (mDir ?? fallbackDir);
1317
- const phaseNum = milestoneIdToPhaseNum(milestoneId);
1318
- const planNum = sliceIdToPlanNum(sliceId);
1319
- const absPath = join(slicePath, planFileName(phaseNum, planNum, "ASSESSMENT"));
1261
+ const absPath = targetSliceFile(
1262
+ basePath,
1263
+ milestoneId,
1264
+ sliceId,
1265
+ "ASSESSMENT",
1266
+ getMilestone(milestoneId)?.title,
1267
+ );
1268
+ mkdirSync(dirname(absPath), { recursive: true });
1320
1269
  const artifactPath = toArtifactPath(absPath, basePath);
1321
1270
 
1322
1271
  const lines: string[] = [];
@@ -265,7 +265,7 @@ export function snapshotUnitMetrics(
265
265
  }
266
266
  saveLedger(basePath, ledger);
267
267
 
268
- if (isUnifiedAuditEnabled()) {
268
+ if (isUnifiedAuditEnabled(basePath)) {
269
269
  emitUokAuditEvent(
270
270
  basePath,
271
271
  buildAuditEnvelope({
@@ -458,7 +458,7 @@ export function snapshotUnitMetricsByScope(
458
458
  }
459
459
  saveLedger(base, scopedLedger);
460
460
 
461
- if (isUnifiedAuditEnabled()) {
461
+ if (isUnifiedAuditEnabled(base)) {
462
462
  emitUokAuditEvent(
463
463
  base,
464
464
  buildAuditEnvelope({
@@ -11,7 +11,7 @@
11
11
  */
12
12
 
13
13
  import { readdirSync, existsSync, realpathSync, statSync, Dirent } from "node:fs";
14
- import { join, dirname, normalize, resolve } from "node:path";
14
+ import { join, dirname, normalize, relative, resolve } from "node:path";
15
15
  import { homedir } from "node:os";
16
16
  import { spawnSync } from "node:child_process";
17
17
  import { nativeScanGsdTree, type GsdTreeEntry } from "./native-parser-bridge.js";
@@ -379,21 +379,26 @@ export function resolveGsdPathContract(
379
379
  const resolvedWorkRoot = resolve(workRoot || process.cwd());
380
380
  const isWorktree = isGsdWorktreePath(resolvedWorkRoot);
381
381
  if (isWorktree && !originalProjectRoot?.trim()) {
382
- const projectGsd = resolveExternalStateProjectGsdFromWorktreePath(resolvedWorkRoot);
383
- if (projectGsd) {
382
+ const rawProjectGsd = resolveExternalStateProjectGsdFromWorktreePath(resolvedWorkRoot);
383
+ if (rawProjectGsd) {
384
+ // Canonicalize the `.gsd` root so the DB path matches what every other
385
+ // accessor (gsdRoot/gsdProjectionRoot) returns — otherwise an unresolved
386
+ // symlink (e.g. `/mnt/c/.../.gsd` → native ext4 on WSL) selects the wrong
387
+ // journal mode and yields a fragile, move-prone handle. See issue #1239.
388
+ const projectGsd = normalizeRealPath(rawProjectGsd);
384
389
  return {
385
390
  projectRoot: dirname(dirname(projectGsd)),
386
391
  workRoot: resolvedWorkRoot,
387
392
  projectGsd,
388
- worktreeGsd: join(resolvedWorkRoot, ".gsd"),
393
+ worktreeGsd: normalizeRealPath(join(resolvedWorkRoot, ".gsd")),
389
394
  projectDb: join(projectGsd, "gsd.db"),
390
395
  isWorktree,
391
396
  };
392
397
  }
393
398
  }
394
399
  const projectRoot = resolve(resolveWorktreeProjectRoot(resolvedWorkRoot, originalProjectRoot));
395
- const projectGsd = join(projectRoot, ".gsd");
396
- const worktreeGsd = isWorktree ? join(resolvedWorkRoot, ".gsd") : null;
400
+ const projectGsd = normalizeRealPath(join(projectRoot, ".gsd"));
401
+ const worktreeGsd = isWorktree ? normalizeRealPath(join(resolvedWorkRoot, ".gsd")) : null;
397
402
 
398
403
  return {
399
404
  projectRoot,
@@ -916,10 +921,24 @@ export function resolveTaskFile(
916
921
  basePath: string, milestoneId: string, sliceId: string,
917
922
  taskId: string, suffix: string
918
923
  ): string | null {
924
+ const phaseDir = resolveMilestonePath(basePath, milestoneId);
925
+ if (!phaseDir) return null;
926
+
927
+ const legacyBase = legacyMilestonesDir(basePath);
928
+ const isLegacy = phaseDir.startsWith(legacyBase + "/") || phaseDir.startsWith(legacyBase + "\\");
929
+
930
+ if (suffix !== "PLAN" && !isLegacy) {
931
+ const flatPath = join(phaseDir, buildTaskFileName(taskId, suffix));
932
+ return existsSync(flatPath) ? flatPath : null;
933
+ }
934
+
919
935
  const tDir = resolveTasksDir(basePath, milestoneId, sliceId);
920
- if (!tDir) return null;
921
- const file = resolveFile(tDir, taskId, suffix);
922
- return file ? join(tDir, file) : null;
936
+ if (tDir) {
937
+ const file = resolveFile(tDir, taskId, suffix);
938
+ if (file) return join(tDir, file);
939
+ }
940
+
941
+ return null;
923
942
  }
924
943
 
925
944
  // ─── Relative Path Builders (for prompts — .gsd/milestones/...) ────────────
@@ -954,31 +973,41 @@ export function relMilestonePath(basePath: string, milestoneId: string, title?:
954
973
  }
955
974
 
956
975
  /**
957
- * Build relative .gsd/ path to a milestone file.
958
- * Layout-aware: uses resolveMilestoneFile to find NN-SUFFIX.md (flat-phase)
959
- * or M001-SUFFIX.md (legacy). Falls back to a layout-appropriate canonical
960
- * name when the file doesn't exist yet.
976
+ * Build the canonical absolute write target for a milestone file.
977
+ * Existing compatibility filenames are intentionally ignored; readers that need
978
+ * to preserve an existing file should call resolveMilestoneFile first.
961
979
  */
962
- export function relMilestoneFile(
963
- basePath: string, milestoneId: string, suffix: string
980
+ export function targetMilestoneFile(
981
+ basePath: string, milestoneId: string, suffix: string, title?: string
964
982
  ): string {
965
- const mRel = relMilestonePath(basePath, milestoneId);
966
- // resolveMilestoneFile checks NN-SUFFIX.md (flat-phase) and MID-SUFFIX.md (legacy).
967
- const absFile = resolveMilestoneFile(basePath, milestoneId, suffix);
968
- if (absFile) {
969
- const fileName = absFile.split(/[/\\]/).pop()!;
970
- return `${mRel}/${fileName}`;
971
- }
972
- // File doesn't exist yet — use layout-aware fallback filename.
973
983
  const mDir = resolveMilestonePath(basePath, milestoneId);
974
984
  const legacyBase = legacyMilestonesDir(basePath);
975
985
  const isLegacy = mDir
976
986
  ? mDir.startsWith(legacyBase + "/") || mDir.startsWith(legacyBase + "\\")
977
- : false;
987
+ : isLegacyMilestonesLayout(basePath);
988
+ const dir = mDir ?? join(
989
+ isLegacy ? legacyBase : milestonesDir(basePath),
990
+ isLegacy ? milestoneId : canonicalPhaseDirName(milestoneId, title),
991
+ );
978
992
  const fileName = isLegacy
979
993
  ? `${milestoneId}-${suffix}.md`
980
994
  : `${String(milestoneIdToPhaseNum(milestoneId)).padStart(2, "0")}-${suffix}.md`;
981
- return `${mRel}/${fileName}`;
995
+ return join(dir, fileName);
996
+ }
997
+
998
+ /**
999
+ * Build relative .gsd/ path to a milestone file.
1000
+ * Preserves an existing compatibility filename when present; otherwise falls
1001
+ * back to the canonical write target.
1002
+ */
1003
+ export function relMilestoneFile(
1004
+ basePath: string, milestoneId: string, suffix: string, title?: string
1005
+ ): string {
1006
+ const rel = relative(
1007
+ gsdProjectionRoot(basePath),
1008
+ resolveMilestoneFile(basePath, milestoneId, suffix) ?? targetMilestoneFile(basePath, milestoneId, suffix, title),
1009
+ ).replace(/\\/g, "/");
1010
+ return `.gsd/${rel}`;
982
1011
  }
983
1012
 
984
1013
  /**
@@ -1010,34 +1039,44 @@ export function relSlicePath(
1010
1039
  }
1011
1040
 
1012
1041
  /**
1013
- * Build relative .gsd/ path to a slice file.
1014
- * Layout-aware: legacy uses S01-SUFFIX.md; flat-phase uses NN-MM-SUFFIX.md.
1015
- *
1016
- * @param milestoneTitle - Optional milestone title forwarded to relSlicePath /
1017
- * relMilestonePath for title-aware flat-phase fallback dir naming. Only used
1018
- * when the phase directory does not yet exist on disk.
1042
+ * Build the canonical absolute write target for a slice file.
1043
+ * Existing compatibility filenames are intentionally ignored; readers that need
1044
+ * to preserve an existing file should call resolveSliceFile first.
1019
1045
  */
1020
- export function relSliceFile(
1046
+ export function targetSliceFile(
1021
1047
  basePath: string, milestoneId: string, sliceId: string, suffix: string, milestoneTitle?: string
1022
1048
  ): string {
1023
- const sRel = relSlicePath(basePath, milestoneId, sliceId, milestoneTitle);
1024
- const absPath = resolveSliceFile(basePath, milestoneId, sliceId, suffix);
1025
- if (absPath) {
1026
- const fileName = absPath.split(/[/\\]/).pop()!;
1027
- return `${sRel}/${fileName}`;
1028
- }
1029
- // Fallback when file doesn't exist yet — use layout-aware filename.
1030
1049
  const mDir = resolveMilestonePath(basePath, milestoneId);
1031
1050
  const legacyBase = legacyMilestonesDir(basePath);
1032
1051
  const isLegacy = mDir
1033
1052
  ? mDir.startsWith(legacyBase + "/") || mDir.startsWith(legacyBase + "\\")
1034
- : false;
1035
- if (isLegacy) {
1036
- return `${sRel}/${sliceId}-${suffix}.md`;
1037
- }
1038
- const phaseNum = milestoneIdToPhaseNum(milestoneId);
1039
- const planNum = sliceIdToPlanNum(sliceId);
1040
- return `${sRel}/${planFileName(phaseNum, planNum, suffix)}`;
1053
+ : isLegacyMilestonesLayout(basePath);
1054
+ const milestoneDir = mDir
1055
+ ?? dirname(targetMilestoneFile(basePath, milestoneId, "ROADMAP", milestoneTitle));
1056
+ const slicesDir = join(milestoneDir, "slices");
1057
+ const dir = isLegacy
1058
+ ? join(slicesDir, resolveDir(slicesDir, sliceId) ?? sliceId)
1059
+ : milestoneDir;
1060
+ const fileName = isLegacy
1061
+ ? `${sliceId}-${suffix}.md`
1062
+ : planFileName(milestoneIdToPhaseNum(milestoneId), sliceIdToPlanNum(sliceId), suffix);
1063
+ return join(dir, fileName);
1064
+ }
1065
+
1066
+ /**
1067
+ * Build relative .gsd/ path to a slice file.
1068
+ * Preserves an existing compatibility filename when present; otherwise falls
1069
+ * back to the canonical write target.
1070
+ */
1071
+ export function relSliceFile(
1072
+ basePath: string, milestoneId: string, sliceId: string, suffix: string, milestoneTitle?: string
1073
+ ): string {
1074
+ const rel = relative(
1075
+ gsdProjectionRoot(basePath),
1076
+ resolveSliceFile(basePath, milestoneId, sliceId, suffix)
1077
+ ?? targetSliceFile(basePath, milestoneId, sliceId, suffix, milestoneTitle),
1078
+ ).replace(/\\/g, "/");
1079
+ return `.gsd/${rel}`;
1041
1080
  }
1042
1081
 
1043
1082
  /**
@@ -1049,7 +1088,7 @@ export function relSliceFile(
1049
1088
  */
1050
1089
  export function relTaskFile(
1051
1090
  basePath: string, milestoneId: string, sliceId: string,
1052
- taskId: string, suffix: string
1091
+ taskId: string, suffix: string, milestoneTitle?: string
1053
1092
  ): string {
1054
1093
  const sDir = resolveSlicePath(basePath, milestoneId, sliceId);
1055
1094
  const phaseDir = resolveMilestonePath(basePath, milestoneId);
@@ -1062,6 +1101,6 @@ export function relTaskFile(
1062
1101
  if (suffix === "PLAN") {
1063
1102
  return relSliceFile(basePath, milestoneId, sliceId, "PLAN");
1064
1103
  }
1065
- const relS = relSlicePath(basePath, milestoneId, sliceId);
1104
+ const relS = relSlicePath(basePath, milestoneId, sliceId, milestoneTitle);
1066
1105
  return `${relS}/${buildTaskFileName(taskId, suffix)}`;
1067
1106
  }
@@ -11,6 +11,7 @@ import type {
11
11
  HookStatusEntry,
12
12
  PostUnitGateBlock,
13
13
  } from "./types.js";
14
+ import type { SidecarItem } from "./auto/session.js";
14
15
  import { getOrCreateRegistry, resolveHookArtifactPath } from "./rule-registry.js";
15
16
 
16
17
  // Re-export resolveHookArtifactPath so existing importers still work.
@@ -75,6 +76,37 @@ export function restoreHookState(basePath: string): void {
75
76
  getOrCreateRegistry().restoreState(basePath);
76
77
  }
77
78
 
79
+ /**
80
+ * Reconcile a restored `activeHook` against the session's sidecar queue.
81
+ *
82
+ * The registry persists `activeHook` to disk but the pending hook *dispatch*
83
+ * lives only on the non-persisted `s.sidecarQueue`. After a pause/resume or
84
+ * crash-recovery, `restoreHookState` re-hydrates `activeHook` while the dispatch
85
+ * is gone, so the registry believes a hook is in-flight but nothing will ever
86
+ * complete it — and the next unrelated unit's close-out is falsely charged
87
+ * against the stale hook and blocked. Re-enqueue the missing dispatch so the
88
+ * hook actually runs, restoring the invariant that a persisted `activeHook`
89
+ * always has a live dispatch (#1246).
90
+ */
91
+ export function reconcileRestoredHookDispatch(
92
+ basePath: string,
93
+ sidecarQueue: SidecarItem[],
94
+ ): void {
95
+ const dispatch = getOrCreateRegistry().getPendingHookDispatch(basePath);
96
+ if (!dispatch) return;
97
+ const alreadyQueued = sidecarQueue.some(
98
+ item => item.kind === "hook" && item.unitType === dispatch.unitType,
99
+ );
100
+ if (alreadyQueued) return;
101
+ sidecarQueue.push({
102
+ kind: "hook",
103
+ unitType: dispatch.unitType,
104
+ unitId: dispatch.unitId,
105
+ prompt: dispatch.prompt,
106
+ model: dispatch.model,
107
+ });
108
+ }
109
+
78
110
  export function clearPersistedHookState(basePath: string): void {
79
111
  getOrCreateRegistry().clearPersistedState(basePath);
80
112
  }