gsd-pi 2.81.0-dev.72a81bdf3 → 2.82.0-dev.20138ae42

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 (463) hide show
  1. package/README.md +50 -31
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/GSD-WORKFLOW.md +10 -1
  4. package/dist/resources/extensions/browser-tools/tools/screenshot.js +1 -0
  5. package/dist/resources/extensions/browser-tools/tools/zoom.js +1 -0
  6. package/dist/resources/extensions/claude-code-cli/partial-builder.js +2 -1
  7. package/dist/resources/extensions/cmux/index.js +5 -0
  8. package/dist/resources/extensions/gsd/auto/infra-errors.js +9 -3
  9. package/dist/resources/extensions/gsd/auto/loop.js +5 -5
  10. package/dist/resources/extensions/gsd/auto/orchestrator.js +124 -6
  11. package/dist/resources/extensions/gsd/auto/phases.js +30 -1
  12. package/dist/resources/extensions/gsd/auto/workflow-memory-pressure.js +12 -0
  13. package/dist/resources/extensions/gsd/auto-dashboard.js +66 -1
  14. package/dist/resources/extensions/gsd/auto-dispatch.js +13 -6
  15. package/dist/resources/extensions/gsd/auto-model-selection.js +2 -0
  16. package/dist/resources/extensions/gsd/auto-post-unit.js +233 -127
  17. package/dist/resources/extensions/gsd/auto-prompts.js +13 -5
  18. package/dist/resources/extensions/gsd/auto-recovery.js +31 -1
  19. package/dist/resources/extensions/gsd/auto-start.js +85 -12
  20. package/dist/resources/extensions/gsd/auto-verification.js +28 -22
  21. package/dist/resources/extensions/gsd/auto-worktree.js +111 -1
  22. package/dist/resources/extensions/gsd/auto.js +158 -55
  23. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +25 -6
  24. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +9 -8
  25. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +9 -2
  26. package/dist/resources/extensions/gsd/bootstrap/subagent-input.js +21 -9
  27. package/dist/resources/extensions/gsd/bootstrap/system-context.js +55 -12
  28. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +16 -2
  29. package/dist/resources/extensions/gsd/clean-root-preflight.js +170 -8
  30. package/dist/resources/extensions/gsd/commands/catalog.js +4 -1
  31. package/dist/resources/extensions/gsd/commands/handlers/core.js +38 -1
  32. package/dist/resources/extensions/gsd/commands-bootstrap.js +5 -0
  33. package/dist/resources/extensions/gsd/commands-handlers.js +15 -2
  34. package/dist/resources/extensions/gsd/context-store.js +112 -0
  35. package/dist/resources/extensions/gsd/crash-recovery.js +31 -5
  36. package/dist/resources/extensions/gsd/db/unit-dispatches.js +3 -2
  37. package/dist/resources/extensions/gsd/db-writer.js +150 -84
  38. package/dist/resources/extensions/gsd/dispatch-guard.js +2 -2
  39. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  40. package/dist/resources/extensions/gsd/doctor-git-checks.js +41 -6
  41. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +28 -11
  42. package/dist/resources/extensions/gsd/doctor.js +2 -28
  43. package/dist/resources/extensions/gsd/export-html.js +27 -425
  44. package/dist/resources/extensions/gsd/git-service.js +39 -1
  45. package/dist/resources/extensions/gsd/gsd-db.js +1 -0
  46. package/dist/resources/extensions/gsd/guided-flow.js +93 -111
  47. package/dist/resources/extensions/gsd/guided-unit-context.js +23 -0
  48. package/dist/resources/extensions/gsd/knowledge-backfill.js +144 -0
  49. package/dist/resources/extensions/gsd/knowledge-capture.js +136 -0
  50. package/dist/resources/extensions/gsd/knowledge-parser.js +154 -0
  51. package/dist/resources/extensions/gsd/knowledge-projection.js +210 -0
  52. package/dist/resources/extensions/gsd/markdown-renderer.js +6 -1
  53. package/dist/resources/extensions/gsd/md-importer.js +1 -1
  54. package/dist/resources/extensions/gsd/memory-backfill.js +73 -17
  55. package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +222 -0
  56. package/dist/resources/extensions/gsd/migrate/command.js +5 -0
  57. package/dist/resources/extensions/gsd/migrate/parsers.js +10 -0
  58. package/dist/resources/extensions/gsd/migrate/preview.js +9 -0
  59. package/dist/resources/extensions/gsd/migrate/transformer.js +51 -4
  60. package/dist/resources/extensions/gsd/migrate/writer.js +11 -1
  61. package/dist/resources/extensions/gsd/migration-auto-check.js +12 -17
  62. package/dist/resources/extensions/gsd/milestone-actions.js +11 -4
  63. package/dist/resources/extensions/gsd/native-git-bridge.js +48 -12
  64. package/dist/resources/extensions/gsd/pending-auto-start.js +52 -0
  65. package/dist/resources/extensions/gsd/post-execution-checks.js +73 -2
  66. package/dist/resources/extensions/gsd/pre-execution-checks.js +28 -1
  67. package/dist/resources/extensions/gsd/prompt-loader.js +1 -1
  68. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  69. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  70. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +8 -8
  71. package/dist/resources/extensions/gsd/prompts/discuss.md +9 -9
  72. package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +4 -4
  73. package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +3 -3
  74. package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -4
  75. package/dist/resources/extensions/gsd/prompts/queue.md +4 -4
  76. package/dist/resources/extensions/gsd/prompts/refine-slice.md +2 -2
  77. package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  78. package/dist/resources/extensions/gsd/prompts/system.md +2 -2
  79. package/dist/resources/extensions/gsd/provider-switch-observer.js +146 -0
  80. package/dist/resources/extensions/gsd/smart-entry-routing.js +36 -0
  81. package/dist/resources/extensions/gsd/state-reconciliation/drift/merge-state.js +6 -1
  82. package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +9 -14
  83. package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +19 -24
  84. package/dist/resources/extensions/gsd/status-guards.js +4 -0
  85. package/dist/resources/extensions/gsd/templates/knowledge.md +2 -2
  86. package/dist/resources/extensions/gsd/templates/plan.md +8 -5
  87. package/dist/resources/extensions/gsd/templates/task-plan.md +4 -2
  88. package/dist/resources/extensions/gsd/tools/complete-milestone.js +6 -8
  89. package/dist/resources/extensions/gsd/tools/complete-slice.js +6 -8
  90. package/dist/resources/extensions/gsd/tools/plan-milestone.js +7 -1
  91. package/dist/resources/extensions/gsd/tools/plan-slice.js +89 -14
  92. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +119 -0
  93. package/dist/resources/extensions/gsd/unit-context-manifest.js +32 -10
  94. package/dist/resources/extensions/gsd/validation.js +23 -1
  95. package/dist/resources/extensions/gsd/verification-gate.js +68 -7
  96. package/dist/resources/extensions/gsd/verification-verdict.js +26 -0
  97. package/dist/resources/extensions/gsd/workflow-projections.js +6 -8
  98. package/dist/resources/extensions/gsd/worktree-lifecycle.js +54 -10
  99. package/dist/resources/extensions/shared/html-shell.js +388 -0
  100. package/dist/resources/extensions/subagent/index.js +448 -78
  101. package/dist/resources/extensions/subagent/launch.js +77 -0
  102. package/dist/resources/extensions/subagent/run-store.js +148 -0
  103. package/dist/resources/extensions/visual-brief/artifact-policy.js +29 -0
  104. package/dist/resources/extensions/visual-brief/extension-manifest.json +8 -0
  105. package/dist/resources/extensions/visual-brief/index.js +5 -0
  106. package/dist/resources/extensions/visual-brief/page-contract.js +124 -0
  107. package/dist/resources/extensions/visual-brief/prompts.js +140 -0
  108. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  109. package/dist/web/standalone/.next/BUILD_ID +1 -1
  110. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  111. package/dist/web/standalone/.next/build-manifest.json +3 -3
  112. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  113. package/dist/web/standalone/.next/react-loadable-manifest.json +3 -3
  114. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  116. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  118. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  119. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  120. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  121. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  122. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  124. package/dist/web/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  125. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  127. package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -7
  128. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -7
  129. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  130. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -5
  131. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  132. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -5
  134. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  135. package/dist/web/standalone/.next/server/app/index.html +1 -1
  136. package/dist/web/standalone/.next/server/app/index.rsc +4 -7
  137. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  138. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -7
  139. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  140. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -5
  141. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -5
  142. package/dist/web/standalone/.next/server/app/page.js +2 -2
  143. package/dist/web/standalone/.next/server/app/page.js.nft.json +1 -1
  144. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  146. package/dist/web/standalone/.next/server/chunks/4266.js +2 -0
  147. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  148. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  151. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  152. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  153. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  154. package/dist/web/standalone/.next/static/chunks/2973.33f26573894b6153.js +2 -0
  155. package/dist/web/standalone/.next/static/chunks/{8359.e059d86b255fce1c.js → 8359.7eb3bb8f8ecf4c01.js} +2 -2
  156. package/dist/web/standalone/.next/static/chunks/app/layout-8c10ec293ae0f1d5.js +1 -0
  157. package/dist/web/standalone/.next/static/chunks/{webpack-de742b64187e13fe.js → webpack-9a4db269f9ed63ad.js} +1 -1
  158. package/dist/web/standalone/.next/static/css/746ee28c929d1880.css +1 -0
  159. package/package.json +6 -5
  160. package/packages/contracts/dist/rpc.test.js +7 -0
  161. package/packages/contracts/dist/rpc.test.js.map +1 -1
  162. package/packages/contracts/dist/workflow.d.ts +21 -0
  163. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  164. package/packages/contracts/dist/workflow.js +24 -0
  165. package/packages/contracts/dist/workflow.js.map +1 -1
  166. package/packages/contracts/src/rpc.test.ts +8 -0
  167. package/packages/contracts/src/workflow.ts +24 -0
  168. package/packages/daemon/package.json +2 -2
  169. package/packages/mcp-server/README.md +14 -3
  170. package/packages/mcp-server/dist/workflow-tools.d.ts +0 -3
  171. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  172. package/packages/mcp-server/dist/workflow-tools.js +80 -0
  173. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  174. package/packages/mcp-server/package.json +2 -2
  175. package/packages/mcp-server/src/workflow-tools-parity.test.ts +244 -0
  176. package/packages/mcp-server/src/workflow-tools.test.ts +23 -1
  177. package/packages/mcp-server/src/workflow-tools.ts +168 -0
  178. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  179. package/packages/native/package.json +1 -1
  180. package/packages/native/tsconfig.json +2 -1
  181. package/packages/native/tsconfig.tsbuildinfo +1 -1
  182. package/packages/pi-agent-core/package.json +1 -1
  183. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  184. package/packages/pi-ai/dist/index.d.ts +2 -2
  185. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  186. package/packages/pi-ai/dist/index.js +1 -1
  187. package/packages/pi-ai/dist/index.js.map +1 -1
  188. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
  189. package/packages/pi-ai/dist/providers/openai-codex-responses.js +82 -1
  190. package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  191. package/packages/pi-ai/dist/providers/openai-codex-responses.test.d.ts +2 -0
  192. package/packages/pi-ai/dist/providers/openai-codex-responses.test.d.ts.map +1 -0
  193. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js +52 -0
  194. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js.map +1 -0
  195. package/packages/pi-ai/dist/providers/simple-options.d.ts +2 -4
  196. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  197. package/packages/pi-ai/dist/providers/simple-options.js +5 -6
  198. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  199. package/packages/pi-ai/dist/providers/simple-options.test.d.ts +2 -0
  200. package/packages/pi-ai/dist/providers/simple-options.test.d.ts.map +1 -0
  201. package/packages/pi-ai/dist/providers/simple-options.test.js +50 -0
  202. package/packages/pi-ai/dist/providers/simple-options.test.js.map +1 -0
  203. package/packages/pi-ai/dist/providers/transform-messages.d.ts +11 -0
  204. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  205. package/packages/pi-ai/dist/providers/transform-messages.js +20 -0
  206. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  207. package/packages/pi-ai/package.json +1 -1
  208. package/packages/pi-ai/src/index.ts +7 -2
  209. package/packages/pi-ai/src/providers/openai-codex-responses.test.ts +63 -0
  210. package/packages/pi-ai/src/providers/openai-codex-responses.ts +91 -1
  211. package/packages/pi-ai/src/providers/simple-options.test.ts +60 -0
  212. package/packages/pi-ai/src/providers/simple-options.ts +5 -6
  213. package/packages/pi-ai/src/providers/transform-messages.ts +24 -0
  214. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  215. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.d.ts +2 -0
  216. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.d.ts.map +1 -0
  217. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.js +66 -0
  218. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.js.map +1 -0
  219. package/packages/pi-coding-agent/dist/core/agent-session.js +1 -1
  220. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  221. package/packages/pi-coding-agent/dist/core/system-prompt.js +4 -4
  222. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  223. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  224. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +24 -6
  225. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  226. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.d.ts +2 -0
  227. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.d.ts.map +1 -0
  228. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.js +17 -0
  229. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.js.map +1 -0
  230. package/packages/pi-coding-agent/package.json +1 -1
  231. package/packages/pi-coding-agent/src/core/agent-session-thinking-level.test.ts +79 -0
  232. package/packages/pi-coding-agent/src/core/agent-session.ts +1 -1
  233. package/packages/pi-coding-agent/src/core/system-prompt.ts +4 -4
  234. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +23 -7
  235. package/packages/pi-coding-agent/src/tests/system-prompt-file-safety.test.ts +22 -0
  236. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  237. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  238. package/packages/pi-tui/dist/tui.js +5 -0
  239. package/packages/pi-tui/dist/tui.js.map +1 -1
  240. package/packages/pi-tui/package.json +1 -1
  241. package/packages/pi-tui/src/tui.ts +6 -0
  242. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  243. package/packages/rpc-client/package.json +1 -1
  244. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  245. package/pkg/package.json +1 -1
  246. package/src/resources/GSD-WORKFLOW.md +10 -1
  247. package/src/resources/extensions/browser-tools/tools/screenshot.ts +1 -0
  248. package/src/resources/extensions/browser-tools/tools/zoom.ts +1 -0
  249. package/src/resources/extensions/claude-code-cli/partial-builder.ts +2 -1
  250. package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +19 -2
  251. package/src/resources/extensions/cmux/index.ts +6 -0
  252. package/src/resources/extensions/gsd/auto/contracts.ts +59 -16
  253. package/src/resources/extensions/gsd/auto/infra-errors.ts +9 -3
  254. package/src/resources/extensions/gsd/auto/loop.ts +8 -5
  255. package/src/resources/extensions/gsd/auto/orchestrator.ts +129 -6
  256. package/src/resources/extensions/gsd/auto/phases.ts +36 -1
  257. package/src/resources/extensions/gsd/auto/workflow-memory-pressure.ts +13 -0
  258. package/src/resources/extensions/gsd/auto-dashboard.ts +72 -1
  259. package/src/resources/extensions/gsd/auto-dispatch.ts +14 -6
  260. package/src/resources/extensions/gsd/auto-model-selection.ts +2 -1
  261. package/src/resources/extensions/gsd/auto-post-unit.ts +266 -139
  262. package/src/resources/extensions/gsd/auto-prompts.ts +13 -5
  263. package/src/resources/extensions/gsd/auto-recovery.ts +29 -0
  264. package/src/resources/extensions/gsd/auto-start.ts +92 -9
  265. package/src/resources/extensions/gsd/auto-verification.ts +36 -34
  266. package/src/resources/extensions/gsd/auto-worktree.ts +119 -1
  267. package/src/resources/extensions/gsd/auto.ts +167 -53
  268. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +33 -6
  269. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +9 -8
  270. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +10 -2
  271. package/src/resources/extensions/gsd/bootstrap/subagent-input.ts +19 -7
  272. package/src/resources/extensions/gsd/bootstrap/system-context.ts +58 -15
  273. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +19 -3
  274. package/src/resources/extensions/gsd/clean-root-preflight.ts +174 -8
  275. package/src/resources/extensions/gsd/commands/catalog.ts +4 -1
  276. package/src/resources/extensions/gsd/commands/handlers/core.ts +41 -1
  277. package/src/resources/extensions/gsd/commands-bootstrap.ts +10 -0
  278. package/src/resources/extensions/gsd/commands-handlers.ts +19 -2
  279. package/src/resources/extensions/gsd/context-store.ts +120 -1
  280. package/src/resources/extensions/gsd/crash-recovery.ts +30 -4
  281. package/src/resources/extensions/gsd/db/unit-dispatches.ts +4 -3
  282. package/src/resources/extensions/gsd/db-writer.ts +167 -84
  283. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -2
  284. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  285. package/src/resources/extensions/gsd/doctor-git-checks.ts +44 -6
  286. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +25 -13
  287. package/src/resources/extensions/gsd/doctor-types.ts +2 -0
  288. package/src/resources/extensions/gsd/doctor.ts +2 -27
  289. package/src/resources/extensions/gsd/export-html.ts +27 -427
  290. package/src/resources/extensions/gsd/git-service.ts +45 -1
  291. package/src/resources/extensions/gsd/gsd-db.ts +3 -0
  292. package/src/resources/extensions/gsd/guided-flow.ts +126 -128
  293. package/src/resources/extensions/gsd/guided-unit-context.ts +30 -0
  294. package/src/resources/extensions/gsd/knowledge-backfill.ts +164 -0
  295. package/src/resources/extensions/gsd/knowledge-capture.ts +160 -0
  296. package/src/resources/extensions/gsd/knowledge-parser.ts +174 -0
  297. package/src/resources/extensions/gsd/knowledge-projection.ts +241 -0
  298. package/src/resources/extensions/gsd/markdown-renderer.ts +6 -1
  299. package/src/resources/extensions/gsd/md-importer.ts +1 -1
  300. package/src/resources/extensions/gsd/memory-backfill.ts +89 -17
  301. package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +277 -0
  302. package/src/resources/extensions/gsd/migrate/command.ts +5 -0
  303. package/src/resources/extensions/gsd/migrate/parsers.ts +11 -0
  304. package/src/resources/extensions/gsd/migrate/preview.ts +10 -0
  305. package/src/resources/extensions/gsd/migrate/transformer.ts +58 -4
  306. package/src/resources/extensions/gsd/migrate/writer.ts +14 -1
  307. package/src/resources/extensions/gsd/migration-auto-check.ts +15 -23
  308. package/src/resources/extensions/gsd/milestone-actions.ts +10 -4
  309. package/src/resources/extensions/gsd/native-git-bridge.ts +54 -12
  310. package/src/resources/extensions/gsd/pending-auto-start.ts +79 -0
  311. package/src/resources/extensions/gsd/post-execution-checks.ts +87 -2
  312. package/src/resources/extensions/gsd/pre-execution-checks.ts +32 -1
  313. package/src/resources/extensions/gsd/prompt-loader.ts +1 -1
  314. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  315. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  316. package/src/resources/extensions/gsd/prompts/discuss-headless.md +8 -8
  317. package/src/resources/extensions/gsd/prompts/discuss.md +9 -9
  318. package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +4 -4
  319. package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +3 -3
  320. package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -4
  321. package/src/resources/extensions/gsd/prompts/queue.md +4 -4
  322. package/src/resources/extensions/gsd/prompts/refine-slice.md +2 -2
  323. package/src/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  324. package/src/resources/extensions/gsd/prompts/system.md +2 -2
  325. package/src/resources/extensions/gsd/provider-switch-observer.ts +185 -0
  326. package/src/resources/extensions/gsd/smart-entry-routing.ts +77 -0
  327. package/src/resources/extensions/gsd/state-reconciliation/drift/merge-state.ts +8 -1
  328. package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +12 -15
  329. package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +17 -25
  330. package/src/resources/extensions/gsd/status-guards.ts +5 -0
  331. package/src/resources/extensions/gsd/templates/knowledge.md +2 -2
  332. package/src/resources/extensions/gsd/templates/plan.md +8 -5
  333. package/src/resources/extensions/gsd/templates/task-plan.md +4 -2
  334. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +71 -0
  335. package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +116 -0
  336. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +129 -0
  337. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +487 -4
  338. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +12 -11
  339. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +53 -2
  340. package/src/resources/extensions/gsd/tests/auto-post-unit-step-message.test.ts +12 -1
  341. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +15 -1
  342. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +4 -4
  343. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +1 -0
  344. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +69 -1
  345. package/src/resources/extensions/gsd/tests/brief-command.test.ts +89 -0
  346. package/src/resources/extensions/gsd/tests/browser-tools-compatibility-declarations.test.ts +62 -0
  347. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +107 -2
  348. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +11 -2
  349. package/src/resources/extensions/gsd/tests/closeout-git-deferral.test.ts +16 -0
  350. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +4 -1
  351. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +5 -9
  352. package/src/resources/extensions/gsd/tests/complete-task.test.ts +3 -1
  353. package/src/resources/extensions/gsd/tests/context-store-decisions-from-memories.test.ts +312 -0
  354. package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +43 -2
  355. package/src/resources/extensions/gsd/tests/db-authority-regression.test.ts +208 -0
  356. package/src/resources/extensions/gsd/tests/db-writer.test.ts +13 -8
  357. package/src/resources/extensions/gsd/tests/decisions-projection-from-memories.test.ts +453 -0
  358. package/src/resources/extensions/gsd/tests/decisions-stop-table-writes.test.ts +348 -0
  359. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +59 -2
  360. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +39 -0
  361. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +27 -0
  362. package/src/resources/extensions/gsd/tests/evidence-cross-ref.test.ts +38 -0
  363. package/src/resources/extensions/gsd/tests/export-html-enhancements.test.ts +8 -0
  364. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +8 -4
  365. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +11 -7
  366. package/src/resources/extensions/gsd/tests/guided-discuss-project-prompt-rendering.test.ts +2 -0
  367. package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +106 -0
  368. package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +59 -11
  369. package/src/resources/extensions/gsd/tests/guided-flow.test.ts +21 -0
  370. package/src/resources/extensions/gsd/tests/guided-tool-contract.test.ts +65 -0
  371. package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +7 -7
  372. package/src/resources/extensions/gsd/tests/hook-model-resolution.test.ts +5 -0
  373. package/src/resources/extensions/gsd/tests/infra-error.test.ts +2 -2
  374. package/src/resources/extensions/gsd/tests/infra-errors-cooldown.test.ts +9 -0
  375. package/src/resources/extensions/gsd/tests/integration/doctor-git.test.ts +44 -0
  376. package/src/resources/extensions/gsd/tests/integration/doctor-runtime.test.ts +20 -0
  377. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +103 -1
  378. package/src/resources/extensions/gsd/tests/integration/integration-lifecycle.test.ts +13 -5
  379. package/src/resources/extensions/gsd/tests/integration/migrate-command.test.ts +48 -3
  380. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +6 -1
  381. package/src/resources/extensions/gsd/tests/knowledge-backfill-projection.test.ts +323 -0
  382. package/src/resources/extensions/gsd/tests/knowledge-capture.test.ts +242 -0
  383. package/src/resources/extensions/gsd/tests/knowledge.test.ts +47 -2
  384. package/src/resources/extensions/gsd/tests/load-knowledge-block-rules-only.test.ts +209 -0
  385. package/src/resources/extensions/gsd/tests/memory-consolidation-scanner.test.ts +316 -0
  386. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +5 -1
  387. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +24 -1
  388. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +6 -1
  389. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +26 -18
  390. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +63 -2
  391. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +121 -1
  392. package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +55 -1
  393. package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +29 -5
  394. package/src/resources/extensions/gsd/tests/plan-milestone-sketch-render.test.ts +157 -0
  395. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +26 -0
  396. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +2 -0
  397. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +225 -1
  398. package/src/resources/extensions/gsd/tests/plan-task.test.ts +17 -0
  399. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +79 -1
  400. package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +86 -0
  401. package/src/resources/extensions/gsd/tests/post-unit-git-failure.test.ts +1 -1
  402. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +53 -0
  403. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +8 -0
  404. package/src/resources/extensions/gsd/tests/prompt-loader.test.ts +23 -0
  405. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +20 -1
  406. package/src/resources/extensions/gsd/tests/provider-switch-observer.test.ts +252 -0
  407. package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +46 -2
  408. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +16 -4
  409. package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +10 -0
  410. package/src/resources/extensions/gsd/tests/smart-entry-routing.test.ts +113 -0
  411. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +53 -2
  412. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +6 -0
  413. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +119 -23
  414. package/src/resources/extensions/gsd/tests/stuck-state-via-db.test.ts +64 -1
  415. package/src/resources/extensions/gsd/tests/summary-render-parity.test.ts +7 -3
  416. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +86 -7
  417. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +110 -1
  418. package/src/resources/extensions/gsd/tests/verification-verdict.test.ts +78 -0
  419. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +1 -1
  420. package/src/resources/extensions/gsd/tests/workflow-memory-pressure.test.ts +21 -1
  421. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +1 -1
  422. package/src/resources/extensions/gsd/tests/worktree-git-pathspec.test.ts +39 -0
  423. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +64 -12
  424. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +25 -0
  425. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +54 -0
  426. package/src/resources/extensions/gsd/tools/complete-milestone.ts +8 -10
  427. package/src/resources/extensions/gsd/tools/complete-slice.ts +6 -8
  428. package/src/resources/extensions/gsd/tools/plan-milestone.ts +5 -1
  429. package/src/resources/extensions/gsd/tools/plan-slice.ts +98 -12
  430. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +135 -0
  431. package/src/resources/extensions/gsd/types.ts +1 -1
  432. package/src/resources/extensions/gsd/unit-context-manifest.ts +47 -11
  433. package/src/resources/extensions/gsd/validation.ts +23 -1
  434. package/src/resources/extensions/gsd/verification-gate.ts +78 -6
  435. package/src/resources/extensions/gsd/verification-verdict.ts +47 -0
  436. package/src/resources/extensions/gsd/workflow-logger.ts +4 -0
  437. package/src/resources/extensions/gsd/workflow-projections.ts +6 -8
  438. package/src/resources/extensions/gsd/worktree-lifecycle.ts +61 -10
  439. package/src/resources/extensions/shared/html-shell.ts +412 -0
  440. package/src/resources/extensions/subagent/index.ts +567 -103
  441. package/src/resources/extensions/subagent/launch.ts +131 -0
  442. package/src/resources/extensions/subagent/run-store.ts +218 -0
  443. package/src/resources/extensions/subagent/tests/launch.test.ts +115 -0
  444. package/src/resources/extensions/subagent/tests/run-store.test.ts +111 -0
  445. package/src/resources/extensions/visual-brief/artifact-policy.ts +41 -0
  446. package/src/resources/extensions/visual-brief/extension-manifest.json +8 -0
  447. package/src/resources/extensions/visual-brief/index.ts +8 -0
  448. package/src/resources/extensions/visual-brief/page-contract.ts +136 -0
  449. package/src/resources/extensions/visual-brief/prompts.ts +183 -0
  450. package/src/resources/extensions/visual-brief/tests/visual-brief.test.ts +212 -0
  451. package/dist/web/standalone/.next/server/chunks/5822.js +0 -2
  452. package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +0 -1
  453. package/dist/web/standalone/.next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js +0 -1
  454. package/dist/web/standalone/.next/static/css/54ec2745c1da488b.css +0 -1
  455. package/dist/web/standalone/.next/static/css/de70bee13400563f.css +0 -1
  456. package/dist/web/standalone/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
  457. package/dist/web/standalone/.next/static/media/747892c23ea88013-s.woff2 +0 -0
  458. package/dist/web/standalone/.next/static/media/8d697b304b401681-s.woff2 +0 -0
  459. package/dist/web/standalone/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
  460. package/dist/web/standalone/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
  461. package/dist/web/standalone/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
  462. /package/dist/web/standalone/.next/static/{rIkMv4YSNlfSeqmGqWVns → xRy4LqKSNKdT7y6ATYyEl}/_buildManifest.js +0 -0
  463. /package/dist/web/standalone/.next/static/{rIkMv4YSNlfSeqmGqWVns → xRy4LqKSNKdT7y6ATYyEl}/_ssgManifest.js +0 -0
@@ -14,7 +14,9 @@ import {
14
14
  generatePreview,
15
15
  writeGSDDirectory,
16
16
  } from '../../migrate/index.ts';
17
+ import { importWrittenMigrationToDb } from '../../migrate/command.ts';
17
18
  import { deriveState } from '../../state.ts';
19
+ import { closeDatabase, getDecisionById, getRequirementCounts } from '../../gsd-db.ts';
18
20
  import { describe, test, beforeEach, afterEach } from 'node:test';
19
21
  import assert from 'node:assert/strict';
20
22
 
@@ -52,6 +54,16 @@ const SAMPLE_REQUIREMENTS = `# Requirements
52
54
  - Description: Output matches GSD format.
53
55
  `;
54
56
 
57
+ const SAMPLE_REQUIREMENTS_LEGACY_IDS = `# Requirements
58
+
59
+ ## Active
60
+
61
+ - [ ] **CORE-PIPELINE**: Pipeline must work end-to-end.
62
+ - [ ] **OUTPUT-FORMAT**: Output matches GSD format.
63
+ - [ ] **IMPORT-DB**: Migration imports requirements into the DB.
64
+ - [ ] **STATUS-WIDGET**: Status can query migrated requirements.
65
+ `;
66
+
55
67
  const SAMPLE_STATE = `# State
56
68
 
57
69
  **Current Phase:** 20-features
@@ -166,14 +178,14 @@ Depends on foundation work.
166
178
  </context>
167
179
  `;
168
180
 
169
- function createCompleteFixture(): string {
181
+ function createCompleteFixture(requirementsContent: string = SAMPLE_REQUIREMENTS): string {
170
182
  const base = mkdtempSync(join(tmpdir(), 'gsd-cmd-test-'));
171
183
  const planning = join(base, '.planning');
172
184
  mkdirSync(planning, { recursive: true });
173
185
 
174
186
  writeFileSync(join(planning, 'PROJECT.md'), SAMPLE_PROJECT);
175
187
  writeFileSync(join(planning, 'ROADMAP.md'), SAMPLE_ROADMAP);
176
- writeFileSync(join(planning, 'REQUIREMENTS.md'), SAMPLE_REQUIREMENTS);
188
+ writeFileSync(join(planning, 'REQUIREMENTS.md'), requirementsContent);
177
189
  writeFileSync(join(planning, 'STATE.md'), SAMPLE_STATE);
178
190
  writeFileSync(join(planning, 'config.json'), SAMPLE_CONFIG);
179
191
 
@@ -303,6 +315,7 @@ test('Full pipeline: parse → transform → preview → write → deriveState',
303
315
  const expectedTaskPct = totalTasks > 0 ? Math.round((doneTasks / totalTasks) * 100) : 0;
304
316
  assert.deepStrictEqual(preview.sliceCompletionPct, expectedSlicePct, 'pipeline: preview sliceCompletionPct');
305
317
  assert.deepStrictEqual(preview.taskCompletionPct, expectedTaskPct, 'pipeline: preview taskCompletionPct');
318
+ assert.deepStrictEqual(preview.decisions.total, 1, 'pipeline: preview decisions total');
306
319
 
307
320
  // Requirements in preview
308
321
  assert.deepStrictEqual(preview.requirements.active, 1, 'pipeline: preview requirements active');
@@ -342,6 +355,39 @@ test('Full pipeline: parse → transform → preview → write → deriveState',
342
355
  }
343
356
  });
344
357
 
358
+ test('Full pipeline: legacy requirement IDs import into DB with canonical IDs', async () => {
359
+ const base = createCompleteFixture(SAMPLE_REQUIREMENTS_LEGACY_IDS);
360
+ const writeTarget = mkdtempSync(join(tmpdir(), 'gsd-cmd-legacy-reqs-'));
361
+ try {
362
+ const parsed = await parsePlanningDirectory(join(base, '.planning'));
363
+ const project = transformToGSD(parsed);
364
+ const preview = generatePreview(project);
365
+
366
+ assert.deepStrictEqual(
367
+ project.requirements.map((req) => req.id),
368
+ ['R001', 'R002', 'R003', 'R004'],
369
+ 'legacy-reqs: transform assigns canonical R IDs',
370
+ );
371
+ assert.ok(
372
+ project.requirements[0]?.description.includes('Legacy ID: CORE-PIPELINE'),
373
+ 'legacy-reqs: original ID survives in migrated requirement content',
374
+ );
375
+
376
+ await writeGSDDirectory(project, writeTarget);
377
+ const imported = await importWrittenMigrationToDb(writeTarget, preview);
378
+ const counts = getRequirementCounts();
379
+
380
+ assert.deepStrictEqual(imported.decisions, 1, 'legacy-reqs: DB import includes migrated decisions');
381
+ assert.deepStrictEqual(imported.requirements, 4, 'legacy-reqs: DB import count matches preview');
382
+ assert.ok(getDecisionById('D001') !== null, 'legacy-reqs: migrated decision is queryable');
383
+ assert.deepStrictEqual(counts.total, 4, 'legacy-reqs: DB stores all migrated requirements');
384
+ } finally {
385
+ closeDatabase();
386
+ rmSync(base, { recursive: true, force: true });
387
+ rmSync(writeTarget, { recursive: true, force: true });
388
+ }
389
+ });
390
+
345
391
  // ─── Test 6: .gsd/ exists detection ────────────────────────────────────
346
392
 
347
393
  test('.gsd/ exists detection', () => {
@@ -357,4 +403,3 @@ test('.gsd/ exists detection', () => {
357
403
  rmSync(base, { recursive: true, force: true });
358
404
  }
359
405
  });
360
-
@@ -195,6 +195,11 @@ describe("infrastructure error detection", () => {
195
195
  assert.equal(isInfrastructureError(err), "EAGAIN");
196
196
  });
197
197
 
198
+ test("ENOBUFS (no buffer space available) is detected", () => {
199
+ const err = Object.assign(new Error("spawnSync git ENOBUFS"), { code: "ENOBUFS" });
200
+ assert.equal(isInfrastructureError(err), "ENOBUFS");
201
+ });
202
+
198
203
  test("SQLite WAL corruption is detected via message scan", () => {
199
204
  const err = new Error("database disk image is malformed");
200
205
  assert.equal(isInfrastructureError(err), "SQLITE_CORRUPT");
@@ -223,7 +228,7 @@ describe("infrastructure error detection", () => {
223
228
  test("all INFRA_ERROR_CODES are covered", () => {
224
229
  const expectedCodes = [
225
230
  "ENOSPC", "ENOMEM", "EROFS", "EDQUOT", "EMFILE",
226
- "ENFILE", "EAGAIN", "ECONNREFUSED", "ENOTFOUND", "ENETUNREACH",
231
+ "ENFILE", "EAGAIN", "ENOBUFS", "ECONNREFUSED", "ENOTFOUND", "ENETUNREACH",
227
232
  ];
228
233
  for (const code of expectedCodes) {
229
234
  assert.ok(INFRA_ERROR_CODES.has(code), `${code} should be in INFRA_ERROR_CODES`);
@@ -0,0 +1,323 @@
1
+ // ADR-013 Stage 2b — KNOWLEDGE.md backfill + hybrid projection tests.
2
+ //
3
+ // Covers four behaviors:
4
+ // 1. parser: splits cells correctly, skips header/separator rows, respects
5
+ // section boundaries
6
+ // 2. backfill: Patterns -> memories(category=pattern), Lessons ->
7
+ // memories(category=gotcha), Rules NOT migrated, idempotent
8
+ // 3. projection: hybrid output preserves manual Rules verbatim while
9
+ // projecting Patterns + Lessons from memories
10
+ // 4. bootstrap path: backfill + projection round-trip produces a stable
11
+ // file that re-reading reconstitutes the same memory set
12
+
13
+ import test from "node:test";
14
+ import assert from "node:assert/strict";
15
+ import {
16
+ existsSync,
17
+ mkdirSync,
18
+ mkdtempSync,
19
+ readFileSync,
20
+ rmSync,
21
+ writeFileSync,
22
+ } from "node:fs";
23
+ import { join } from "node:path";
24
+ import { tmpdir } from "node:os";
25
+
26
+ import {
27
+ _getAdapter,
28
+ closeDatabase,
29
+ openDatabase,
30
+ } from "../gsd-db.ts";
31
+ import { backfillKnowledgeToMemories } from "../knowledge-backfill.ts";
32
+ import {
33
+ knowledgeMdPath,
34
+ parseKnowledgeRows,
35
+ splitPipeRow,
36
+ } from "../knowledge-parser.ts";
37
+ import { renderKnowledgeProjection } from "../knowledge-projection.ts";
38
+
39
+ function makeTmpBase(): string {
40
+ const base = mkdtempSync(join(tmpdir(), "gsd-knowledge-stage2b-"));
41
+ mkdirSync(join(base, ".gsd"), { recursive: true });
42
+ openDatabase(join(base, ".gsd", "gsd.db"));
43
+ return base;
44
+ }
45
+
46
+ function cleanup(base: string): void {
47
+ try {
48
+ closeDatabase();
49
+ } catch {
50
+ /* noop */
51
+ }
52
+ try {
53
+ rmSync(base, { recursive: true, force: true });
54
+ } catch {
55
+ /* noop */
56
+ }
57
+ }
58
+
59
+ function writeKnowledgeMd(base: string, body: string): void {
60
+ writeFileSync(join(base, ".gsd", "KNOWLEDGE.md"), body, "utf-8");
61
+ }
62
+
63
+ const FIXTURE = `# Project Knowledge
64
+
65
+ Append-only register of project-specific rules, patterns, and lessons learned.
66
+ Agents read this before every unit. Add entries when you discover something worth remembering.
67
+
68
+ ## Rules
69
+
70
+ | # | Scope | Rule | Why | Added |
71
+ |---|-------|------|-----|-------|
72
+ | K001 | project | All timestamps in UTC | clarity | 2026-01-01 |
73
+ | K002 | M001 | Never trust user input | safety | 2026-01-02 |
74
+
75
+ ## Patterns
76
+
77
+ | # | Pattern | Where | Notes |
78
+ |---|---------|-------|-------|
79
+ | P001 | Repository pattern | services/ | guards |
80
+ | P002 | Adapter at the seam | packages/pi-ai/ | observability |
81
+
82
+ ## Lessons Learned
83
+
84
+ | # | What Happened | Root Cause | Fix | Scope |
85
+ |---|--------------|------------|-----|-------|
86
+ | L001 | Cache poisoning | reused key | versioned key | project |
87
+ `;
88
+
89
+ // ─── splitPipeRow ──────────────────────────────────────────────────────────
90
+
91
+ test("splitPipeRow extracts cells from a standard table row", () => {
92
+ const cells = splitPipeRow("| K001 | project | All timestamps in UTC | clarity | 2026-01-01 |");
93
+ assert.deepEqual(cells, ["K001", "project", "All timestamps in UTC", "clarity", "2026-01-01"]);
94
+ });
95
+
96
+ test("splitPipeRow preserves escaped pipes inside a cell", () => {
97
+ const cells = splitPipeRow(`| K001 | project | Use A \\| B operator | safety | 2026-01-01 |`);
98
+ assert.equal(cells[2], "Use A | B operator");
99
+ });
100
+
101
+ // ─── parseKnowledgeRows ────────────────────────────────────────────────────
102
+
103
+ test("parseKnowledgeRows returns rows per section with the correct table tag", () => {
104
+ const rows = parseKnowledgeRows(FIXTURE);
105
+ assert.equal(rows.length, 5, "two rules + two patterns + one lesson");
106
+ const tables = rows.map((r) => r.table);
107
+ assert.deepEqual(tables, ["rules", "rules", "patterns", "patterns", "lessons"]);
108
+ });
109
+
110
+ test("parseKnowledgeRows captures cell values aligned with the section schema", () => {
111
+ const rows = parseKnowledgeRows(FIXTURE);
112
+ const p1 = rows.find((r) => r.id === "P001");
113
+ assert.ok(p1);
114
+ assert.equal(p1.cells[1], "Repository pattern");
115
+ assert.equal(p1.cells[2], "services/");
116
+ });
117
+
118
+ // ─── backfillKnowledgeToMemories ───────────────────────────────────────────
119
+
120
+ test("backfill migrates Patterns + Lessons but skips Rules", () => {
121
+ const base = makeTmpBase();
122
+ try {
123
+ writeKnowledgeMd(base, FIXTURE);
124
+ const written = backfillKnowledgeToMemories(base);
125
+ assert.equal(written, 3, "P001 + P002 + L001 should migrate; K rows skipped");
126
+
127
+ const adapter = _getAdapter();
128
+ assert.ok(adapter);
129
+
130
+ const knowledgeMemories = adapter
131
+ .prepare(
132
+ "SELECT category, structured_fields FROM memories WHERE structured_fields LIKE '%\"sourceKnowledgeId\":\"%' ORDER BY seq",
133
+ )
134
+ .all() as Array<{ category: string; structured_fields: string }>;
135
+
136
+ assert.equal(knowledgeMemories.length, 3);
137
+ const categoriesById: Record<string, string> = {};
138
+ for (const m of knowledgeMemories) {
139
+ const sf = JSON.parse(m.structured_fields) as { sourceKnowledgeId: string };
140
+ categoriesById[sf.sourceKnowledgeId] = m.category;
141
+ }
142
+ assert.equal(categoriesById["P001"], "pattern");
143
+ assert.equal(categoriesById["P002"], "pattern");
144
+ assert.equal(categoriesById["L001"], "gotcha");
145
+ assert.equal(categoriesById["K001"], undefined, "K001 must NOT be in memories");
146
+ } finally {
147
+ cleanup(base);
148
+ }
149
+ });
150
+
151
+ test("backfill is idempotent — second run on the same file is a no-op", () => {
152
+ const base = makeTmpBase();
153
+ try {
154
+ writeKnowledgeMd(base, FIXTURE);
155
+ const first = backfillKnowledgeToMemories(base);
156
+ assert.equal(first, 3);
157
+ const second = backfillKnowledgeToMemories(base);
158
+ assert.equal(second, 0, "already-migrated rows must not be re-inserted");
159
+ } finally {
160
+ cleanup(base);
161
+ }
162
+ });
163
+
164
+ test("backfill returns 0 when KNOWLEDGE.md is absent", () => {
165
+ const base = makeTmpBase();
166
+ try {
167
+ assert.equal(backfillKnowledgeToMemories(base), 0);
168
+ } finally {
169
+ cleanup(base);
170
+ }
171
+ });
172
+
173
+ // ─── renderKnowledgeProjection ─────────────────────────────────────────────
174
+
175
+ test("projection preserves the manual Rules section verbatim", () => {
176
+ const base = makeTmpBase();
177
+ try {
178
+ writeKnowledgeMd(base, FIXTURE);
179
+ backfillKnowledgeToMemories(base);
180
+ renderKnowledgeProjection(base);
181
+
182
+ const rendered = readFileSync(knowledgeMdPath(base), "utf-8");
183
+ // Both K-rows must appear unchanged.
184
+ assert.match(rendered, /\| K001 \| project \| All timestamps in UTC \| clarity \| 2026-01-01 \|/);
185
+ assert.match(rendered, /\| K002 \| M001 \| Never trust user input \| safety \| 2026-01-02 \|/);
186
+ } finally {
187
+ cleanup(base);
188
+ }
189
+ });
190
+
191
+ test("projection renders Patterns + Lessons from memories", () => {
192
+ const base = makeTmpBase();
193
+ try {
194
+ writeKnowledgeMd(base, FIXTURE);
195
+ backfillKnowledgeToMemories(base);
196
+ // Wipe the original Patterns/Lessons table rows from the file so the
197
+ // projection's output can ONLY come from memories. Keep Rules intact.
198
+ writeKnowledgeMd(
199
+ base,
200
+ `# Project Knowledge
201
+
202
+ ## Rules
203
+
204
+ | # | Scope | Rule | Why | Added |
205
+ |---|-------|------|-----|-------|
206
+ | K001 | project | All timestamps in UTC | clarity | 2026-01-01 |
207
+ | K002 | M001 | Never trust user input | safety | 2026-01-02 |
208
+ `,
209
+ );
210
+
211
+ renderKnowledgeProjection(base);
212
+ const rendered = readFileSync(knowledgeMdPath(base), "utf-8");
213
+
214
+ assert.match(rendered, /\| P001 \| Repository pattern \| services\/ \| guards \|/);
215
+ assert.match(rendered, /\| P002 \| Adapter at the seam \| packages\/pi-ai\/ \| observability \|/);
216
+ assert.match(rendered, /\| L001 \| Cache poisoning \| reused key \| versioned key \| project \|/);
217
+
218
+ // Section structure must still be the canonical three headings, in order.
219
+ const rulesIdx = rendered.indexOf("## Rules");
220
+ const patternsIdx = rendered.indexOf("## Patterns");
221
+ const lessonsIdx = rendered.indexOf("## Lessons Learned");
222
+ assert.ok(rulesIdx >= 0 && patternsIdx > rulesIdx && lessonsIdx > patternsIdx, "headings must appear in canonical order");
223
+ } finally {
224
+ cleanup(base);
225
+ }
226
+ });
227
+
228
+ test("projection is idempotent when nothing has changed", () => {
229
+ const base = makeTmpBase();
230
+ try {
231
+ // Seed a file the projection will *change* (no canonical headers yet, so
232
+ // the first render must rewrite to add structure).
233
+ writeKnowledgeMd(
234
+ base,
235
+ `# Project Knowledge
236
+
237
+ ## Rules
238
+
239
+ | # | Scope | Rule | Why | Added |
240
+ |---|-------|------|-----|-------|
241
+ | K001 | project | manual rule | reason | 2026-01-01 |
242
+ `,
243
+ );
244
+
245
+ const first = renderKnowledgeProjection(base);
246
+ assert.equal(first.written, true, "first render adds the missing Patterns + Lessons section scaffolding");
247
+
248
+ const second = renderKnowledgeProjection(base);
249
+ assert.equal(second.written, false, "second render is a no-op when content already matches");
250
+ } finally {
251
+ cleanup(base);
252
+ }
253
+ });
254
+
255
+ test("projection emits empty section tables when no rows exist for that category", () => {
256
+ const base = makeTmpBase();
257
+ try {
258
+ writeKnowledgeMd(
259
+ base,
260
+ `# Project Knowledge
261
+
262
+ ## Rules
263
+
264
+ | # | Scope | Rule | Why | Added |
265
+ |---|-------|------|-----|-------|
266
+ `,
267
+ );
268
+ renderKnowledgeProjection(base);
269
+ const rendered = readFileSync(knowledgeMdPath(base), "utf-8");
270
+
271
+ assert.match(rendered, /## Patterns/);
272
+ assert.match(rendered, /## Lessons Learned/);
273
+ // The empty-table headers must be present.
274
+ assert.match(rendered, /\| # \| Pattern \| Where \| Notes \|/);
275
+ assert.match(rendered, /\| # \| What Happened \| Root Cause \| Fix \| Scope \|/);
276
+ } finally {
277
+ cleanup(base);
278
+ }
279
+ });
280
+
281
+ test("projection escapes pipes in memory content", () => {
282
+ const base = makeTmpBase();
283
+ try {
284
+ writeKnowledgeMd(
285
+ base,
286
+ `## Patterns
287
+
288
+ | # | Pattern | Where | Notes |
289
+ |---|---------|-------|-------|
290
+ | P001 | Use A \\| B fallback | adapters/ | watch out |
291
+ `,
292
+ );
293
+ backfillKnowledgeToMemories(base);
294
+ renderKnowledgeProjection(base);
295
+ const rendered = readFileSync(knowledgeMdPath(base), "utf-8");
296
+
297
+ // Pipe MUST stay escaped in the rendered output so the table doesn't break.
298
+ assert.match(rendered, /\| P001 \| Use A \\\| B fallback \| adapters\/ \| watch out \|/);
299
+ } finally {
300
+ cleanup(base);
301
+ }
302
+ });
303
+
304
+ // ─── End-to-end round-trip ─────────────────────────────────────────────────
305
+
306
+ test("backfill + projection round-trip: re-running backfill on rendered file is a no-op", () => {
307
+ const base = makeTmpBase();
308
+ try {
309
+ writeKnowledgeMd(base, FIXTURE);
310
+ const initial = backfillKnowledgeToMemories(base);
311
+ assert.equal(initial, 3);
312
+
313
+ renderKnowledgeProjection(base);
314
+ assert.ok(existsSync(knowledgeMdPath(base)));
315
+
316
+ // The rendered file contains the same P/L IDs as the source. A second
317
+ // backfill pass must NOT re-insert them.
318
+ const second = backfillKnowledgeToMemories(base);
319
+ assert.equal(second, 0, "round-trip rendered file must remain idempotent for backfill");
320
+ } finally {
321
+ cleanup(base);
322
+ }
323
+ });
@@ -0,0 +1,242 @@
1
+ // ADR-013 Stage 2c — /gsd knowledge write-side redirect tests.
2
+ //
3
+ // Locks in four properties of captureKnowledgeEntry / nextKnowledgeId:
4
+ // 1. Pattern entries write a memory row with category="pattern" and a
5
+ // sourceKnowledgeId marker — but do NOT touch KNOWLEDGE.md.
6
+ // 2. Lesson entries write a memory row with category="gotcha" — same
7
+ // file-vs-memory split.
8
+ // 3. Next-ID is monotonic across both surfaces: a P004 in the file plus a
9
+ // P007 in memories yields P008 next.
10
+ // 4. The legacy appendKnowledge stays the canonical path for Rules
11
+ // (covered indirectly via no-memory side effect on type="rule").
12
+
13
+ import test from "node:test";
14
+ import assert from "node:assert/strict";
15
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
16
+ import { join } from "node:path";
17
+ import { tmpdir } from "node:os";
18
+
19
+ import {
20
+ _getAdapter,
21
+ closeDatabase,
22
+ openDatabase,
23
+ } from "../gsd-db.ts";
24
+ import { captureKnowledgeEntry, nextKnowledgeId } from "../knowledge-capture.ts";
25
+ import { knowledgeMdPath } from "../knowledge-parser.ts";
26
+
27
+ function makeTmpBase(): string {
28
+ const base = mkdtempSync(join(tmpdir(), "gsd-knowledge-capture-"));
29
+ mkdirSync(join(base, ".gsd"), { recursive: true });
30
+ openDatabase(join(base, ".gsd", "gsd.db"));
31
+ return base;
32
+ }
33
+
34
+ function cleanup(base: string): void {
35
+ try {
36
+ closeDatabase();
37
+ } catch {
38
+ /* noop */
39
+ }
40
+ try {
41
+ rmSync(base, { recursive: true, force: true });
42
+ } catch {
43
+ /* noop */
44
+ }
45
+ }
46
+
47
+ function readMemoriesWithMarker(prefix: "P" | "L"): Array<{ category: string; sf: Record<string, unknown> }> {
48
+ const adapter = _getAdapter();
49
+ if (!adapter) return [];
50
+ const rows = adapter
51
+ .prepare(
52
+ "SELECT category, structured_fields FROM memories WHERE structured_fields LIKE :pattern ORDER BY seq",
53
+ )
54
+ .all({ ":pattern": `%"sourceKnowledgeId":"${prefix}%` }) as Array<{
55
+ category: string;
56
+ structured_fields: string;
57
+ }>;
58
+ return rows.map((r) => ({ category: r.category, sf: JSON.parse(r.structured_fields) }));
59
+ }
60
+
61
+ // ─── Pattern path ───────────────────────────────────────────────────────────
62
+
63
+ test("pattern entry creates a memory and assigns the next P### id", () => {
64
+ const base = makeTmpBase();
65
+ try {
66
+ const result = captureKnowledgeEntry(base, "pattern", "Repository pattern at the seam", "M001");
67
+ assert.equal(result.id, "P001", "first pattern on a fresh project should be P001");
68
+ assert.equal(result.written, true);
69
+
70
+ const memories = readMemoriesWithMarker("P");
71
+ assert.equal(memories.length, 1);
72
+ assert.equal(memories[0]?.category, "pattern");
73
+ assert.equal(memories[0]?.sf.sourceKnowledgeId, "P001");
74
+ assert.equal(memories[0]?.sf.pattern, "Repository pattern at the seam");
75
+ assert.equal(memories[0]?.sf.where, "");
76
+ assert.equal(memories[0]?.sf.notes, "");
77
+ } finally {
78
+ cleanup(base);
79
+ }
80
+ });
81
+
82
+ test("pattern entry does NOT write to KNOWLEDGE.md (memory-only)", () => {
83
+ const base = makeTmpBase();
84
+ try {
85
+ captureKnowledgeEntry(base, "pattern", "Adapter at the seam", "project");
86
+ assert.equal(
87
+ existsSync(knowledgeMdPath(base)),
88
+ false,
89
+ "KNOWLEDGE.md should remain untouched — projection re-renders on next session start",
90
+ );
91
+ } finally {
92
+ cleanup(base);
93
+ }
94
+ });
95
+
96
+ test("repeated pattern captures advance the ID monotonically", () => {
97
+ const base = makeTmpBase();
98
+ try {
99
+ const a = captureKnowledgeEntry(base, "pattern", "First", "project");
100
+ const b = captureKnowledgeEntry(base, "pattern", "Second", "project");
101
+ const c = captureKnowledgeEntry(base, "pattern", "Third", "project");
102
+ assert.deepEqual([a.id, b.id, c.id], ["P001", "P002", "P003"]);
103
+ } finally {
104
+ cleanup(base);
105
+ }
106
+ });
107
+
108
+ // ─── Lesson path ────────────────────────────────────────────────────────────
109
+
110
+ test("lesson entry creates a memory with category=gotcha and L### id", () => {
111
+ const base = makeTmpBase();
112
+ try {
113
+ const result = captureKnowledgeEntry(base, "lesson", "Cache poisoning via reused key", "M001/S01");
114
+ assert.equal(result.id, "L001");
115
+ assert.equal(result.written, true);
116
+
117
+ const memories = readMemoriesWithMarker("L");
118
+ assert.equal(memories.length, 1);
119
+ assert.equal(memories[0]?.category, "gotcha");
120
+ assert.equal(memories[0]?.sf.sourceKnowledgeId, "L001");
121
+ assert.equal(memories[0]?.sf.whatHappened, "Cache poisoning via reused key");
122
+ assert.equal(memories[0]?.sf.scopeText, "M001/S01");
123
+ } finally {
124
+ cleanup(base);
125
+ }
126
+ });
127
+
128
+ // ─── Empty input ────────────────────────────────────────────────────────────
129
+
130
+ test("empty entry text does NOT create a memory but still assigns an id", () => {
131
+ const base = makeTmpBase();
132
+ try {
133
+ const result = captureKnowledgeEntry(base, "pattern", " ", "project");
134
+ assert.equal(result.written, false);
135
+ assert.equal(readMemoriesWithMarker("P").length, 0);
136
+ } finally {
137
+ cleanup(base);
138
+ }
139
+ });
140
+
141
+ // ─── nextKnowledgeId — cross-surface monotonicity ───────────────────────────
142
+
143
+ test("nextKnowledgeId takes max of file and memory surfaces", () => {
144
+ const base = makeTmpBase();
145
+ try {
146
+ // Pre-existing patterns in KNOWLEDGE.md and one new pattern in memories.
147
+ // The legacy appendKnowledge format starts P at 001 — we simulate higher
148
+ // existing IDs to verify the take-max-then-+1 rule.
149
+ writeFileSync(
150
+ knowledgeMdPath(base),
151
+ `## Patterns
152
+
153
+ | # | Pattern | Where | Notes |
154
+ |---|---------|-------|-------|
155
+ | P004 | Legacy pattern | services/ | preserved |
156
+ `,
157
+ "utf-8",
158
+ );
159
+
160
+ // Capture jumps the memory ID forward.
161
+ captureKnowledgeEntry(base, "pattern", "Memory-only pattern", "project");
162
+ captureKnowledgeEntry(base, "pattern", "Another memory-only pattern", "project");
163
+ captureKnowledgeEntry(base, "pattern", "And another", "project");
164
+
165
+ // After: file has P004 (max=4); memories have P005, P006, P007 (max=7).
166
+ // Next must be P008.
167
+ assert.equal(nextKnowledgeId(base, "P"), "P008");
168
+ } finally {
169
+ cleanup(base);
170
+ }
171
+ });
172
+
173
+ test("nextKnowledgeId works per-prefix without cross-talk", () => {
174
+ const base = makeTmpBase();
175
+ try {
176
+ captureKnowledgeEntry(base, "pattern", "P entry", "project");
177
+ captureKnowledgeEntry(base, "pattern", "P entry 2", "project");
178
+ captureKnowledgeEntry(base, "lesson", "L entry", "project");
179
+
180
+ assert.equal(nextKnowledgeId(base, "P"), "P003");
181
+ assert.equal(nextKnowledgeId(base, "L"), "L002");
182
+ // K has no entries on this project — first one should be K001.
183
+ assert.equal(nextKnowledgeId(base, "K"), "K001");
184
+ } finally {
185
+ cleanup(base);
186
+ }
187
+ });
188
+
189
+ test("nextKnowledgeId pads to three digits even at the rollover", () => {
190
+ const base = makeTmpBase();
191
+ try {
192
+ writeFileSync(
193
+ knowledgeMdPath(base),
194
+ `## Patterns
195
+
196
+ | # | Pattern | Where | Notes |
197
+ |---|---------|-------|-------|
198
+ | P099 | high water mark | — | — |
199
+ `,
200
+ "utf-8",
201
+ );
202
+ assert.equal(nextKnowledgeId(base, "P"), "P100");
203
+ } finally {
204
+ cleanup(base);
205
+ }
206
+ });
207
+
208
+ // ─── Sanity: file-side reads still see existing rows after capture ─────────
209
+
210
+ test("after pattern capture, KNOWLEDGE.md untouched but next id reflects memory state", () => {
211
+ const base = makeTmpBase();
212
+ try {
213
+ captureKnowledgeEntry(base, "pattern", "First", "project");
214
+ // KNOWLEDGE.md still absent.
215
+ assert.equal(existsSync(knowledgeMdPath(base)), false);
216
+ // Next-ID logic sees the new memory.
217
+ assert.equal(nextKnowledgeId(base, "P"), "P002");
218
+ } finally {
219
+ cleanup(base);
220
+ }
221
+ });
222
+
223
+ test("rule writes still flow through appendKnowledge (file-canonical) — memory unchanged", async () => {
224
+ const base = makeTmpBase();
225
+ try {
226
+ // We invoke the legacy file path directly here; the command handler
227
+ // dispatch is verified separately at the integration level. The point
228
+ // of this test is to lock in that captureKnowledgeEntry is NOT the
229
+ // rule path — rules must go through appendKnowledge.
230
+ const { appendKnowledge } = await import("../files.ts");
231
+ await appendKnowledge(base, "rule", "Always pin SQLite version", "project");
232
+
233
+ const md = readFileSync(knowledgeMdPath(base), "utf-8");
234
+ assert.match(md, /\| K001 \| project \| Always pin SQLite version /);
235
+
236
+ // No pattern/lesson memories should exist as a side effect.
237
+ assert.equal(readMemoriesWithMarker("P").length, 0);
238
+ assert.equal(readMemoriesWithMarker("L").length, 0);
239
+ } finally {
240
+ cleanup(base);
241
+ }
242
+ });