@opengsd/gsd-pi 1.1.1-dev.9f86580 → 1.1.1-dev.b2556262

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 (261) hide show
  1. package/dist/headless-recover.js +56 -1
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/browser-tools/index.js +39 -22
  4. package/dist/resources/extensions/browser-tools/state.js +12 -0
  5. package/dist/resources/extensions/browser-tools/tools/session.js +3 -2
  6. package/dist/resources/extensions/browser-tools/utils.js +3 -3
  7. package/dist/resources/extensions/gsd/auto/loop.js +4 -2
  8. package/dist/resources/extensions/gsd/auto/phases.js +43 -10
  9. package/dist/resources/extensions/gsd/auto/session.js +20 -1
  10. package/dist/resources/extensions/gsd/auto/workflow-kernel.js +1 -0
  11. package/dist/resources/extensions/gsd/auto-dispatch.js +72 -12
  12. package/dist/resources/extensions/gsd/auto-model-selection.js +128 -9
  13. package/dist/resources/extensions/gsd/auto-post-unit.js +19 -2
  14. package/dist/resources/extensions/gsd/auto-prompts.js +24 -19
  15. package/dist/resources/extensions/gsd/auto-recovery.js +4 -2
  16. package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
  17. package/dist/resources/extensions/gsd/auto-start.js +1 -1
  18. package/dist/resources/extensions/gsd/auto.js +14 -11
  19. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
  20. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +172 -65
  21. package/dist/resources/extensions/gsd/closeout-wizard.js +32 -9
  22. package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -9
  23. package/dist/resources/extensions/gsd/commands-maintenance.js +93 -15
  24. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +2 -2
  25. package/dist/resources/extensions/gsd/db-writer.js +35 -0
  26. package/dist/resources/extensions/gsd/docs/preferences-reference.md +50 -1
  27. package/dist/resources/extensions/gsd/gsd-db.js +480 -172
  28. package/dist/resources/extensions/gsd/markdown-renderer.js +37 -53
  29. package/dist/resources/extensions/gsd/md-importer.js +38 -3
  30. package/dist/resources/extensions/gsd/migration-auto-check.js +126 -31
  31. package/dist/resources/extensions/gsd/parsers-legacy.js +23 -0
  32. package/dist/resources/extensions/gsd/planning-path-scope.js +22 -4
  33. package/dist/resources/extensions/gsd/pre-execution-checks.js +10 -2
  34. package/dist/resources/extensions/gsd/preferences-models.js +110 -43
  35. package/dist/resources/extensions/gsd/preferences-types.js +13 -0
  36. package/dist/resources/extensions/gsd/preferences-validation.js +68 -3
  37. package/dist/resources/extensions/gsd/preferences.js +4 -1
  38. package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  39. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  40. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  41. package/dist/resources/extensions/gsd/roadmap-slices.js +5 -1
  42. package/dist/resources/extensions/gsd/safety/content-validator.js +6 -4
  43. package/dist/resources/extensions/gsd/source-observations.js +306 -0
  44. package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +15 -8
  45. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +33 -5
  46. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-worker.js +34 -13
  47. package/dist/resources/extensions/gsd/state-reconciliation/index.js +39 -14
  48. package/dist/resources/extensions/gsd/state-reconciliation/spawn-gate.js +4 -4
  49. package/dist/resources/extensions/gsd/state.js +7 -3
  50. package/dist/resources/extensions/gsd/tool-contract.js +14 -0
  51. package/dist/resources/extensions/gsd/tool-presentation-plan.js +1 -9
  52. package/dist/resources/extensions/gsd/tools/complete-slice.js +7 -6
  53. package/dist/resources/extensions/gsd/tools/plan-slice.js +42 -11
  54. package/dist/resources/extensions/gsd/tools/plan-task.js +7 -1
  55. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +57 -429
  56. package/dist/resources/extensions/gsd/uat-policy.js +130 -0
  57. package/dist/resources/extensions/gsd/uat-run.js +414 -0
  58. package/dist/resources/extensions/gsd/unit-context-manifest.js +3 -4
  59. package/dist/resources/extensions/gsd/verdict-parser.js +3 -8
  60. package/dist/resources/extensions/gsd/workflow-manifest.js +132 -5
  61. package/dist/resources/extensions/gsd/workflow-projections.js +8 -0
  62. package/dist/resources/extensions/gsd/worktree-state-projection.js +18 -17
  63. package/dist/resources/extensions/subagent/agents.js +1 -0
  64. package/dist/resources/extensions/subagent/index.js +27 -12
  65. package/dist/resources/extensions/subagent/launch.js +7 -2
  66. package/dist/web/standalone/.next/BUILD_ID +1 -1
  67. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  68. package/dist/web/standalone/.next/build-manifest.json +2 -2
  69. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  70. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/index.html +1 -1
  87. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  94. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  95. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  97. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  98. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  99. package/dist/web/standalone/node_modules/@gsd/native/dist/native.js +22 -0
  100. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  101. package/package.json +4 -4
  102. package/packages/cloud-mcp-gateway/package.json +2 -2
  103. package/packages/contracts/package.json +1 -1
  104. package/packages/daemon/package.json +4 -4
  105. package/packages/gsd-agent-core/package.json +5 -5
  106. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  107. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js +21 -23
  108. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js.map +1 -1
  109. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +3 -0
  110. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  111. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -0
  112. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  113. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts +1 -0
  114. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  115. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +66 -12
  116. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  117. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  118. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +18 -11
  119. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  120. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
  121. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +16 -0
  122. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
  123. package/packages/gsd-agent-modes/package.json +7 -7
  124. package/packages/mcp-server/dist/workflow-tools.js +1 -1
  125. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  126. package/packages/mcp-server/package.json +3 -3
  127. package/packages/native/dist/native.js +22 -0
  128. package/packages/native/package.json +1 -1
  129. package/packages/pi-agent-core/package.json +1 -1
  130. package/packages/pi-ai/dist/image-models.generated.d.ts +30 -0
  131. package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  132. package/packages/pi-ai/dist/image-models.generated.js +30 -0
  133. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  134. package/packages/pi-ai/dist/models.generated.d.ts +23 -17
  135. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  136. package/packages/pi-ai/dist/models.generated.js +25 -24
  137. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  138. package/packages/pi-ai/package.json +1 -1
  139. package/packages/pi-coding-agent/dist/core/settings-manager.js +1 -1
  140. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  141. package/packages/pi-coding-agent/dist/theme/themes.js +1 -1
  142. package/packages/pi-coding-agent/dist/theme/themes.js.map +1 -1
  143. package/packages/pi-coding-agent/package.json +7 -7
  144. package/packages/pi-tui/dist/utils.d.ts +11 -0
  145. package/packages/pi-tui/dist/utils.d.ts.map +1 -1
  146. package/packages/pi-tui/dist/utils.js +119 -6
  147. package/packages/pi-tui/dist/utils.js.map +1 -1
  148. package/packages/pi-tui/package.json +2 -1
  149. package/packages/rpc-client/package.json +2 -2
  150. package/pkg/dist/theme/themes.js +1 -1
  151. package/pkg/dist/theme/themes.js.map +1 -1
  152. package/pkg/package.json +1 -1
  153. package/src/resources/extensions/browser-tools/index.ts +39 -22
  154. package/src/resources/extensions/browser-tools/state.ts +13 -0
  155. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +57 -0
  156. package/src/resources/extensions/browser-tools/tools/session.ts +4 -2
  157. package/src/resources/extensions/browser-tools/utils.ts +3 -3
  158. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  159. package/src/resources/extensions/gsd/auto/loop.ts +4 -2
  160. package/src/resources/extensions/gsd/auto/phases.ts +42 -10
  161. package/src/resources/extensions/gsd/auto/session.ts +22 -1
  162. package/src/resources/extensions/gsd/auto/workflow-kernel.ts +1 -0
  163. package/src/resources/extensions/gsd/auto-dispatch.ts +85 -12
  164. package/src/resources/extensions/gsd/auto-model-selection.ts +164 -12
  165. package/src/resources/extensions/gsd/auto-post-unit.ts +20 -2
  166. package/src/resources/extensions/gsd/auto-prompts.ts +23 -20
  167. package/src/resources/extensions/gsd/auto-recovery.ts +22 -3
  168. package/src/resources/extensions/gsd/auto-runtime-state.ts +5 -0
  169. package/src/resources/extensions/gsd/auto-start.ts +1 -1
  170. package/src/resources/extensions/gsd/auto.ts +13 -10
  171. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
  172. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +225 -72
  173. package/src/resources/extensions/gsd/closeout-wizard.ts +47 -13
  174. package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -17
  175. package/src/resources/extensions/gsd/commands-maintenance.ts +124 -13
  176. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +2 -2
  177. package/src/resources/extensions/gsd/db-writer.ts +38 -0
  178. package/src/resources/extensions/gsd/docs/preferences-reference.md +50 -1
  179. package/src/resources/extensions/gsd/gsd-db.ts +564 -186
  180. package/src/resources/extensions/gsd/markdown-renderer.ts +44 -66
  181. package/src/resources/extensions/gsd/md-importer.ts +49 -2
  182. package/src/resources/extensions/gsd/migration-auto-check.ts +154 -34
  183. package/src/resources/extensions/gsd/parsers-legacy.ts +20 -0
  184. package/src/resources/extensions/gsd/planning-path-scope.ts +22 -4
  185. package/src/resources/extensions/gsd/pre-execution-checks.ts +9 -2
  186. package/src/resources/extensions/gsd/preferences-models.ts +112 -43
  187. package/src/resources/extensions/gsd/preferences-types.ts +39 -0
  188. package/src/resources/extensions/gsd/preferences-validation.ts +76 -2
  189. package/src/resources/extensions/gsd/preferences.ts +5 -0
  190. package/src/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  191. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  192. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  193. package/src/resources/extensions/gsd/roadmap-slices.ts +6 -1
  194. package/src/resources/extensions/gsd/safety/content-validator.ts +8 -5
  195. package/src/resources/extensions/gsd/source-observations.ts +402 -0
  196. package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +20 -8
  197. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +44 -5
  198. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-worker.ts +39 -11
  199. package/src/resources/extensions/gsd/state-reconciliation/index.ts +45 -15
  200. package/src/resources/extensions/gsd/state-reconciliation/spawn-gate.ts +4 -4
  201. package/src/resources/extensions/gsd/state.ts +7 -4
  202. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +15 -0
  203. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +299 -1
  204. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +32 -0
  205. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +75 -3
  206. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +22 -1
  207. package/src/resources/extensions/gsd/tests/before-provider-context-management.test.ts +145 -0
  208. package/src/resources/extensions/gsd/tests/closeout-wizard.test.ts +44 -0
  209. package/src/resources/extensions/gsd/tests/commands-dispatcher-unmerged-milestone.test.ts +26 -1
  210. package/src/resources/extensions/gsd/tests/content-validator.test.ts +74 -0
  211. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +16 -2
  212. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +1 -11
  213. package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +64 -0
  214. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +15 -0
  215. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +62 -1
  216. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +15 -0
  217. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +42 -0
  218. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +99 -0
  219. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +99 -2
  220. package/src/resources/extensions/gsd/tests/plan-task.test.ts +19 -0
  221. package/src/resources/extensions/gsd/tests/preferences.test.ts +14 -0
  222. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +1 -0
  223. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +9 -0
  224. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +101 -1
  225. package/src/resources/extensions/gsd/tests/repository-registry.test.ts +2 -2
  226. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +8 -0
  227. package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +5 -3
  228. package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +162 -18
  229. package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +8 -0
  230. package/src/resources/extensions/gsd/tests/source-observations.test.ts +275 -0
  231. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +43 -0
  232. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +76 -21
  233. package/src/resources/extensions/gsd/tests/thinking-level-resolution.test.ts +203 -0
  234. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +170 -0
  235. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +7 -1
  236. package/src/resources/extensions/gsd/tests/workflow-kernel.test.ts +7 -0
  237. package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +306 -1
  238. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +73 -6
  239. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +511 -1
  240. package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +44 -0
  241. package/src/resources/extensions/gsd/tool-contract.ts +28 -0
  242. package/src/resources/extensions/gsd/tool-presentation-plan.ts +1 -11
  243. package/src/resources/extensions/gsd/tools/complete-slice.ts +7 -6
  244. package/src/resources/extensions/gsd/tools/plan-slice.ts +54 -12
  245. package/src/resources/extensions/gsd/tools/plan-task.ts +8 -1
  246. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +66 -526
  247. package/src/resources/extensions/gsd/types.ts +1 -0
  248. package/src/resources/extensions/gsd/uat-policy.ts +191 -0
  249. package/src/resources/extensions/gsd/uat-run.ts +550 -0
  250. package/src/resources/extensions/gsd/unit-context-manifest.ts +3 -4
  251. package/src/resources/extensions/gsd/verdict-parser.ts +3 -10
  252. package/src/resources/extensions/gsd/workflow-manifest.ts +193 -7
  253. package/src/resources/extensions/gsd/workflow-projections.ts +9 -0
  254. package/src/resources/extensions/gsd/worktree-state-projection.ts +22 -22
  255. package/src/resources/extensions/shared/tests/format-utils.test.ts +8 -3
  256. package/src/resources/extensions/subagent/agents.ts +4 -0
  257. package/src/resources/extensions/subagent/index.ts +28 -3
  258. package/src/resources/extensions/subagent/launch.ts +8 -0
  259. package/src/resources/extensions/subagent/tests/model-override.test.ts +31 -0
  260. /package/dist/web/standalone/.next/static/{zzYMrKpPGfRQRxSFO32Jr → tJOKQbQRO-9MiFDO8DIDS}/_buildManifest.js +0 -0
  261. /package/dist/web/standalone/.next/static/{zzYMrKpPGfRQRxSFO32Jr → tJOKQbQRO-9MiFDO8DIDS}/_ssgManifest.js +0 -0
@@ -46,13 +46,20 @@ async function loadExtensionModules() {
46
46
  const dbModule = await jiti.import(gsdExtensionPath('gsd-db.ts'), {});
47
47
  const importerModule = await jiti.import(gsdExtensionPath('md-importer.ts'), {});
48
48
  const dynamicToolsModule = await jiti.import(gsdExtensionPath('bootstrap/dynamic-tools.ts'), {});
49
+ const migrationCheckModule = await jiti.import(gsdExtensionPath('migration-auto-check.ts'), {});
50
+ const rendererModule = await jiti.import(gsdExtensionPath('markdown-renderer.ts'), {});
49
51
  return {
50
52
  ensureDbOpen: dynamicToolsModule.ensureDbOpen,
51
53
  isDbAvailable: dbModule.isDbAvailable,
52
54
  clearEngineHierarchy: dbModule.clearEngineHierarchy,
53
55
  transaction: dbModule.transaction,
56
+ backupDatabaseSnapshot: dbModule.backupDatabaseSnapshot,
54
57
  migrateHierarchyToDb: importerModule.migrateHierarchyToDb,
55
58
  invalidateStateCache: stateModule.invalidateStateCache,
59
+ countDbHierarchy: migrationCheckModule.countDbHierarchy,
60
+ countMarkdownHierarchy: migrationCheckModule.countMarkdownHierarchy,
61
+ recoverWouldDeleteDbRows: migrationCheckModule.recoverWouldDeleteDbRows,
62
+ renderAllFromDb: rendererModule.renderAllFromDb,
56
63
  };
57
64
  }
58
65
  export async function handleRecover(basePath) {
@@ -75,6 +82,24 @@ export async function handleRecover(basePath) {
75
82
  process.stderr.write(`[headless] recover: failed to open or create the GSD database at ${basePath}\n`);
76
83
  return { exitCode: 1 };
77
84
  }
85
+ // Refuse a destructive recover that would delete authoritative DB rows the
86
+ // markdown lacks, unless explicitly allowed. The DB is the source of truth;
87
+ // re-projecting via rebuild is almost always what's wanted instead. The
88
+ // check is identity-based, so it also catches equal-count divergence (DB S99
89
+ // vs markdown S01) that a cardinality-only comparison would miss.
90
+ const markdown = modules.countMarkdownHierarchy(basePath);
91
+ const beforeDb = modules.countDbHierarchy();
92
+ const dataLoss = modules.recoverWouldDeleteDbRows(basePath);
93
+ const allowDataLoss = process.env.GSD_RECOVER_ALLOW_DATA_LOSS === '1';
94
+ if (dataLoss && !allowDataLoss) {
95
+ process.stderr.write(`[headless] recover refused: the DB (${beforeDb.milestones}M/${beforeDb.slices}S/${beforeDb.tasks}T) ` +
96
+ `holds rows the markdown (${markdown.milestones}M/${markdown.slices}S/${markdown.tasks}T) lacks; ` +
97
+ `recover would delete them. Set GSD_RECOVER_ALLOW_DATA_LOSS=1 to override, ` +
98
+ `or run a DB-to-markdown rebuild instead.\n`);
99
+ return { exitCode: 1 };
100
+ }
101
+ // Snapshot the DB before the destructive clear so recover is reversible.
102
+ const backupPath = modules.backupDatabaseSnapshot('pre-recover');
78
103
  let counts;
79
104
  try {
80
105
  counts = modules.transaction(() => {
@@ -88,6 +113,36 @@ export async function handleRecover(basePath) {
88
113
  return { exitCode: 1 };
89
114
  }
90
115
  modules.invalidateStateCache();
91
- process.stderr.write(`gsd-recover: recovered ${counts.milestones}M/${counts.slices}S/${counts.tasks}T hierarchy\n`);
116
+ // Re-project markdown from the freshly imported DB so disk and DB agree.
117
+ // renderAllFromDb resolves even when individual artifacts fail, so inspect
118
+ // its error list — a silent projection failure must surface, not pass as a
119
+ // clean recover.
120
+ let projectionErrors = 0;
121
+ try {
122
+ const renderResult = await modules.renderAllFromDb(basePath);
123
+ projectionErrors = renderResult.errors.length;
124
+ if (projectionErrors > 0) {
125
+ process.stderr.write(`[headless] recover: ${projectionErrors} markdown projection(s) failed to render — markdown may be stale:\n`);
126
+ for (const e of renderResult.errors.slice(0, 10)) {
127
+ process.stderr.write(` - ${e}\n`);
128
+ }
129
+ }
130
+ }
131
+ catch (err) {
132
+ const msg = err instanceof Error ? err.message : String(err);
133
+ process.stderr.write(`[headless] recover: re-render after import failed: ${msg}\n`);
134
+ projectionErrors = 1;
135
+ }
136
+ if (counts.milestones < markdown.milestones ||
137
+ counts.slices < markdown.slices ||
138
+ counts.tasks < markdown.tasks) {
139
+ process.stderr.write(`[headless] recover: imported fewer rows than markdown contained ` +
140
+ `(${markdown.milestones}M/${markdown.slices}S/${markdown.tasks}T on disk); some markdown may have failed to parse\n`);
141
+ }
142
+ if (backupPath) {
143
+ process.stderr.write(`[headless] recover: pre-recover snapshot saved to ${backupPath}\n`);
144
+ }
145
+ process.stderr.write(`gsd-recover: recovered ${counts.milestones}M/${counts.slices}S/${counts.tasks}T hierarchy` +
146
+ `${projectionErrors > 0 ? ` (${projectionErrors} projection error(s) — run rebuild markdown)` : ''}\n`);
92
147
  return { exitCode: 0 };
93
148
  }
@@ -1 +1 @@
1
- f692671bcb7f8bc4
1
+ fc4ebdcd3723ade9
@@ -2,6 +2,7 @@
2
2
  import { importExtensionModule } from "@gsd/pi-coding-agent";
3
3
  import { closeManagedGsdBrowser, registerManagedGsdBrowserTools, warmUpManagedGsdBrowser } from "./engine/managed-gsd-browser.js";
4
4
  import { resolveBrowserEngineMode } from "./engine/selection.js";
5
+ import { setArtifactRootForCwd } from "./state.js";
5
6
  import { detectWebApp } from "./web-app-detect.js";
6
7
  let legacyRegistrationPromise = null;
7
8
  let managedRegistrationPromise = null;
@@ -84,28 +85,29 @@ async function registerLegacyBrowserTools(pi) {
84
85
  sanitizeArtifactName: utils.sanitizeArtifactName,
85
86
  formatArtifactTimestamp: utils.formatArtifactTimestamp,
86
87
  };
87
- navigation.registerNavigationTools(pi, deps);
88
- screenshot.registerScreenshotTools(pi, deps);
89
- interaction.registerInteractionTools(pi, deps);
90
- inspection.registerInspectionTools(pi, deps);
91
- session.registerSessionTools(pi, deps);
92
- assertions.registerAssertionTools(pi, deps);
93
- refTools.registerRefTools(pi, deps);
94
- wait.registerWaitTools(pi, deps);
95
- pages.registerPageTools(pi, deps);
96
- forms.registerFormTools(pi, deps);
97
- intent.registerIntentTools(pi, deps);
98
- pdf.registerPdfTools(pi, deps);
99
- statePersistence.registerStatePersistenceTools(pi, deps);
100
- networkMock.registerNetworkMockTools(pi, deps);
101
- device.registerDeviceTools(pi, deps);
102
- extract.registerExtractTools(pi, deps);
103
- visualDiff.registerVisualDiffTools(pi, deps);
104
- zoom.registerZoomTools(pi, deps);
105
- codegen.registerCodegenTools(pi, deps);
106
- actionCache.registerActionCacheTools(pi, deps);
107
- injectionDetection.registerInjectionDetectionTools(pi, deps);
108
- verify.registerVerifyTools(pi, deps);
88
+ const cwdScopedPi = withBrowserArtifactCwdScope(pi);
89
+ navigation.registerNavigationTools(cwdScopedPi, deps);
90
+ screenshot.registerScreenshotTools(cwdScopedPi, deps);
91
+ interaction.registerInteractionTools(cwdScopedPi, deps);
92
+ inspection.registerInspectionTools(cwdScopedPi, deps);
93
+ session.registerSessionTools(cwdScopedPi, deps);
94
+ assertions.registerAssertionTools(cwdScopedPi, deps);
95
+ refTools.registerRefTools(cwdScopedPi, deps);
96
+ wait.registerWaitTools(cwdScopedPi, deps);
97
+ pages.registerPageTools(cwdScopedPi, deps);
98
+ forms.registerFormTools(cwdScopedPi, deps);
99
+ intent.registerIntentTools(cwdScopedPi, deps);
100
+ pdf.registerPdfTools(cwdScopedPi, deps);
101
+ statePersistence.registerStatePersistenceTools(cwdScopedPi, deps);
102
+ networkMock.registerNetworkMockTools(cwdScopedPi, deps);
103
+ device.registerDeviceTools(cwdScopedPi, deps);
104
+ extract.registerExtractTools(cwdScopedPi, deps);
105
+ visualDiff.registerVisualDiffTools(cwdScopedPi, deps);
106
+ zoom.registerZoomTools(cwdScopedPi, deps);
107
+ codegen.registerCodegenTools(cwdScopedPi, deps);
108
+ actionCache.registerActionCacheTools(cwdScopedPi, deps);
109
+ injectionDetection.registerInjectionDetectionTools(cwdScopedPi, deps);
110
+ verify.registerVerifyTools(cwdScopedPi, deps);
109
111
  })().catch((error) => {
110
112
  legacyRegistrationPromise = null;
111
113
  throw error;
@@ -113,6 +115,21 @@ async function registerLegacyBrowserTools(pi) {
113
115
  }
114
116
  return legacyRegistrationPromise;
115
117
  }
118
+ function withBrowserArtifactCwdScope(pi) {
119
+ return {
120
+ ...pi,
121
+ registerTool(definition) {
122
+ pi.registerTool({
123
+ ...definition,
124
+ async execute(toolCallId, params, signal, onUpdate, ctx) {
125
+ if (ctx?.cwd)
126
+ setArtifactRootForCwd(ctx.cwd);
127
+ return definition.execute(toolCallId, params, signal, onUpdate, ctx);
128
+ },
129
+ });
130
+ },
131
+ };
132
+ }
116
133
  async function registerBrowserTools(pi) {
117
134
  const engine = resolveBrowserEngineMode();
118
135
  if (engine === "off")
@@ -18,6 +18,17 @@ export const HAR_FILENAME = "session.har";
18
18
  // ---------------------------------------------------------------------------
19
19
  // Mutable state variables — accessed only via get/set functions
20
20
  // ---------------------------------------------------------------------------
21
+ // 0. artifactRoot
22
+ let _artifactRoot = ARTIFACT_ROOT;
23
+ export function getArtifactRoot() { return _artifactRoot; }
24
+ export function setArtifactRootForCwd(cwd) {
25
+ const newRoot = path.resolve(cwd, ".artifacts", "browser");
26
+ if (newRoot !== _artifactRoot) {
27
+ _artifactRoot = newRoot;
28
+ _sessionArtifactDir = null;
29
+ }
30
+ return _artifactRoot;
31
+ }
21
32
  // 1. browser
22
33
  let _browser = null;
23
34
  export function getBrowser() { return _browser; }
@@ -102,6 +113,7 @@ export function setHarState(h) { _harState = h; }
102
113
  // resetAllState — mirrors closeBrowser()'s reset logic
103
114
  // ---------------------------------------------------------------------------
104
115
  export function resetAllState() {
116
+ _artifactRoot = ARTIFACT_ROOT;
105
117
  _browser = null;
106
118
  _context = null;
107
119
  pageRegistry.pages = [];
@@ -2,7 +2,7 @@ import { Type } from "@sinclair/typebox";
2
2
  import { stat } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { formatTimelineEntries, buildFailureHypothesis, summarizeBrowserSession, } from "../core.js";
5
- import { ARTIFACT_ROOT, HAR_FILENAME, getPageRegistry, getConsoleLogs, getNetworkLogs, getDialogLogs, getActionTimeline, getActiveTraceSession, setActiveTraceSession, getHarState, setHarState, getSessionStartedAt, getSessionArtifactDir, } from "../state.js";
5
+ import { HAR_FILENAME, getArtifactRoot, getPageRegistry, getConsoleLogs, getNetworkLogs, getDialogLogs, getActionTimeline, getActiveTraceSession, setActiveTraceSession, getHarState, setHarState, getSessionStartedAt, getSessionArtifactDir, } from "../state.js";
6
6
  import { getActiveFrameMetadata, ensureDir, } from "../utils.js";
7
7
  export function registerSessionTools(pi, deps) {
8
8
  // -------------------------------------------------------------------------
@@ -289,7 +289,8 @@ export function registerSessionTools(pi, deps) {
289
289
  const { page: p } = await deps.ensureBrowser();
290
290
  const startedAt = Date.now();
291
291
  const sessionDir = await deps.ensureSessionArtifactDir();
292
- const bundleDir = path.join(ARTIFACT_ROOT, `${deps.formatArtifactTimestamp(startedAt)}-${deps.sanitizeArtifactName(params.name ?? "debug-bundle", "debug-bundle")}`);
292
+ const bundleName = `${deps.formatArtifactTimestamp(startedAt)}-${deps.sanitizeArtifactName(params.name ?? "debug-bundle", "debug-bundle")}`;
293
+ const bundleDir = path.join(getArtifactRoot(), bundleName);
293
294
  await ensureDir(bundleDir);
294
295
  const pages = await deps.getLivePagesSnapshot();
295
296
  const actionTimeline = getActionTimeline();
@@ -8,7 +8,7 @@ import { mkdir, stat, writeFile, copyFile } from "node:fs/promises";
8
8
  import path from "node:path";
9
9
  import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, truncateHead, } from "@gsd/pi-coding-agent";
10
10
  import { beginAction, finishAction, findAction, toActionParamsSummary, registryListPages, } from "./core.js";
11
- import { ARTIFACT_ROOT, getActiveFrame, getActiveTraceSession, getConsoleLogs, getDialogLogs, getHarState, getNetworkLogs, getSessionArtifactDir, getSessionStartedAt, setSessionArtifactDir, setSessionStartedAt, pageRegistry, actionTimeline, getPendingCriticalRequestsByPage, } from "./state.js";
11
+ import { getActiveFrame, getArtifactRoot, getActiveTraceSession, getConsoleLogs, getDialogLogs, getHarState, getNetworkLogs, getSessionArtifactDir, getSessionStartedAt, setSessionArtifactDir, setSessionStartedAt, pageRegistry, actionTimeline, getPendingCriticalRequestsByPage, } from "./state.js";
12
12
  // ---------------------------------------------------------------------------
13
13
  // Text truncation
14
14
  // ---------------------------------------------------------------------------
@@ -60,7 +60,7 @@ export async function ensureSessionArtifactDir() {
60
60
  return existing;
61
61
  }
62
62
  const startedAt = ensureSessionStartedAt();
63
- const dir = path.join(ARTIFACT_ROOT, `${formatArtifactTimestamp(startedAt)}-session`);
63
+ const dir = path.join(getArtifactRoot(), `${formatArtifactTimestamp(startedAt)}-session`);
64
64
  setSessionArtifactDir(dir);
65
65
  await ensureDir(dir);
66
66
  return dir;
@@ -95,7 +95,7 @@ export function getActiveFrameMetadata() {
95
95
  }
96
96
  export function getSessionArtifactMetadata() {
97
97
  return {
98
- artifactRoot: ARTIFACT_ROOT,
98
+ artifactRoot: getArtifactRoot(),
99
99
  sessionStartedAt: getSessionStartedAt(),
100
100
  sessionArtifactDir: getSessionArtifactDir(),
101
101
  activeTraceSession: getActiveTraceSession(),
@@ -1106,7 +1106,7 @@ export async function autoLoop(ctx, pi, s, deps, options) {
1106
1106
  unitId: iterData.unitId,
1107
1107
  });
1108
1108
  const finalizeReason = finalizeResult.action === "break" ? finalizeResult.reason : undefined;
1109
- const finalizeStatus = finalizeReason === "step-wizard"
1109
+ const finalizeStatus = (finalizeReason === "step-wizard" || finalizeReason === "milestone-complete")
1110
1110
  ? "completed"
1111
1111
  : finalizeResult.action === "next"
1112
1112
  ? "completed"
@@ -1172,7 +1172,9 @@ export async function autoLoop(ctx, pi, s, deps, options) {
1172
1172
  stuckStatePersistedThisIteration = true;
1173
1173
  finishTurn("completed");
1174
1174
  if (finalizeDecision.action === "complete-and-break") {
1175
- s.preserveStepSurfaceAfterLoopExit = true;
1175
+ if (!s.completionStopInProgress) {
1176
+ s.preserveStepSurfaceAfterLoopExit = true;
1177
+ }
1176
1178
  break;
1177
1179
  }
1178
1180
  }
@@ -49,7 +49,7 @@ import { resolveSafetyHarnessConfig } from "../safety/safety-harness.js";
49
49
  import { getContextPauseAction } from "../auto-budget.js";
50
50
  import { getWorkflowTransportSupportError, getRequiredWorkflowToolsForAutoUnit, supportsStructuredQuestions, } from "../workflow-mcp.js";
51
51
  import { prepareWorkflowMcpForProject } from "../workflow-mcp-auto-prep.js";
52
- import { getToolBaselineSnapshot } from "../auto-model-selection.js";
52
+ import { getToolBaselineSnapshot, applyThinkingLevelForModel, floorThinkingLevelForUnit } from "../auto-model-selection.js";
53
53
  import { resolveManifest } from "../unit-context-manifest.js";
54
54
  import { createWorktreeSafetyModule } from "../worktree-safety.js";
55
55
  import { isSuspiciousGhostCompletion } from "../auto-unit-closeout.js";
@@ -393,7 +393,7 @@ async function generateMilestoneReport(s, ctx, milestoneId) {
393
393
  async function closeoutAndStop(ctx, pi, s, deps, reason) {
394
394
  if (s.currentUnit) {
395
395
  await deps.closeoutUnit(ctx, s.basePath, s.currentUnit.type, s.currentUnit.id, s.currentUnit.startedAt, deps.buildSnapshotOpts(s.currentUnit.type, s.currentUnit.id));
396
- s.currentUnit = null;
396
+ s.clearCurrentUnit();
397
397
  }
398
398
  await deps.stopAuto(ctx, pi, reason);
399
399
  }
@@ -581,7 +581,7 @@ async function failClosedOnFinalizeTimeout(ic, iterData, loopState, stage, start
581
581
  });
582
582
  ctx.ui.notify(`${stage === "pre" ? "postUnitPreVerification" : "postUnitPostVerification"} timed out after ${timeoutMs / 1000}s for ${unitType} ${unitId} (${loopState.consecutiveFinalizeTimeouts}/${MAX_FINALIZE_TIMEOUTS}) — pausing auto-mode for recovery.`, "warning");
583
583
  await deps.pauseAuto(ctx, pi);
584
- s.currentUnit = null;
584
+ s.clearCurrentUnit();
585
585
  clearCurrentPhase();
586
586
  drainLogs();
587
587
  return { action: "break", reason: progressKind };
@@ -1029,7 +1029,7 @@ export async function runPreDispatch(ic, loopState) {
1029
1029
  deps.logCmuxEvent(prefs, `Milestone ${mid} complete.`, "success");
1030
1030
  if (s.currentUnit) {
1031
1031
  await deps.closeoutUnit(ctx, s.basePath, s.currentUnit.type, s.currentUnit.id, s.currentUnit.startedAt, deps.buildSnapshotOpts(s.currentUnit.type, s.currentUnit.id));
1032
- s.currentUnit = null;
1032
+ s.clearCurrentUnit();
1033
1033
  }
1034
1034
  await deps.stopAuto(ctx, pi, `Milestone ${mid} complete`, {
1035
1035
  completionWidget: {
@@ -1712,9 +1712,16 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1712
1712
  if (match) {
1713
1713
  const ok = await pi.setModel(match, { persist: false });
1714
1714
  if (ok) {
1715
- if (s.autoModeStartThinkingLevel) {
1716
- pi.setThinkingLevel(s.autoModeStartThinkingLevel);
1717
- }
1715
+ // Apply the per-phase reasoning effort selectAndApplyModel resolved for
1716
+ // this unit — not the auto-start session snapshot — but route it through
1717
+ // the same floor + capability-clamp pipeline against the *hook* model
1718
+ // (ADR-026). The hook override can pick a different model family than the
1719
+ // one selectAndApplyModel clamped against, so re-clamping here prevents
1720
+ // sending an unsupported level; the floor fills in when no phase level
1721
+ // resolved so a hook-overridden execute-task still meets the floor.
1722
+ const hookThinkingBase = modelResult.appliedThinkingLevel
1723
+ ?? floorThinkingLevelForUnit(unitType, s.autoModeStartThinkingLevel);
1724
+ applyThinkingLevelForModel(pi, hookThinkingBase, match, ctx);
1718
1725
  s.currentUnitModel = match;
1719
1726
  ctx.ui.notify(`Hook model override: ${match.provider}/${match.id}`, "info");
1720
1727
  }
@@ -1783,7 +1790,21 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1783
1790
  _resetLogs();
1784
1791
  const unitStartedAt = Date.now();
1785
1792
  s.unitDispatchCount.set(dispatchKey, nextDispatchCount);
1786
- s.currentUnit = { type: unitType, id: unitId, startedAt: unitStartedAt, workspaceRoot: s.basePath };
1793
+ s.setCurrentUnit({ type: unitType, id: unitId, startedAt: unitStartedAt, workspaceRoot: s.basePath });
1794
+ if (unitType === "execute-task") {
1795
+ const { milestone, slice, task } = parseUnitId(unitId);
1796
+ if (milestone && slice && task && isDbAvailable()) {
1797
+ try {
1798
+ const taskRow = getTask(milestone, slice, task);
1799
+ if (taskRow)
1800
+ s.sourceObservations.observePlanTask(taskRow);
1801
+ }
1802
+ catch (err) {
1803
+ const message = err instanceof Error ? err.message : String(err);
1804
+ logWarning("prompt", `failed to preload source observations for ${unitId}: ${message}`);
1805
+ }
1806
+ }
1807
+ }
1787
1808
  s.rootWriteBaseline = isIsolatedWorktreeSession(s)
1788
1809
  ? captureRootDirtySnapshot(s.originalBasePath)
1789
1810
  : null;
@@ -1869,7 +1890,7 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1869
1890
  category: "unknown",
1870
1891
  isTransient: true,
1871
1892
  });
1872
- s.currentUnit = null;
1893
+ s.clearCurrentUnit();
1873
1894
  await deps.pauseAuto(ctx, pi);
1874
1895
  return { action: "break", reason: "ghost-completion" };
1875
1896
  }
@@ -2204,7 +2225,7 @@ export async function runFinalize(ic, iterData, loopState, sidecarItem) {
2204
2225
  s.currentUnit?.type === preUnitSnapshot.type &&
2205
2226
  s.currentUnit?.id === preUnitSnapshot.id &&
2206
2227
  s.currentUnit?.startedAt === preUnitSnapshot.startedAt) {
2207
- s.currentUnit = null;
2228
+ s.clearCurrentUnit();
2208
2229
  }
2209
2230
  s.rootWriteBaseline = null;
2210
2231
  };
@@ -2439,5 +2460,17 @@ export async function runFinalize(ic, iterData, loopState, sidecarItem) {
2439
2460
  ctx.ui.notify(formatForNotification(logs), severity);
2440
2461
  }
2441
2462
  }
2463
+ if (preUnitSnapshot?.type === "complete-milestone" && s.currentMilestoneId) {
2464
+ // cleanupAfterLoopExit skips gsd-progress when preserveCompletionSurface is true, so clear stale controls here.
2465
+ ctx.ui.setStatus?.("gsd-step", undefined);
2466
+ ctx.ui.setWidget?.("gsd-progress", undefined);
2467
+ await deps.stopAuto(ctx, pi, `Milestone ${s.currentMilestoneId} complete`, {
2468
+ completionWidget: {
2469
+ milestoneId: s.currentMilestoneId,
2470
+ milestoneTitle: iterData.midTitle,
2471
+ },
2472
+ });
2473
+ return { action: "break", reason: "milestone-complete" };
2474
+ }
2442
2475
  return { action: "next", data: undefined };
2443
2476
  }
@@ -17,6 +17,7 @@
17
17
  * auto-session-encapsulation.test.ts enforce that auto.ts has no module-level
18
18
  * `let` or `var` declarations.
19
19
  */
20
+ import { SourceObservationStore, supportsSourceObservationsForUnit } from "../source-observations.js";
20
21
  import { resolveWorktreeProjectRoot } from "../worktree-root.js";
21
22
  import { normalizeRealPath } from "../paths.js";
22
23
  // ─── Constants ───────────────────────────────────────────────────────────────
@@ -76,6 +77,7 @@ export class AutoSession {
76
77
  currentTurnId = null;
77
78
  currentUnitRouting = null;
78
79
  currentMilestoneId = null;
80
+ sourceObservations = new SourceObservationStore();
79
81
  // ── Model state ──────────────────────────────────────────────────────────
80
82
  autoModeStartModel = null;
81
83
  /** Explicit /gsd model pin captured at bootstrap (session-scoped policy override). */
@@ -196,6 +198,23 @@ export class AutoSession {
196
198
  this.unitDispatchCount.clear();
197
199
  this.unitLifetimeDispatches.clear();
198
200
  }
201
+ setCurrentUnit(unit) {
202
+ this.currentUnit = unit;
203
+ if (!supportsSourceObservationsForUnit(unit.type)) {
204
+ this.sourceObservations.clear();
205
+ return;
206
+ }
207
+ this.sourceObservations.beginUnit({
208
+ unitType: unit.type,
209
+ unitId: unit.id,
210
+ startedAt: unit.startedAt,
211
+ basePath: unit.workspaceRoot ?? this.basePath,
212
+ });
213
+ }
214
+ clearCurrentUnit() {
215
+ this.currentUnit = null;
216
+ this.sourceObservations.clear();
217
+ }
199
218
  get lockBasePath() {
200
219
  return resolveWorktreeProjectRoot(this.basePath, this.originalBasePath);
201
220
  }
@@ -246,7 +265,7 @@ export class AutoSession {
246
265
  this.unitLifetimeDispatches.clear();
247
266
  this.unitRecoveryCount.clear();
248
267
  // Unit
249
- this.currentUnit = null;
268
+ this.clearCurrentUnit();
250
269
  this.currentTraceId = null;
251
270
  this.currentTurnId = null;
252
271
  this.currentUnitRouting = null;
@@ -71,6 +71,7 @@ const COMPLETE_AND_BREAK_REASONS = [
71
71
  "verification-pause",
72
72
  "finalize-pre-timeout",
73
73
  "finalize-post-timeout",
74
+ "milestone-complete",
74
75
  ];
75
76
  function isCompleteAndBreakReason(reason) {
76
77
  return COMPLETE_AND_BREAK_REASONS.includes(reason);
@@ -1,7 +1,8 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: Declarative auto-mode dispatch rules and dispatch resolver.
3
3
  import { loadFile, extractUatType, loadActiveOverrides } from "./files.js";
4
- import { isDbAvailable, getMilestoneSlices, getPendingGates, markAllGatesOmitted, getMilestone, insertAssessment, setSliceSketchFlag, transaction, getAssessment, } from "./gsd-db.js";
4
+ import { getUatBrowserToolSupportError } from "./uat-policy.js";
5
+ import { isDbAvailable, getMilestoneSlices, getPendingGatesForTurn, markPendingGatesOmittedForTurn, getMilestone, insertArtifact, insertAssessment, setSliceSketchFlag, transaction, getAssessment, } from "./gsd-db.js";
5
6
  import { isClosedStatus } from "./status-guards.js";
6
7
  import { extractVerdict, isAcceptableUatVerdict } from "./verdict-parser.js";
7
8
  import { gsdRoot, resolveGsdPathContract, resolveMilestoneFile, resolveMilestonePath, resolveSliceFile, resolveSlicePath, resolveTaskFile, relTaskFile, relSliceFile, buildMilestoneFileName, buildSliceFileName, buildTaskFileName, gsdProjectionRoot, } from "./paths.js";
@@ -12,7 +13,7 @@ import { logWarning, logError } from "./workflow-logger.js";
12
13
  import { dirname, join } from "node:path";
13
14
  import { hasImplementationArtifacts } from "./milestone-implementation-evidence.js";
14
15
  import { buildDiscussMilestonePrompt, buildDiscussProjectPrompt, buildDiscussRequirementsPrompt, buildResearchDecisionPrompt, buildResearchProjectPrompt, buildResearchMilestonePrompt, buildPlanMilestonePrompt, buildResearchSlicePrompt, buildPlanSlicePrompt, buildRefineSlicePrompt, buildExecuteTaskPrompt, buildCompleteSlicePrompt, buildCompleteMilestonePrompt, buildValidateMilestonePrompt, buildReplanSlicePrompt, buildRunUatPrompt, buildReassessRoadmapPrompt, buildRewriteDocsPrompt, buildReactiveExecutePrompt, buildGateEvaluatePrompt, buildParallelResearchSlicesPrompt, checkNeedsReassessment, checkNeedsRunUat, } from "./auto-prompts.js";
15
- import { resolveModelWithFallbacksForUnit } from "./preferences-models.js";
16
+ import { resolveModelWithFallbacksForUnit, resolveThinkingLevelForUnit } from "./preferences-models.js";
16
17
  import { resolveUokFlags } from "./uok/flags.js";
17
18
  import { selectReactiveDispatchBatch } from "./uok/execution-graph.js";
18
19
  import { getMilestonePipelineVariant } from "./milestone-scope-classifier.js";
@@ -250,6 +251,43 @@ export function findMissingSummaries(basePath, mid) {
250
251
  })
251
252
  .map(s => s.id);
252
253
  }
254
+ function stringField(row, key) {
255
+ const value = row?.[key];
256
+ return typeof value === "string" ? value : null;
257
+ }
258
+ function stripGsdPrefix(path) {
259
+ return path.startsWith(".gsd/") ? path.slice(".gsd/".length) : path;
260
+ }
261
+ function persistSliceAssessmentBackfill(assessmentRelPath, mid, sliceId, content) {
262
+ const artifactPath = stripGsdPrefix(assessmentRelPath);
263
+ const existingAssessment = getAssessment(assessmentRelPath) ??
264
+ getAssessment(artifactPath);
265
+ const scope = stringField(existingAssessment, "scope") ?? "run-uat";
266
+ const status = stringField(existingAssessment, "status") ??
267
+ extractVerdict(content)?.toLowerCase() ??
268
+ "unknown";
269
+ transaction(() => {
270
+ insertArtifact({
271
+ path: artifactPath,
272
+ artifact_type: "ASSESSMENT",
273
+ milestone_id: mid,
274
+ slice_id: sliceId,
275
+ task_id: null,
276
+ full_content: content,
277
+ });
278
+ if (!getAssessment(assessmentRelPath)) {
279
+ insertAssessment({
280
+ path: assessmentRelPath,
281
+ milestoneId: mid,
282
+ sliceId,
283
+ taskId: null,
284
+ status,
285
+ scope,
286
+ fullContent: content,
287
+ });
288
+ }
289
+ });
290
+ }
253
291
  function backfillMissingAssessmentsFromSummaries(basePath, mid) {
254
292
  const completedSliceIds = new Set();
255
293
  if (isDbAvailable()) {
@@ -281,11 +319,12 @@ function backfillMissingAssessmentsFromSummaries(basePath, mid) {
281
319
  const slicePath = resolveSlicePath(basePath, mid, sliceId);
282
320
  const assessmentPath = resolveSliceFile(basePath, mid, sliceId, "ASSESSMENT")
283
321
  ?? (slicePath ? join(slicePath, buildSliceFileName(sliceId, "ASSESSMENT")) : null);
284
- if (!assessmentPath || existsSync(assessmentPath))
322
+ if (!assessmentPath)
285
323
  continue;
286
- mkdirSync(dirname(assessmentPath), { recursive: true });
324
+ const assessmentRelPath = relSliceFile(basePath, mid, sliceId, "ASSESSMENT");
287
325
  const now = new Date().toISOString();
288
- const content = [
326
+ const didCreateAssessment = !existsSync(assessmentPath);
327
+ const content = didCreateAssessment ? [
289
328
  "---",
290
329
  `sliceId: ${sliceId}`,
291
330
  "verdict: PASS",
@@ -297,8 +336,19 @@ function backfillMissingAssessmentsFromSummaries(basePath, mid) {
297
336
  "Auto-created during milestone validation because this completed slice had a SUMMARY but no ASSESSMENT artifact.",
298
337
  "No additional reassessment changes were detected in this backfill step.",
299
338
  "",
300
- ].join("\n");
301
- writeFileSync(assessmentPath, content, "utf-8");
339
+ ].join("\n") : readFileSync(assessmentPath, "utf-8");
340
+ if (isDbAvailable()) {
341
+ try {
342
+ persistSliceAssessmentBackfill(assessmentRelPath, mid, sliceId, content);
343
+ }
344
+ catch (err) {
345
+ logWarning("dispatch", `failed to backfill assessment DB rows for ${mid}/${sliceId}: ${err.message}`);
346
+ }
347
+ }
348
+ if (didCreateAssessment) {
349
+ mkdirSync(dirname(assessmentPath), { recursive: true });
350
+ writeFileSync(assessmentPath, content, "utf-8");
351
+ }
302
352
  }
303
353
  }
304
354
  // ─── Rewrite Circuit Breaker ──────────────────────────────────────────────
@@ -480,6 +530,15 @@ export const DISPATCH_RULES = [
480
530
  if (transportError) {
481
531
  return { action: "stop", reason: transportError, level: "warning" };
482
532
  }
533
+ const browserToolError = getUatBrowserToolSupportError({
534
+ uatType,
535
+ activeTools,
536
+ milestoneId: mid,
537
+ sliceId,
538
+ });
539
+ if (browserToolError) {
540
+ return { action: "stop", reason: browserToolError, level: "warning" };
541
+ }
483
542
  // Cap run-uat dispatch attempts to prevent infinite replay (#3624).
484
543
  // Check before incrementing so an exhausted counter cannot create a
485
544
  // no-progress skip loop that starves later dispatch rules.
@@ -885,7 +944,7 @@ export const DISPATCH_RULES = [
885
944
  action: "dispatch",
886
945
  unitType: "research-slice",
887
946
  unitId: `${mid}/parallel-research`,
888
- prompt: await buildParallelResearchSlicesPrompt(mid, midTitle, researchReadySlices, basePath, resolveModelWithFallbacksForUnit("subagent")?.primary),
947
+ prompt: await buildParallelResearchSlicesPrompt(mid, midTitle, researchReadySlices, basePath, resolveModelWithFallbacksForUnit("subagent")?.primary, resolveThinkingLevelForUnit("subagent")),
889
948
  };
890
949
  },
891
950
  },
@@ -1037,17 +1096,17 @@ export const DISPATCH_RULES = [
1037
1096
  // Gate evaluation is opt-in via preferences
1038
1097
  const gateConfig = prefs?.gate_evaluation;
1039
1098
  if (!gateConfig?.enabled) {
1040
- markAllGatesOmitted(mid, sid);
1099
+ markPendingGatesOmittedForTurn(mid, sid, "gate-evaluate");
1041
1100
  return { action: "skip" };
1042
1101
  }
1043
- const pending = getPendingGates(mid, sid, "slice");
1102
+ const pending = getPendingGatesForTurn(mid, sid, "gate-evaluate");
1044
1103
  if (pending.length === 0)
1045
1104
  return { action: "skip" };
1046
1105
  return {
1047
1106
  action: "dispatch",
1048
1107
  unitType: "gate-evaluate",
1049
1108
  unitId: `${mid}/${sid}/gates+${pending.map(g => g.gate_id).join(",")}`,
1050
- prompt: await buildGateEvaluatePrompt(mid, midTitle, sid, sTitle, basePath, resolveModelWithFallbacksForUnit("subagent")?.primary),
1109
+ prompt: await buildGateEvaluatePrompt(mid, midTitle, sid, sTitle, basePath, resolveModelWithFallbacksForUnit("subagent")?.primary, resolveThinkingLevelForUnit("subagent")),
1051
1110
  };
1052
1111
  },
1053
1112
  },
@@ -1090,6 +1149,7 @@ export const DISPATCH_RULES = [
1090
1149
  return null;
1091
1150
  const maxParallel = reactiveConfig?.max_parallel ?? 2;
1092
1151
  const subagentModel = reactiveConfig?.subagent_model ?? resolveModelWithFallbacksForUnit("subagent")?.primary;
1152
+ const subagentThinking = resolveThinkingLevelForUnit("subagent");
1093
1153
  // Default-on safety threshold: only activate reactive dispatch when at
1094
1154
  // least N tasks are ready. Users who explicitly enabled reactive_execution
1095
1155
  // keep the legacy threshold of 2 (matches the prior "any parallelism is
@@ -1147,7 +1207,7 @@ export const DISPATCH_RULES = [
1147
1207
  action: "dispatch",
1148
1208
  unitType: "reactive-execute",
1149
1209
  unitId: `${mid}/${sid}/reactive+${batchSuffix}`,
1150
- prompt: await buildReactiveExecutePrompt(mid, midTitle, sid, sTitle, selected, basePath, subagentModel, { sessionContextWindow, modelRegistry, sessionProvider }),
1210
+ prompt: await buildReactiveExecutePrompt(mid, midTitle, sid, sTitle, selected, basePath, subagentModel, { sessionContextWindow, modelRegistry, sessionProvider, subagentThinking }),
1151
1211
  };
1152
1212
  }
1153
1213
  catch (err) {