gsd-pi 2.44.0 → 2.45.0-dev.6b9da3e
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.
- package/README.md +30 -12
- package/dist/resources/extensions/gsd/activity-log.js +7 -0
- package/dist/resources/extensions/gsd/auto/infra-errors.js +3 -0
- package/dist/resources/extensions/gsd/auto/phases.js +37 -36
- package/dist/resources/extensions/gsd/auto-prompts.js +24 -1
- package/dist/resources/extensions/gsd/auto-start.js +31 -2
- package/dist/resources/extensions/gsd/auto-timers.js +57 -3
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +4 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +9 -6
- package/dist/resources/extensions/gsd/auto.js +30 -3
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +156 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -12
- package/dist/resources/extensions/gsd/commands/catalog.js +7 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +2 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +10 -0
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
- package/dist/resources/extensions/gsd/commands-mcp-status.js +187 -0
- package/dist/resources/extensions/gsd/db-writer.js +34 -16
- package/dist/resources/extensions/gsd/doctor.js +8 -0
- package/dist/resources/extensions/gsd/git-service.js +8 -3
- package/dist/resources/extensions/gsd/gsd-db.js +12 -1
- package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
- package/dist/resources/extensions/gsd/preferences.js +9 -1
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +3 -14
- package/dist/resources/extensions/gsd/prompts/rethink.md +78 -0
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
- package/dist/resources/extensions/gsd/provider-error-pause.js +7 -0
- package/dist/resources/extensions/gsd/repo-identity.js +45 -7
- package/dist/resources/extensions/gsd/rethink.js +115 -0
- package/dist/resources/extensions/gsd/state.js +41 -3
- package/dist/resources/extensions/gsd/tools/plan-slice.js +1 -0
- package/dist/resources/extensions/gsd/tools/plan-task.js +1 -0
- package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -0
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +88 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +32 -2
- package/dist/resources/extensions/gsd/worktree-resolver.js +6 -0
- package/dist/resources/extensions/mcp-client/index.js +14 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
- package/dist/web/standalone/.next/build-manifest.json +4 -4
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
- package/dist/web/standalone/.next/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +5 -5
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +5 -5
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +6 -6
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +6 -6
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
- package/dist/web/standalone/.next/server/chunks/229.js +1 -1
- package/dist/web/standalone/.next/server/chunks/471.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/4024.11ca5c01938e5948.js +9 -0
- package/dist/web/standalone/.next/static/chunks/{3721.bf31263de6d5fa46.js → 485.243af25f0cdf50d6.js} +2 -2
- package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-6654a8cca61a3d1c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
- package/dist/web/standalone/.next/static/chunks/webpack-0a4cd455ec4197d2.js +1 -0
- package/dist/web/standalone/.next/static/css/dd4ae3f58ac9b600.css +1 -0
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/native/dist/stream-process/index.js +2 -2
- package/packages/native/src/__tests__/stream-process.test.mjs +34 -0
- package/packages/native/src/stream-process/index.ts +2 -2
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +3 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +15 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
- package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +15 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.js +41 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +11 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +20 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
- package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +6 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +17 -0
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +32 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +8 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +15 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +40 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +4 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +5 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +13 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +17 -8
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -3
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
- package/packages/pi-coding-agent/src/core/auth-storage.ts +15 -1
- package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
- package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
- package/packages/pi-coding-agent/src/core/local-model-check.ts +45 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +21 -1
- package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
- package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
- package/packages/pi-coding-agent/src/core/settings-manager.ts +9 -0
- package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
- package/packages/pi-coding-agent/src/main.ts +19 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +38 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +10 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +48 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +3 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +18 -3
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +16 -7
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +8 -1
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/activity-log.ts +1 -0
- package/src/resources/extensions/gsd/auto/infra-errors.ts +3 -0
- package/src/resources/extensions/gsd/auto/phases.ts +46 -48
- package/src/resources/extensions/gsd/auto-prompts.ts +24 -1
- package/src/resources/extensions/gsd/auto-start.ts +39 -2
- package/src/resources/extensions/gsd/auto-timers.ts +64 -3
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +5 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +9 -6
- package/src/resources/extensions/gsd/auto.ts +37 -3
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +148 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +48 -11
- package/src/resources/extensions/gsd/commands/catalog.ts +7 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +2 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +10 -0
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
- package/src/resources/extensions/gsd/commands-mcp-status.ts +247 -0
- package/src/resources/extensions/gsd/db-writer.ts +39 -17
- package/src/resources/extensions/gsd/doctor.ts +7 -1
- package/src/resources/extensions/gsd/git-service.ts +6 -2
- package/src/resources/extensions/gsd/gsd-db.ts +16 -1
- package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
- package/src/resources/extensions/gsd/preferences.ts +11 -1
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
- package/src/resources/extensions/gsd/prompts/replan-slice.md +3 -14
- package/src/resources/extensions/gsd/prompts/rethink.md +78 -0
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
- package/src/resources/extensions/gsd/provider-error-pause.ts +9 -0
- package/src/resources/extensions/gsd/repo-identity.ts +46 -7
- package/src/resources/extensions/gsd/rethink.ts +154 -0
- package/src/resources/extensions/gsd/state.ts +41 -1
- package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
- package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
- package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
- package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +88 -0
- package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +465 -523
- package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
- package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +533 -656
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
- package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
- package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
- package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
- package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
- package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
- package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +38 -59
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +228 -263
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +250 -302
- package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +114 -0
- package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
- package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
- package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +465 -416
- package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
- package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +68 -83
- package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +121 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +210 -181
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
- package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
- package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +164 -180
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
- package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
- package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +27 -29
- package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
- package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +54 -75
- package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +21 -32
- package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +38 -44
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
- package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +54 -60
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
- package/src/resources/extensions/gsd/tests/doctor.test.ts +104 -134
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
- package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
- package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
- package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +30 -42
- package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +198 -206
- package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
- package/src/resources/extensions/gsd/tests/git-service.test.ts +285 -388
- package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
- package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
- package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
- package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +81 -103
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
- package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
- package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -102
- package/src/resources/extensions/gsd/tests/infra-error.test.ts +20 -2
- package/src/resources/extensions/gsd/tests/inherited-repo-home-dir.test.ts +121 -0
- package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
- package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
- package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
- package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
- package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
- package/src/resources/extensions/gsd/tests/knowledge.test.ts +89 -0
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +150 -194
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
- package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +80 -93
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
- package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +83 -93
- package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
- package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
- package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +87 -96
- package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +125 -164
- package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +35 -36
- package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
- package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
- package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
- package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
- package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
- package/src/resources/extensions/gsd/tests/parsers.test.ts +546 -611
- package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
- package/src/resources/extensions/gsd/tests/preferences.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +11 -7
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
- package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
- package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
- package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
- package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
- package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
- package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +176 -0
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
- package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
- package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
- package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
- package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
- package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
- package/src/resources/extensions/gsd/tests/shared-wal.test.ts +19 -26
- package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
- package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +67 -0
- package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +108 -0
- package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
- package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +10 -11
- package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
- package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
- package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
- package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
- package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
- package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
- package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
- package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
- package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
- package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +65 -0
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
- package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
- package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +2 -0
- package/src/resources/extensions/gsd/tools/replan-slice.ts +3 -0
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +127 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +43 -2
- package/src/resources/extensions/gsd/worktree-resolver.ts +7 -0
- package/src/resources/extensions/mcp-client/index.ts +20 -0
- package/dist/web/standalone/.next/static/chunks/4024.0de81b543b28b9fe.js +0 -9
- package/dist/web/standalone/.next/static/chunks/app/page-7e9530a7122506c5.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
- package/dist/web/standalone/.next/static/chunks/webpack-9014b5adb127a98a.js +0 -1
- package/dist/web/standalone/.next/static/css/8a727f372cf53002.css +0 -1
- /package/dist/web/standalone/.next/static/{mgkxN0mGP6gSUmGPEzbk_ → rzO54ZboyINyEt7cVM_uS}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{mgkxN0mGP6gSUmGPEzbk_ → rzO54ZboyINyEt7cVM_uS}/_ssgManifest.js +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { describe, test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
1
3
|
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
4
|
import { join } from 'node:path';
|
|
3
5
|
import { tmpdir } from 'node:os';
|
|
@@ -9,13 +11,10 @@ import {
|
|
|
9
11
|
insertArtifact,
|
|
10
12
|
isDbAvailable,
|
|
11
13
|
insertMilestone,
|
|
14
|
+
getAllMilestones,
|
|
12
15
|
insertSlice,
|
|
13
16
|
insertTask,
|
|
14
17
|
} from '../gsd-db.ts';
|
|
15
|
-
import { createTestContext } from './test-helpers.ts';
|
|
16
|
-
|
|
17
|
-
const { assertEq, assertTrue, report } = createTestContext();
|
|
18
|
-
|
|
19
18
|
// ─── Fixture Helpers ───────────────────────────────────────────────────────
|
|
20
19
|
|
|
21
20
|
function createFixtureBase(): string {
|
|
@@ -100,11 +99,10 @@ const REQUIREMENTS_CONTENT = `# Requirements
|
|
|
100
99
|
- Description: Already validated.
|
|
101
100
|
`;
|
|
102
101
|
|
|
103
|
-
async
|
|
102
|
+
describe('derive-state-db', async () => {
|
|
104
103
|
|
|
105
104
|
// ─── Test 1: DB-backed deriveState produces identical GSDState ─────────
|
|
106
|
-
|
|
107
|
-
{
|
|
105
|
+
test('derive-state-db: DB path matches file path', async () => {
|
|
108
106
|
const base = createFixtureBase();
|
|
109
107
|
try {
|
|
110
108
|
// Write files to disk (for file-only path)
|
|
@@ -120,7 +118,7 @@ async function main(): Promise<void> {
|
|
|
120
118
|
|
|
121
119
|
// Now open DB, insert matching artifacts
|
|
122
120
|
openDatabase(':memory:');
|
|
123
|
-
|
|
121
|
+
assert.ok(isDbAvailable(), 'db-match: DB is available after open');
|
|
124
122
|
|
|
125
123
|
insertArtifactRow('milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT, {
|
|
126
124
|
artifact_type: 'roadmap',
|
|
@@ -140,36 +138,35 @@ async function main(): Promise<void> {
|
|
|
140
138
|
const dbState = await deriveState(base);
|
|
141
139
|
|
|
142
140
|
// Field-by-field equality
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
141
|
+
assert.deepStrictEqual(dbState.phase, fileState.phase, 'db-match: phase matches');
|
|
142
|
+
assert.deepStrictEqual(dbState.activeMilestone?.id, fileState.activeMilestone?.id, 'db-match: activeMilestone.id matches');
|
|
143
|
+
assert.deepStrictEqual(dbState.activeMilestone?.title, fileState.activeMilestone?.title, 'db-match: activeMilestone.title matches');
|
|
144
|
+
assert.deepStrictEqual(dbState.activeSlice?.id, fileState.activeSlice?.id, 'db-match: activeSlice.id matches');
|
|
145
|
+
assert.deepStrictEqual(dbState.activeSlice?.title, fileState.activeSlice?.title, 'db-match: activeSlice.title matches');
|
|
146
|
+
assert.deepStrictEqual(dbState.activeTask?.id, fileState.activeTask?.id, 'db-match: activeTask.id matches');
|
|
147
|
+
assert.deepStrictEqual(dbState.activeTask?.title, fileState.activeTask?.title, 'db-match: activeTask.title matches');
|
|
148
|
+
assert.deepStrictEqual(dbState.blockers, fileState.blockers, 'db-match: blockers match');
|
|
149
|
+
assert.deepStrictEqual(dbState.registry.length, fileState.registry.length, 'db-match: registry length matches');
|
|
150
|
+
assert.deepStrictEqual(dbState.registry[0]?.status, fileState.registry[0]?.status, 'db-match: registry[0] status matches');
|
|
151
|
+
assert.deepStrictEqual(dbState.requirements?.active, fileState.requirements?.active, 'db-match: requirements.active matches');
|
|
152
|
+
assert.deepStrictEqual(dbState.requirements?.validated, fileState.requirements?.validated, 'db-match: requirements.validated matches');
|
|
153
|
+
assert.deepStrictEqual(dbState.requirements?.total, fileState.requirements?.total, 'db-match: requirements.total matches');
|
|
154
|
+
assert.deepStrictEqual(dbState.progress?.milestones?.done, fileState.progress?.milestones?.done, 'db-match: milestones.done matches');
|
|
155
|
+
assert.deepStrictEqual(dbState.progress?.milestones?.total, fileState.progress?.milestones?.total, 'db-match: milestones.total matches');
|
|
156
|
+
assert.deepStrictEqual(dbState.progress?.slices?.done, fileState.progress?.slices?.done, 'db-match: slices.done matches');
|
|
157
|
+
assert.deepStrictEqual(dbState.progress?.slices?.total, fileState.progress?.slices?.total, 'db-match: slices.total matches');
|
|
158
|
+
assert.deepStrictEqual(dbState.progress?.tasks?.done, fileState.progress?.tasks?.done, 'db-match: tasks.done matches');
|
|
159
|
+
assert.deepStrictEqual(dbState.progress?.tasks?.total, fileState.progress?.tasks?.total, 'db-match: tasks.total matches');
|
|
162
160
|
|
|
163
161
|
closeDatabase();
|
|
164
162
|
} finally {
|
|
165
163
|
closeDatabase();
|
|
166
164
|
cleanup(base);
|
|
167
165
|
}
|
|
168
|
-
}
|
|
166
|
+
});
|
|
169
167
|
|
|
170
168
|
// ─── Test 2: Fallback when DB unavailable ─────────────────────────────
|
|
171
|
-
|
|
172
|
-
{
|
|
169
|
+
test('derive-state-db: fallback when DB unavailable', async () => {
|
|
173
170
|
const base = createFixtureBase();
|
|
174
171
|
try {
|
|
175
172
|
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
@@ -178,22 +175,21 @@ async function main(): Promise<void> {
|
|
|
178
175
|
writeFile(base, 'milestones/M001/slices/S01/tasks/T01-PLAN.md', '# T01 Plan');
|
|
179
176
|
|
|
180
177
|
// No DB open — isDbAvailable() is false
|
|
181
|
-
|
|
178
|
+
assert.ok(!isDbAvailable(), 'fallback: DB is not available');
|
|
182
179
|
invalidateStateCache();
|
|
183
180
|
const state = await deriveState(base);
|
|
184
181
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
182
|
+
assert.deepStrictEqual(state.phase, 'executing', 'fallback: phase is executing');
|
|
183
|
+
assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'fallback: activeMilestone is M001');
|
|
184
|
+
assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'fallback: activeSlice is S01');
|
|
185
|
+
assert.deepStrictEqual(state.activeTask?.id, 'T01', 'fallback: activeTask is T01');
|
|
189
186
|
} finally {
|
|
190
187
|
cleanup(base);
|
|
191
188
|
}
|
|
192
|
-
}
|
|
189
|
+
});
|
|
193
190
|
|
|
194
191
|
// ─── Test 3: Empty DB falls back to file reads ────────────────────────
|
|
195
|
-
|
|
196
|
-
{
|
|
192
|
+
test('derive-state-db: empty DB falls back to files', async () => {
|
|
197
193
|
const base = createFixtureBase();
|
|
198
194
|
try {
|
|
199
195
|
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
@@ -203,27 +199,26 @@ async function main(): Promise<void> {
|
|
|
203
199
|
|
|
204
200
|
// Open DB but insert nothing — empty artifacts table
|
|
205
201
|
openDatabase(':memory:');
|
|
206
|
-
|
|
202
|
+
assert.ok(isDbAvailable(), 'empty-db: DB is available');
|
|
207
203
|
|
|
208
204
|
invalidateStateCache();
|
|
209
205
|
const state = await deriveState(base);
|
|
210
206
|
|
|
211
207
|
// Should still work via cachedLoadFile → loadFile disk fallback
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
208
|
+
assert.deepStrictEqual(state.phase, 'executing', 'empty-db: phase is executing');
|
|
209
|
+
assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'empty-db: activeMilestone is M001');
|
|
210
|
+
assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'empty-db: activeSlice is S01');
|
|
211
|
+
assert.deepStrictEqual(state.activeTask?.id, 'T01', 'empty-db: activeTask is T01');
|
|
216
212
|
|
|
217
213
|
closeDatabase();
|
|
218
214
|
} finally {
|
|
219
215
|
closeDatabase();
|
|
220
216
|
cleanup(base);
|
|
221
217
|
}
|
|
222
|
-
}
|
|
218
|
+
});
|
|
223
219
|
|
|
224
220
|
// ─── Test 4: Partial DB content fills gaps from disk ──────────────────
|
|
225
|
-
|
|
226
|
-
{
|
|
221
|
+
test('derive-state-db: partial DB fills gaps from disk', async () => {
|
|
227
222
|
const base = createFixtureBase();
|
|
228
223
|
try {
|
|
229
224
|
// Write all files to disk
|
|
@@ -244,25 +239,24 @@ async function main(): Promise<void> {
|
|
|
244
239
|
const state = await deriveState(base);
|
|
245
240
|
|
|
246
241
|
// Should work: roadmap from DB, plan from disk fallback
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
242
|
+
assert.deepStrictEqual(state.phase, 'executing', 'partial-db: phase is executing');
|
|
243
|
+
assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'partial-db: activeMilestone is M001');
|
|
244
|
+
assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'partial-db: activeSlice is S01');
|
|
245
|
+
assert.deepStrictEqual(state.activeTask?.id, 'T01', 'partial-db: activeTask is T01');
|
|
251
246
|
// Requirements loaded from disk fallback
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
247
|
+
assert.deepStrictEqual(state.requirements?.active, 2, 'partial-db: requirements.active from disk');
|
|
248
|
+
assert.deepStrictEqual(state.requirements?.validated, 1, 'partial-db: requirements.validated from disk');
|
|
249
|
+
assert.deepStrictEqual(state.requirements?.total, 3, 'partial-db: requirements.total from disk');
|
|
255
250
|
|
|
256
251
|
closeDatabase();
|
|
257
252
|
} finally {
|
|
258
253
|
closeDatabase();
|
|
259
254
|
cleanup(base);
|
|
260
255
|
}
|
|
261
|
-
}
|
|
256
|
+
});
|
|
262
257
|
|
|
263
258
|
// ─── Test 5: Requirements counting from disk (DB no longer used for content) ─
|
|
264
|
-
|
|
265
|
-
{
|
|
259
|
+
test('derive-state-db: requirements from disk content', async () => {
|
|
266
260
|
const base = createFixtureBase();
|
|
267
261
|
try {
|
|
268
262
|
// Write minimal milestone dir (needed for milestone discovery)
|
|
@@ -274,17 +268,16 @@ async function main(): Promise<void> {
|
|
|
274
268
|
const state = await deriveState(base);
|
|
275
269
|
|
|
276
270
|
// Requirements should come from disk
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
271
|
+
assert.deepStrictEqual(state.requirements?.active, 2, 'req-from-disk: requirements.active = 2');
|
|
272
|
+
assert.deepStrictEqual(state.requirements?.validated, 1, 'req-from-disk: requirements.validated = 1');
|
|
273
|
+
assert.deepStrictEqual(state.requirements?.total, 3, 'req-from-disk: requirements.total = 3');
|
|
280
274
|
} finally {
|
|
281
275
|
cleanup(base);
|
|
282
276
|
}
|
|
283
|
-
}
|
|
277
|
+
});
|
|
284
278
|
|
|
285
279
|
// ─── Test 6: DB content with multi-milestone registry ─────────────────
|
|
286
|
-
|
|
287
|
-
{
|
|
280
|
+
test('derive-state-db: multi-milestone from DB', async () => {
|
|
288
281
|
const base = createFixtureBase();
|
|
289
282
|
|
|
290
283
|
const completedRoadmap = `# M001: First Milestone
|
|
@@ -337,24 +330,23 @@ async function main(): Promise<void> {
|
|
|
337
330
|
invalidateStateCache();
|
|
338
331
|
const state = await deriveState(base);
|
|
339
332
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
333
|
+
assert.deepStrictEqual(state.registry.length, 2, 'multi-ms-db: registry has 2 entries');
|
|
334
|
+
assert.deepStrictEqual(state.registry[0]?.id, 'M001', 'multi-ms-db: registry[0] is M001');
|
|
335
|
+
assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'multi-ms-db: M001 is complete');
|
|
336
|
+
assert.deepStrictEqual(state.registry[1]?.id, 'M002', 'multi-ms-db: registry[1] is M002');
|
|
337
|
+
assert.deepStrictEqual(state.registry[1]?.status, 'active', 'multi-ms-db: M002 is active');
|
|
338
|
+
assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'multi-ms-db: activeMilestone is M002');
|
|
339
|
+
assert.deepStrictEqual(state.phase, 'planning', 'multi-ms-db: phase is planning (no plan for S01)');
|
|
347
340
|
|
|
348
341
|
closeDatabase();
|
|
349
342
|
} finally {
|
|
350
343
|
closeDatabase();
|
|
351
344
|
cleanup(base);
|
|
352
345
|
}
|
|
353
|
-
}
|
|
346
|
+
});
|
|
354
347
|
|
|
355
348
|
// ─── Test 7: Cache invalidation works for DB path ─────────────────────
|
|
356
|
-
|
|
357
|
-
{
|
|
349
|
+
test('derive-state-db: cache invalidation', async () => {
|
|
358
350
|
const base = createFixtureBase();
|
|
359
351
|
try {
|
|
360
352
|
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
@@ -375,7 +367,7 @@ async function main(): Promise<void> {
|
|
|
375
367
|
|
|
376
368
|
invalidateStateCache();
|
|
377
369
|
const state1 = await deriveState(base);
|
|
378
|
-
|
|
370
|
+
assert.deepStrictEqual(state1.activeTask?.id, 'T01', 'cache-inv: first call gets T01');
|
|
379
371
|
|
|
380
372
|
// Simulate task completion by updating the plan in DB
|
|
381
373
|
const updatedPlan = PLAN_CONTENT.replace('- [ ] **T01:', '- [x] **T01:');
|
|
@@ -389,28 +381,27 @@ async function main(): Promise<void> {
|
|
|
389
381
|
|
|
390
382
|
// Without invalidation, should return cached result (T01 still active)
|
|
391
383
|
const state2 = await deriveState(base);
|
|
392
|
-
|
|
384
|
+
assert.deepStrictEqual(state2.activeTask?.id, 'T01', 'cache-inv: cached result still has T01');
|
|
393
385
|
|
|
394
386
|
// After invalidation, should pick up updated content
|
|
395
387
|
invalidateStateCache();
|
|
396
388
|
const state3 = await deriveState(base);
|
|
397
|
-
|
|
398
|
-
|
|
389
|
+
assert.deepStrictEqual(state3.phase, 'summarizing', 'cache-inv: after invalidation, phase is summarizing (all tasks done)');
|
|
390
|
+
assert.deepStrictEqual(state3.activeTask, null, 'cache-inv: activeTask is null after all done');
|
|
399
391
|
|
|
400
392
|
closeDatabase();
|
|
401
393
|
} finally {
|
|
402
394
|
closeDatabase();
|
|
403
395
|
cleanup(base);
|
|
404
396
|
}
|
|
405
|
-
}
|
|
397
|
+
});
|
|
406
398
|
|
|
407
399
|
// ═════════════════════════════════════════════════════════════════════════
|
|
408
400
|
// New: deriveStateFromDb() cross-validation tests
|
|
409
401
|
// ═════════════════════════════════════════════════════════════════════════
|
|
410
402
|
|
|
411
403
|
// ─── Test 8: Pre-planning — milestone exists, no roadmap, no slices ───
|
|
412
|
-
|
|
413
|
-
{
|
|
404
|
+
test('derive-state-db: pre-planning via DB', async () => {
|
|
414
405
|
const base = createFixtureBase();
|
|
415
406
|
try {
|
|
416
407
|
// Create milestone dir on disk with a CONTEXT file (not a ghost)
|
|
@@ -427,23 +418,22 @@ async function main(): Promise<void> {
|
|
|
427
418
|
invalidateStateCache();
|
|
428
419
|
const dbState = await deriveStateFromDb(base);
|
|
429
420
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
421
|
+
assert.deepStrictEqual(dbState.phase, fileState.phase, 'pre-plan-db: phase matches');
|
|
422
|
+
assert.deepStrictEqual(dbState.activeMilestone?.id, fileState.activeMilestone?.id, 'pre-plan-db: activeMilestone.id matches');
|
|
423
|
+
assert.deepStrictEqual(dbState.activeSlice, fileState.activeSlice, 'pre-plan-db: activeSlice matches');
|
|
424
|
+
assert.deepStrictEqual(dbState.activeTask, fileState.activeTask, 'pre-plan-db: activeTask matches');
|
|
425
|
+
assert.deepStrictEqual(dbState.registry.length, fileState.registry.length, 'pre-plan-db: registry length matches');
|
|
426
|
+
assert.deepStrictEqual(dbState.registry[0]?.status, fileState.registry[0]?.status, 'pre-plan-db: registry[0] status matches');
|
|
436
427
|
|
|
437
428
|
closeDatabase();
|
|
438
429
|
} finally {
|
|
439
430
|
closeDatabase();
|
|
440
431
|
cleanup(base);
|
|
441
432
|
}
|
|
442
|
-
}
|
|
433
|
+
});
|
|
443
434
|
|
|
444
435
|
// ─── Test 9: Executing — active task with partial completion ──────────
|
|
445
|
-
|
|
446
|
-
{
|
|
436
|
+
test('derive-state-db: executing via DB', async () => {
|
|
447
437
|
const base = createFixtureBase();
|
|
448
438
|
try {
|
|
449
439
|
// Build filesystem fixture
|
|
@@ -466,24 +456,23 @@ async function main(): Promise<void> {
|
|
|
466
456
|
invalidateStateCache();
|
|
467
457
|
const dbState = await deriveStateFromDb(base);
|
|
468
458
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
459
|
+
assert.deepStrictEqual(dbState.phase, 'executing', 'exec-db: phase is executing');
|
|
460
|
+
assert.deepStrictEqual(dbState.activeMilestone?.id, 'M001', 'exec-db: activeMilestone is M001');
|
|
461
|
+
assert.deepStrictEqual(dbState.activeSlice?.id, 'S01', 'exec-db: activeSlice is S01');
|
|
462
|
+
assert.deepStrictEqual(dbState.activeTask?.id, 'T01', 'exec-db: activeTask is T01');
|
|
463
|
+
assert.deepStrictEqual(dbState.progress?.tasks?.done, 1, 'exec-db: tasks.done = 1');
|
|
464
|
+
assert.deepStrictEqual(dbState.progress?.tasks?.total, 2, 'exec-db: tasks.total = 2');
|
|
465
|
+
assert.deepStrictEqual(dbState.phase, fileState.phase, 'exec-db: phase matches filesystem');
|
|
476
466
|
|
|
477
467
|
closeDatabase();
|
|
478
468
|
} finally {
|
|
479
469
|
closeDatabase();
|
|
480
470
|
cleanup(base);
|
|
481
471
|
}
|
|
482
|
-
}
|
|
472
|
+
});
|
|
483
473
|
|
|
484
474
|
// ─── Test 10: Summarizing — all tasks complete, no slice summary ──────
|
|
485
|
-
|
|
486
|
-
{
|
|
475
|
+
test('derive-state-db: summarizing via DB', async () => {
|
|
487
476
|
const base = createFixtureBase();
|
|
488
477
|
try {
|
|
489
478
|
const allDonePlan = `# S01: First Slice
|
|
@@ -517,21 +506,20 @@ async function main(): Promise<void> {
|
|
|
517
506
|
invalidateStateCache();
|
|
518
507
|
const dbState = await deriveStateFromDb(base);
|
|
519
508
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
509
|
+
assert.deepStrictEqual(dbState.phase, 'summarizing', 'summarize-db: phase is summarizing');
|
|
510
|
+
assert.deepStrictEqual(dbState.phase, fileState.phase, 'summarize-db: phase matches filesystem');
|
|
511
|
+
assert.deepStrictEqual(dbState.activeSlice?.id, 'S01', 'summarize-db: activeSlice is S01');
|
|
512
|
+
assert.deepStrictEqual(dbState.activeTask, null, 'summarize-db: activeTask is null');
|
|
524
513
|
|
|
525
514
|
closeDatabase();
|
|
526
515
|
} finally {
|
|
527
516
|
closeDatabase();
|
|
528
517
|
cleanup(base);
|
|
529
518
|
}
|
|
530
|
-
}
|
|
519
|
+
});
|
|
531
520
|
|
|
532
521
|
// ─── Test 11: Complete — all milestones complete ──────────────────────
|
|
533
|
-
|
|
534
|
-
{
|
|
522
|
+
test('derive-state-db: all complete via DB', async () => {
|
|
535
523
|
const base = createFixtureBase();
|
|
536
524
|
try {
|
|
537
525
|
const completedRoadmap = `# M001: Done Milestone
|
|
@@ -557,21 +545,20 @@ async function main(): Promise<void> {
|
|
|
557
545
|
invalidateStateCache();
|
|
558
546
|
const dbState = await deriveStateFromDb(base);
|
|
559
547
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
548
|
+
assert.deepStrictEqual(dbState.phase, 'complete', 'complete-db: phase is complete');
|
|
549
|
+
assert.deepStrictEqual(dbState.phase, fileState.phase, 'complete-db: phase matches filesystem');
|
|
550
|
+
assert.deepStrictEqual(dbState.registry.length, 1, 'complete-db: registry has 1 entry');
|
|
551
|
+
assert.deepStrictEqual(dbState.registry[0]?.status, 'complete', 'complete-db: M001 is complete');
|
|
564
552
|
|
|
565
553
|
closeDatabase();
|
|
566
554
|
} finally {
|
|
567
555
|
closeDatabase();
|
|
568
556
|
cleanup(base);
|
|
569
557
|
}
|
|
570
|
-
}
|
|
558
|
+
});
|
|
571
559
|
|
|
572
560
|
// ─── Test 12: Blocked — slice deps unmet ──────────────────────────────
|
|
573
|
-
|
|
574
|
-
{
|
|
561
|
+
test('derive-state-db: blocked slice via DB', async () => {
|
|
575
562
|
const base = createFixtureBase();
|
|
576
563
|
try {
|
|
577
564
|
// Roadmap with S02 depending on S01, but S01 not done
|
|
@@ -601,20 +588,19 @@ async function main(): Promise<void> {
|
|
|
601
588
|
invalidateStateCache();
|
|
602
589
|
const dbState = await deriveStateFromDb(base);
|
|
603
590
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
591
|
+
assert.deepStrictEqual(dbState.phase, 'blocked', 'blocked-db: phase is blocked');
|
|
592
|
+
assert.deepStrictEqual(dbState.phase, fileState.phase, 'blocked-db: phase matches filesystem');
|
|
593
|
+
assert.ok(dbState.blockers.length > 0, 'blocked-db: has blockers');
|
|
607
594
|
|
|
608
595
|
closeDatabase();
|
|
609
596
|
} finally {
|
|
610
597
|
closeDatabase();
|
|
611
598
|
cleanup(base);
|
|
612
599
|
}
|
|
613
|
-
}
|
|
600
|
+
});
|
|
614
601
|
|
|
615
602
|
// ─── Test 13: Parked milestone ────────────────────────────────────────
|
|
616
|
-
|
|
617
|
-
{
|
|
603
|
+
test('derive-state-db: parked milestone via DB', async () => {
|
|
618
604
|
const base = createFixtureBase();
|
|
619
605
|
try {
|
|
620
606
|
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
@@ -631,20 +617,19 @@ async function main(): Promise<void> {
|
|
|
631
617
|
invalidateStateCache();
|
|
632
618
|
const dbState = await deriveStateFromDb(base);
|
|
633
619
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
620
|
+
assert.deepStrictEqual(dbState.phase, fileState.phase, 'parked-db: phase matches filesystem');
|
|
621
|
+
assert.deepStrictEqual(dbState.activeMilestone?.id, 'M002', 'parked-db: activeMilestone is M002');
|
|
622
|
+
assert.ok(dbState.registry.some(e => e.id === 'M001' && e.status === 'parked'), 'parked-db: M001 is parked in registry');
|
|
637
623
|
|
|
638
624
|
closeDatabase();
|
|
639
625
|
} finally {
|
|
640
626
|
closeDatabase();
|
|
641
627
|
cleanup(base);
|
|
642
628
|
}
|
|
643
|
-
}
|
|
629
|
+
});
|
|
644
630
|
|
|
645
631
|
// ─── Test 14: Validating-milestone — all slices done, no terminal validation ─
|
|
646
|
-
|
|
647
|
-
{
|
|
632
|
+
test('derive-state-db: validating-milestone via DB', async () => {
|
|
648
633
|
const base = createFixtureBase();
|
|
649
634
|
try {
|
|
650
635
|
const doneRoadmap = `# M001: Validate Test
|
|
@@ -669,20 +654,19 @@ async function main(): Promise<void> {
|
|
|
669
654
|
invalidateStateCache();
|
|
670
655
|
const dbState = await deriveStateFromDb(base);
|
|
671
656
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
657
|
+
assert.deepStrictEqual(dbState.phase, 'validating-milestone', 'validate-db: phase is validating-milestone');
|
|
658
|
+
assert.deepStrictEqual(dbState.phase, fileState.phase, 'validate-db: phase matches filesystem');
|
|
659
|
+
assert.deepStrictEqual(dbState.activeMilestone?.id, 'M001', 'validate-db: activeMilestone is M001');
|
|
675
660
|
|
|
676
661
|
closeDatabase();
|
|
677
662
|
} finally {
|
|
678
663
|
closeDatabase();
|
|
679
664
|
cleanup(base);
|
|
680
665
|
}
|
|
681
|
-
}
|
|
666
|
+
});
|
|
682
667
|
|
|
683
668
|
// ─── Test 15: Completing-milestone — terminal validation, no summary ──
|
|
684
|
-
|
|
685
|
-
{
|
|
669
|
+
test('derive-state-db: completing-milestone via DB', async () => {
|
|
686
670
|
const base = createFixtureBase();
|
|
687
671
|
try {
|
|
688
672
|
const doneRoadmap = `# M001: Complete Test
|
|
@@ -707,19 +691,18 @@ async function main(): Promise<void> {
|
|
|
707
691
|
invalidateStateCache();
|
|
708
692
|
const dbState = await deriveStateFromDb(base);
|
|
709
693
|
|
|
710
|
-
|
|
711
|
-
|
|
694
|
+
assert.deepStrictEqual(dbState.phase, 'completing-milestone', 'completing-db: phase is completing-milestone');
|
|
695
|
+
assert.deepStrictEqual(dbState.phase, fileState.phase, 'completing-db: phase matches filesystem');
|
|
712
696
|
|
|
713
697
|
closeDatabase();
|
|
714
698
|
} finally {
|
|
715
699
|
closeDatabase();
|
|
716
700
|
cleanup(base);
|
|
717
701
|
}
|
|
718
|
-
}
|
|
702
|
+
});
|
|
719
703
|
|
|
720
704
|
// ─── Test 16: Replanning-slice — REPLAN-TRIGGER file exists ───────────
|
|
721
|
-
|
|
722
|
-
{
|
|
705
|
+
test('derive-state-db: replanning-slice via DB', async () => {
|
|
723
706
|
const base = createFixtureBase();
|
|
724
707
|
try {
|
|
725
708
|
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
@@ -749,19 +732,18 @@ async function main(): Promise<void> {
|
|
|
749
732
|
invalidateStateCache();
|
|
750
733
|
const dbState = await deriveStateFromDb(base);
|
|
751
734
|
|
|
752
|
-
|
|
753
|
-
|
|
735
|
+
assert.deepStrictEqual(dbState.phase, 'replanning-slice', 'replan-db: phase is replanning-slice');
|
|
736
|
+
assert.deepStrictEqual(dbState.phase, fileState.phase, 'replan-db: phase matches filesystem');
|
|
754
737
|
|
|
755
738
|
closeDatabase();
|
|
756
739
|
} finally {
|
|
757
740
|
closeDatabase();
|
|
758
741
|
cleanup(base);
|
|
759
742
|
}
|
|
760
|
-
}
|
|
743
|
+
});
|
|
761
744
|
|
|
762
745
|
// ─── Test 17: Performance — deriveStateFromDb < 1ms on populated DB ───
|
|
763
|
-
|
|
764
|
-
{
|
|
746
|
+
test('derive-state-db: performance assertion', async () => {
|
|
765
747
|
const base = createFixtureBase();
|
|
766
748
|
try {
|
|
767
749
|
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
@@ -789,18 +771,17 @@ async function main(): Promise<void> {
|
|
|
789
771
|
console.log(` deriveStateFromDb() took ${elapsed.toFixed(3)}ms`);
|
|
790
772
|
// Use 10ms threshold — catches real regressions without flaking on
|
|
791
773
|
// CI runners under load (1ms threshold failed at 1.050ms on GitHub Actions)
|
|
792
|
-
|
|
774
|
+
assert.ok(elapsed < 10, `perf-db: deriveStateFromDb() <10ms (got ${elapsed.toFixed(3)}ms)`);
|
|
793
775
|
|
|
794
776
|
closeDatabase();
|
|
795
777
|
} finally {
|
|
796
778
|
closeDatabase();
|
|
797
779
|
cleanup(base);
|
|
798
780
|
}
|
|
799
|
-
}
|
|
781
|
+
});
|
|
800
782
|
|
|
801
783
|
// ─── Test 18: Multi-milestone with deps — M001 complete, M002 depends on M001, M003 depends on M002 ─
|
|
802
|
-
|
|
803
|
-
{
|
|
784
|
+
test('derive-state-db: multi-milestone deps via DB', async () => {
|
|
804
785
|
const base = createFixtureBase();
|
|
805
786
|
try {
|
|
806
787
|
const m1Roadmap = `# M001: First
|
|
@@ -841,29 +822,28 @@ async function main(): Promise<void> {
|
|
|
841
822
|
invalidateStateCache();
|
|
842
823
|
const dbState = await deriveStateFromDb(base);
|
|
843
824
|
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
825
|
+
assert.deepStrictEqual(dbState.registry.length, fileState.registry.length, 'multi-deps-db: registry length matches');
|
|
826
|
+
assert.deepStrictEqual(dbState.activeMilestone?.id, 'M002', 'multi-deps-db: activeMilestone is M002 (M001 complete, M003 dep unmet)');
|
|
827
|
+
assert.deepStrictEqual(dbState.activeMilestone?.id, fileState.activeMilestone?.id, 'multi-deps-db: activeMilestone matches filesystem');
|
|
828
|
+
assert.deepStrictEqual(dbState.phase, fileState.phase, 'multi-deps-db: phase matches filesystem');
|
|
848
829
|
|
|
849
830
|
// Check registry statuses
|
|
850
831
|
const m1reg = dbState.registry.find(e => e.id === 'M001');
|
|
851
832
|
const m2reg = dbState.registry.find(e => e.id === 'M002');
|
|
852
833
|
const m3reg = dbState.registry.find(e => e.id === 'M003');
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
834
|
+
assert.deepStrictEqual(m1reg?.status, 'complete', 'multi-deps-db: M001 is complete');
|
|
835
|
+
assert.deepStrictEqual(m2reg?.status, 'active', 'multi-deps-db: M002 is active');
|
|
836
|
+
assert.deepStrictEqual(m3reg?.status, 'pending', 'multi-deps-db: M003 is pending (dep M002 unmet)');
|
|
856
837
|
|
|
857
838
|
closeDatabase();
|
|
858
839
|
} finally {
|
|
859
840
|
closeDatabase();
|
|
860
841
|
cleanup(base);
|
|
861
842
|
}
|
|
862
|
-
}
|
|
843
|
+
});
|
|
863
844
|
|
|
864
845
|
// ─── Test 19: K002 — both 'complete' and 'done' treated as done ───────
|
|
865
|
-
|
|
866
|
-
{
|
|
846
|
+
test('derive-state-db: K002 status handling', async () => {
|
|
867
847
|
const base = createFixtureBase();
|
|
868
848
|
try {
|
|
869
849
|
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
@@ -882,20 +862,19 @@ async function main(): Promise<void> {
|
|
|
882
862
|
invalidateStateCache();
|
|
883
863
|
const dbState = await deriveStateFromDb(base);
|
|
884
864
|
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
865
|
+
assert.deepStrictEqual(dbState.phase, 'executing', 'k002-db: phase is executing');
|
|
866
|
+
assert.deepStrictEqual(dbState.activeTask?.id, 'T01', 'k002-db: activeTask is T01 (T02 done)');
|
|
867
|
+
assert.deepStrictEqual(dbState.progress?.tasks?.done, 1, 'k002-db: tasks.done counts done status');
|
|
888
868
|
|
|
889
869
|
closeDatabase();
|
|
890
870
|
} finally {
|
|
891
871
|
closeDatabase();
|
|
892
872
|
cleanup(base);
|
|
893
873
|
}
|
|
894
|
-
}
|
|
874
|
+
});
|
|
895
875
|
|
|
896
876
|
// ─── Test 20: Dual-path wiring — deriveState() uses DB when populated ─
|
|
897
|
-
|
|
898
|
-
{
|
|
877
|
+
test('derive-state-db: dual-path wiring', async () => {
|
|
899
878
|
const base = createFixtureBase();
|
|
900
879
|
try {
|
|
901
880
|
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
@@ -914,21 +893,20 @@ async function main(): Promise<void> {
|
|
|
914
893
|
invalidateStateCache();
|
|
915
894
|
const state = await deriveState(base);
|
|
916
895
|
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
896
|
+
assert.deepStrictEqual(state.phase, 'executing', 'dual-path: phase is executing');
|
|
897
|
+
assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'dual-path: activeMilestone is M001');
|
|
898
|
+
assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'dual-path: activeSlice is S01');
|
|
899
|
+
assert.deepStrictEqual(state.activeTask?.id, 'T01', 'dual-path: activeTask is T01');
|
|
921
900
|
|
|
922
901
|
closeDatabase();
|
|
923
902
|
} finally {
|
|
924
903
|
closeDatabase();
|
|
925
904
|
cleanup(base);
|
|
926
905
|
}
|
|
927
|
-
}
|
|
906
|
+
});
|
|
928
907
|
|
|
929
908
|
// ─── Test 21: Ghost milestone skipped ─────────────────────────────────
|
|
930
|
-
|
|
931
|
-
{
|
|
909
|
+
test('derive-state-db: ghost milestone skipped', async () => {
|
|
932
910
|
const base = createFixtureBase();
|
|
933
911
|
try {
|
|
934
912
|
// Ghost: milestone dir exists with only META.json, no context/roadmap/summary
|
|
@@ -949,21 +927,20 @@ async function main(): Promise<void> {
|
|
|
949
927
|
const dbState = await deriveStateFromDb(base);
|
|
950
928
|
|
|
951
929
|
// Ghost should be skipped — M002 should be active
|
|
952
|
-
|
|
953
|
-
|
|
930
|
+
assert.deepStrictEqual(dbState.activeMilestone?.id, 'M002', 'ghost-db: activeMilestone is M002 (ghost skipped)');
|
|
931
|
+
assert.deepStrictEqual(dbState.activeMilestone?.id, fileState.activeMilestone?.id, 'ghost-db: matches filesystem');
|
|
954
932
|
// Ghost should not appear in registry
|
|
955
|
-
|
|
933
|
+
assert.ok(!dbState.registry.some(e => e.id === 'M001'), 'ghost-db: M001 not in registry');
|
|
956
934
|
|
|
957
935
|
closeDatabase();
|
|
958
936
|
} finally {
|
|
959
937
|
closeDatabase();
|
|
960
938
|
cleanup(base);
|
|
961
939
|
}
|
|
962
|
-
}
|
|
940
|
+
});
|
|
963
941
|
|
|
964
942
|
// ─── Test 22: Needs-discussion — CONTEXT-DRAFT exists ─────────────────
|
|
965
|
-
|
|
966
|
-
{
|
|
943
|
+
test('derive-state-db: needs-discussion via DB', async () => {
|
|
967
944
|
const base = createFixtureBase();
|
|
968
945
|
try {
|
|
969
946
|
writeFile(base, 'milestones/M001/M001-CONTEXT-DRAFT.md', '# M001: Draft\n\nDraft content.');
|
|
@@ -977,20 +954,72 @@ async function main(): Promise<void> {
|
|
|
977
954
|
invalidateStateCache();
|
|
978
955
|
const dbState = await deriveStateFromDb(base);
|
|
979
956
|
|
|
980
|
-
|
|
981
|
-
|
|
957
|
+
assert.deepStrictEqual(dbState.phase, 'needs-discussion', 'discuss-db: phase is needs-discussion');
|
|
958
|
+
assert.deepStrictEqual(dbState.phase, fileState.phase, 'discuss-db: phase matches filesystem');
|
|
982
959
|
|
|
983
960
|
closeDatabase();
|
|
984
961
|
} finally {
|
|
985
962
|
closeDatabase();
|
|
986
963
|
cleanup(base);
|
|
987
964
|
}
|
|
988
|
-
}
|
|
965
|
+
});
|
|
989
966
|
|
|
990
|
-
|
|
991
|
-
|
|
967
|
+
// ─── Regression: disk-only milestones synced into DB (#2416) ─────────
|
|
968
|
+
test('derive-state-db: disk-only milestone auto-synced into DB (#2416)', async () => {
|
|
969
|
+
const base = createFixtureBase();
|
|
970
|
+
try {
|
|
971
|
+
// M001 is complete and exists in DB. M002 was queued on disk only — no DB row.
|
|
972
|
+
writeFile(base, 'milestones/M001/M001-SUMMARY.md', '# M001 Summary\n\nDone.');
|
|
973
|
+
writeFile(base, 'milestones/M002/M002-CONTEXT.md', '# M002: Queued\n\nQueued milestone.');
|
|
974
|
+
|
|
975
|
+
openDatabase(':memory:');
|
|
976
|
+
// Only insert M001 — simulates the state after migration guard ran then /gsd queue added M002
|
|
977
|
+
insertMilestone({ id: 'M001', title: 'First', status: 'complete' });
|
|
978
|
+
|
|
979
|
+
invalidateStateCache();
|
|
980
|
+
const state = await deriveStateFromDb(base);
|
|
981
|
+
|
|
982
|
+
// Before the fix, M002 was invisible: getAllMilestones() returned only M001
|
|
983
|
+
// (complete) → phase='complete' → auto-mode stopped.
|
|
984
|
+
// After the fix, deriveStateFromDb reconciles disk dirs and inserts M002.
|
|
985
|
+
assert.deepStrictEqual(state.phase, 'pre-planning', 'disk-sync-2416: phase is pre-planning, not complete');
|
|
986
|
+
assert.deepStrictEqual(state.registry.length, 2, 'disk-sync-2416: both milestones visible in registry');
|
|
987
|
+
assert.deepStrictEqual(state.registry[0]?.id, 'M001', 'disk-sync-2416: registry[0] is M001');
|
|
988
|
+
assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'disk-sync-2416: M001 is complete');
|
|
989
|
+
assert.deepStrictEqual(state.registry[1]?.id, 'M002', 'disk-sync-2416: registry[1] is M002');
|
|
990
|
+
assert.deepStrictEqual(state.registry[1]?.status, 'active', 'disk-sync-2416: M002 is active');
|
|
991
|
+
assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'disk-sync-2416: activeMilestone is M002');
|
|
992
|
+
|
|
993
|
+
closeDatabase();
|
|
994
|
+
} finally {
|
|
995
|
+
closeDatabase();
|
|
996
|
+
cleanup(base);
|
|
997
|
+
}
|
|
998
|
+
});
|
|
992
999
|
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1000
|
+
// ─── Queued milestone row not clobbered by later plan (#2416 root cause) ──
|
|
1001
|
+
test('derive-state-db: queued milestone row survives gsd_plan_milestone INSERT OR IGNORE', async () => {
|
|
1002
|
+
try {
|
|
1003
|
+
openDatabase(':memory:');
|
|
1004
|
+
|
|
1005
|
+
// Simulates gsd_milestone_generate_id inserting a minimal queued row
|
|
1006
|
+
insertMilestone({ id: 'M001', status: 'queued' });
|
|
1007
|
+
|
|
1008
|
+
const before = getAllMilestones();
|
|
1009
|
+
assert.equal(before.length, 1, 'queued-row: one row after generate_id');
|
|
1010
|
+
assert.equal(before[0]!.status, 'queued', 'queued-row: status is queued');
|
|
1011
|
+
|
|
1012
|
+
// Simulates gsd_plan_milestone calling insertMilestone (INSERT OR IGNORE)
|
|
1013
|
+
insertMilestone({ id: 'M001', title: 'Planned Title', status: 'active' });
|
|
1014
|
+
|
|
1015
|
+
const after = getAllMilestones();
|
|
1016
|
+
assert.equal(after.length, 1, 'queued-row: still one row after plan');
|
|
1017
|
+
// INSERT OR IGNORE keeps the original row — status stays 'queued'
|
|
1018
|
+
assert.equal(after[0]!.status, 'queued', 'queued-row: INSERT OR IGNORE preserves original status');
|
|
1019
|
+
|
|
1020
|
+
closeDatabase();
|
|
1021
|
+
} finally {
|
|
1022
|
+
closeDatabase();
|
|
1023
|
+
}
|
|
1024
|
+
});
|
|
996
1025
|
});
|