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,11 +1,10 @@
|
|
|
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';
|
|
4
6
|
|
|
5
7
|
import { deriveState, isSliceComplete, isMilestoneComplete, isGhostMilestone } from '../state.ts';
|
|
6
|
-
import { createTestContext } from './test-helpers.ts';
|
|
7
|
-
|
|
8
|
-
const { assertEq, assertTrue, report } = createTestContext();
|
|
9
8
|
// ─── Fixture Helpers ───────────────────────────────────────────────────────
|
|
10
9
|
|
|
11
10
|
function createFixtureBase(): string {
|
|
@@ -65,30 +64,28 @@ function cleanup(base: string): void {
|
|
|
65
64
|
// Test Groups
|
|
66
65
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
67
66
|
|
|
68
|
-
async
|
|
67
|
+
describe('derive-state', async () => {
|
|
69
68
|
|
|
70
69
|
// ─── Test 1: empty milestones dir → pre-planning ───────────────────────
|
|
71
|
-
|
|
72
|
-
{
|
|
70
|
+
test('empty milestones dir → pre-planning', async () => {
|
|
73
71
|
const base = createFixtureBase();
|
|
74
72
|
try {
|
|
75
73
|
const state = await deriveState(base);
|
|
76
74
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
75
|
+
assert.deepStrictEqual(state.phase, 'pre-planning', 'phase is pre-planning');
|
|
76
|
+
assert.deepStrictEqual(state.activeMilestone, null, 'activeMilestone is null');
|
|
77
|
+
assert.deepStrictEqual(state.activeSlice, null, 'activeSlice is null');
|
|
78
|
+
assert.deepStrictEqual(state.activeTask, null, 'activeTask is null');
|
|
79
|
+
assert.deepStrictEqual(state.registry, [], 'registry is empty');
|
|
80
|
+
assert.deepStrictEqual(state.progress?.milestones?.done, 0, 'milestones done = 0');
|
|
81
|
+
assert.deepStrictEqual(state.progress?.milestones?.total, 0, 'milestones total = 0');
|
|
84
82
|
} finally {
|
|
85
83
|
cleanup(base);
|
|
86
84
|
}
|
|
87
|
-
}
|
|
85
|
+
});
|
|
88
86
|
|
|
89
87
|
// ─── Test 2: milestone dir exists but no roadmap → pre-planning ────────
|
|
90
|
-
|
|
91
|
-
{
|
|
88
|
+
test('milestone dir exists but no roadmap → pre-planning', async () => {
|
|
92
89
|
const base = createFixtureBase();
|
|
93
90
|
try {
|
|
94
91
|
// Create M001 directory with CONTEXT but no roadmap file
|
|
@@ -97,21 +94,20 @@ async function main(): Promise<void> {
|
|
|
97
94
|
|
|
98
95
|
const state = await deriveState(base);
|
|
99
96
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
97
|
+
assert.deepStrictEqual(state.phase, 'pre-planning', 'phase is pre-planning');
|
|
98
|
+
assert.ok(state.activeMilestone !== null, 'activeMilestone is not null');
|
|
99
|
+
assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'activeMilestone id is M001');
|
|
100
|
+
assert.deepStrictEqual(state.activeSlice, null, 'activeSlice is null');
|
|
101
|
+
assert.deepStrictEqual(state.activeTask, null, 'activeTask is null');
|
|
102
|
+
assert.deepStrictEqual(state.registry.length, 1, 'registry has 1 entry');
|
|
103
|
+
assert.deepStrictEqual(state.registry[0]?.status, 'active', 'registry entry status is active');
|
|
107
104
|
} finally {
|
|
108
105
|
cleanup(base);
|
|
109
106
|
}
|
|
110
|
-
}
|
|
107
|
+
});
|
|
111
108
|
|
|
112
109
|
// ─── Test 3: roadmap with incomplete slice, no plan → planning ─────────
|
|
113
|
-
|
|
114
|
-
{
|
|
110
|
+
test('roadmap with incomplete slice, no plan → planning', async () => {
|
|
115
111
|
const base = createFixtureBase();
|
|
116
112
|
try {
|
|
117
113
|
writeRoadmap(base, 'M001', `# M001: Test Milestone
|
|
@@ -126,20 +122,19 @@ async function main(): Promise<void> {
|
|
|
126
122
|
|
|
127
123
|
const state = await deriveState(base);
|
|
128
124
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
125
|
+
assert.deepStrictEqual(state.phase, 'planning', 'phase is planning');
|
|
126
|
+
assert.ok(state.activeSlice !== null, 'activeSlice is not null');
|
|
127
|
+
assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'activeSlice id is S01');
|
|
128
|
+
assert.deepStrictEqual(state.activeTask, null, 'activeTask is null');
|
|
129
|
+
assert.deepStrictEqual(state.progress?.slices?.done, 0, 'slices done = 0');
|
|
130
|
+
assert.deepStrictEqual(state.progress?.slices?.total, 1, 'slices total = 1');
|
|
135
131
|
} finally {
|
|
136
132
|
cleanup(base);
|
|
137
133
|
}
|
|
138
|
-
}
|
|
134
|
+
});
|
|
139
135
|
|
|
140
136
|
// ─── Test 4: roadmap + plan with incomplete tasks → executing ──────────
|
|
141
|
-
|
|
142
|
-
{
|
|
137
|
+
test('roadmap + plan with incomplete tasks → executing', async () => {
|
|
143
138
|
const base = createFixtureBase();
|
|
144
139
|
try {
|
|
145
140
|
writeRoadmap(base, 'M001', `# M001: Test Milestone
|
|
@@ -168,19 +163,18 @@ async function main(): Promise<void> {
|
|
|
168
163
|
|
|
169
164
|
const state = await deriveState(base);
|
|
170
165
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
166
|
+
assert.deepStrictEqual(state.phase, 'executing', 'phase is executing');
|
|
167
|
+
assert.ok(state.activeTask !== null, 'activeTask is not null');
|
|
168
|
+
assert.deepStrictEqual(state.activeTask?.id, 'T01', 'activeTask id is T01');
|
|
169
|
+
assert.deepStrictEqual(state.progress?.tasks?.done, 0, 'tasks done = 0');
|
|
170
|
+
assert.deepStrictEqual(state.progress?.tasks?.total, 2, 'tasks total = 2');
|
|
176
171
|
} finally {
|
|
177
172
|
cleanup(base);
|
|
178
173
|
}
|
|
179
|
-
}
|
|
174
|
+
});
|
|
180
175
|
|
|
181
176
|
// ─── Test 5: executing + continue file → resume message ─────────────
|
|
182
|
-
|
|
183
|
-
{
|
|
177
|
+
test('executing + continue file → resume message', async () => {
|
|
184
178
|
const base = createFixtureBase();
|
|
185
179
|
try {
|
|
186
180
|
writeRoadmap(base, 'M001', `# M001: Test Milestone
|
|
@@ -228,21 +222,20 @@ Continue from step 2.
|
|
|
228
222
|
|
|
229
223
|
const state = await deriveState(base);
|
|
230
224
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
225
|
+
assert.deepStrictEqual(state.phase, 'executing', 'interrupted: phase is executing');
|
|
226
|
+
assert.ok(state.activeTask !== null, 'interrupted: activeTask is not null');
|
|
227
|
+
assert.deepStrictEqual(state.activeTask?.id, 'T01', 'interrupted: activeTask id is T01');
|
|
228
|
+
assert.ok(
|
|
235
229
|
state.nextAction.includes('Resume') || state.nextAction.includes('resume') || state.nextAction.includes('continue.md'),
|
|
236
230
|
'interrupted: nextAction mentions Resume/resume/continue.md'
|
|
237
231
|
);
|
|
238
232
|
} finally {
|
|
239
233
|
cleanup(base);
|
|
240
234
|
}
|
|
241
|
-
}
|
|
235
|
+
});
|
|
242
236
|
|
|
243
237
|
// ─── Test 6: all tasks done, slice not [x] → summarizing ──────────────
|
|
244
|
-
|
|
245
|
-
{
|
|
238
|
+
test('all tasks done, slice not [x] → summarizing', async () => {
|
|
246
239
|
const base = createFixtureBase();
|
|
247
240
|
try {
|
|
248
241
|
writeRoadmap(base, 'M001', `# M001: Test Milestone
|
|
@@ -271,24 +264,23 @@ Continue from step 2.
|
|
|
271
264
|
|
|
272
265
|
const state = await deriveState(base);
|
|
273
266
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
267
|
+
assert.deepStrictEqual(state.phase, 'summarizing', 'summarizing: phase is summarizing');
|
|
268
|
+
assert.ok(state.activeSlice !== null, 'summarizing: activeSlice is not null');
|
|
269
|
+
assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'summarizing: activeSlice id is S01');
|
|
270
|
+
assert.deepStrictEqual(state.activeTask, null, 'summarizing: activeTask is null');
|
|
271
|
+
assert.ok(
|
|
279
272
|
state.nextAction.toLowerCase().includes('summary') || state.nextAction.toLowerCase().includes('complete'),
|
|
280
273
|
'summarizing: nextAction mentions summary or complete'
|
|
281
274
|
);
|
|
282
|
-
|
|
283
|
-
|
|
275
|
+
assert.deepStrictEqual(state.progress?.tasks?.done, 2, 'summarizing: tasks done = 2');
|
|
276
|
+
assert.deepStrictEqual(state.progress?.tasks?.total, 2, 'summarizing: tasks total = 2');
|
|
284
277
|
} finally {
|
|
285
278
|
cleanup(base);
|
|
286
279
|
}
|
|
287
|
-
}
|
|
280
|
+
});
|
|
288
281
|
|
|
289
282
|
// ─── Test 7: all milestones complete → complete ────────────────────────
|
|
290
|
-
|
|
291
|
-
{
|
|
283
|
+
test('all milestones complete → complete', async () => {
|
|
292
284
|
const base = createFixtureBase();
|
|
293
285
|
try {
|
|
294
286
|
writeRoadmap(base, 'M001', `# M001: Test Milestone
|
|
@@ -306,23 +298,22 @@ Continue from step 2.
|
|
|
306
298
|
|
|
307
299
|
const state = await deriveState(base);
|
|
308
300
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
301
|
+
assert.deepStrictEqual(state.phase, 'complete', 'complete: phase is complete');
|
|
302
|
+
assert.deepStrictEqual(state.activeSlice, null, 'complete: activeSlice is null');
|
|
303
|
+
assert.deepStrictEqual(state.activeTask, null, 'complete: activeTask is null');
|
|
304
|
+
assert.ok(
|
|
313
305
|
state.nextAction.toLowerCase().includes('complete'),
|
|
314
306
|
'complete: nextAction mentions complete'
|
|
315
307
|
);
|
|
316
|
-
|
|
317
|
-
|
|
308
|
+
assert.deepStrictEqual(state.registry.length, 1, 'complete: registry has 1 entry');
|
|
309
|
+
assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'complete: registry[0] status is complete');
|
|
318
310
|
} finally {
|
|
319
311
|
cleanup(base);
|
|
320
312
|
}
|
|
321
|
-
}
|
|
313
|
+
});
|
|
322
314
|
|
|
323
315
|
// ─── Test 7b: complete with active requirements → surfaces unmapped reqs ──
|
|
324
|
-
|
|
325
|
-
{
|
|
316
|
+
test('complete with active requirements → surfaces unmapped reqs', async () => {
|
|
326
317
|
const base = createFixtureBase();
|
|
327
318
|
try {
|
|
328
319
|
writeRoadmap(base, 'M001', `# M001: Test Milestone
|
|
@@ -355,23 +346,22 @@ Continue from step 2.
|
|
|
355
346
|
|
|
356
347
|
const state = await deriveState(base);
|
|
357
348
|
|
|
358
|
-
|
|
359
|
-
|
|
349
|
+
assert.deepStrictEqual(state.phase, 'complete', 'complete-with-reqs: phase is complete');
|
|
350
|
+
assert.ok(
|
|
360
351
|
state.nextAction.includes('2 active requirements'),
|
|
361
352
|
'complete-with-reqs: nextAction mentions 2 active requirements'
|
|
362
353
|
);
|
|
363
|
-
|
|
354
|
+
assert.ok(
|
|
364
355
|
state.nextAction.includes('REQUIREMENTS.md'),
|
|
365
356
|
'complete-with-reqs: nextAction mentions REQUIREMENTS.md'
|
|
366
357
|
);
|
|
367
358
|
} finally {
|
|
368
359
|
cleanup(base);
|
|
369
360
|
}
|
|
370
|
-
}
|
|
361
|
+
});
|
|
371
362
|
|
|
372
363
|
// ─── Test 7c: complete with no active requirements → standard message ──
|
|
373
|
-
|
|
374
|
-
{
|
|
364
|
+
test('complete with no active requirements → standard message', async () => {
|
|
375
365
|
const base = createFixtureBase();
|
|
376
366
|
try {
|
|
377
367
|
writeRoadmap(base, 'M001', `# M001: Test Milestone
|
|
@@ -396,16 +386,15 @@ Continue from step 2.
|
|
|
396
386
|
|
|
397
387
|
const state = await deriveState(base);
|
|
398
388
|
|
|
399
|
-
|
|
400
|
-
|
|
389
|
+
assert.deepStrictEqual(state.phase, 'complete', 'complete-no-active-reqs: phase is complete');
|
|
390
|
+
assert.deepStrictEqual(state.nextAction, 'All milestones complete.', 'complete-no-active-reqs: standard completion message');
|
|
401
391
|
} finally {
|
|
402
392
|
cleanup(base);
|
|
403
393
|
}
|
|
404
|
-
}
|
|
394
|
+
});
|
|
405
395
|
|
|
406
396
|
// ─── Test 8: blocked dependencies ──────────────────────────────────────
|
|
407
|
-
|
|
408
|
-
{
|
|
397
|
+
test('blocked dependencies', async () => {
|
|
409
398
|
// Case A: S01 active (deps satisfied), S02 blocked on S01
|
|
410
399
|
const base1 = createFixtureBase();
|
|
411
400
|
try {
|
|
@@ -436,8 +425,8 @@ Continue from step 2.
|
|
|
436
425
|
|
|
437
426
|
const state1 = await deriveState(base1);
|
|
438
427
|
|
|
439
|
-
|
|
440
|
-
|
|
428
|
+
assert.deepStrictEqual(state1.phase, 'executing', 'blocked-A: phase is executing (S01 active)');
|
|
429
|
+
assert.deepStrictEqual(state1.activeSlice?.id, 'S01', 'blocked-A: activeSlice is S01');
|
|
441
430
|
} finally {
|
|
442
431
|
cleanup(base1);
|
|
443
432
|
}
|
|
@@ -457,17 +446,16 @@ Continue from step 2.
|
|
|
457
446
|
|
|
458
447
|
const state2 = await deriveState(base2);
|
|
459
448
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
449
|
+
assert.deepStrictEqual(state2.phase, 'blocked', 'blocked-B: phase is blocked');
|
|
450
|
+
assert.deepStrictEqual(state2.activeSlice, null, 'blocked-B: activeSlice is null');
|
|
451
|
+
assert.ok(state2.blockers.length > 0, 'blocked-B: blockers array is non-empty');
|
|
463
452
|
} finally {
|
|
464
453
|
cleanup(base2);
|
|
465
454
|
}
|
|
466
|
-
}
|
|
455
|
+
});
|
|
467
456
|
|
|
468
457
|
// ─── Test 9: multi-milestone registry ──────────────────────────────────
|
|
469
|
-
|
|
470
|
-
{
|
|
458
|
+
test('multi-milestone registry', async () => {
|
|
471
459
|
const base = createFixtureBase();
|
|
472
460
|
try {
|
|
473
461
|
// M001: complete (all slices done)
|
|
@@ -501,24 +489,23 @@ Continue from step 2.
|
|
|
501
489
|
|
|
502
490
|
const state = await deriveState(base);
|
|
503
491
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
492
|
+
assert.deepStrictEqual(state.registry.length, 3, 'multi-ms: registry has 3 entries');
|
|
493
|
+
assert.deepStrictEqual(state.registry[0]?.id, 'M001', 'multi-ms: registry[0] is M001');
|
|
494
|
+
assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'multi-ms: M001 is complete');
|
|
495
|
+
assert.deepStrictEqual(state.registry[1]?.id, 'M002', 'multi-ms: registry[1] is M002');
|
|
496
|
+
assert.deepStrictEqual(state.registry[1]?.status, 'active', 'multi-ms: M002 is active');
|
|
497
|
+
assert.deepStrictEqual(state.registry[2]?.id, 'M003', 'multi-ms: registry[2] is M003');
|
|
498
|
+
assert.deepStrictEqual(state.registry[2]?.status, 'pending', 'multi-ms: M003 is pending');
|
|
499
|
+
assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'multi-ms: activeMilestone is M002');
|
|
500
|
+
assert.deepStrictEqual(state.progress?.milestones?.done, 1, 'multi-ms: milestones done = 1');
|
|
501
|
+
assert.deepStrictEqual(state.progress?.milestones?.total, 3, 'multi-ms: milestones total = 3');
|
|
514
502
|
} finally {
|
|
515
503
|
cleanup(base);
|
|
516
504
|
}
|
|
517
|
-
}
|
|
505
|
+
});
|
|
518
506
|
|
|
519
507
|
// ─── Test 10: requirements integration ─────────────────────────────────
|
|
520
|
-
|
|
521
|
-
{
|
|
508
|
+
test('requirements integration', async () => {
|
|
522
509
|
const base = createFixtureBase();
|
|
523
510
|
try {
|
|
524
511
|
writeRequirements(base, `# Requirements
|
|
@@ -559,20 +546,19 @@ Continue from step 2.
|
|
|
559
546
|
// Need at least an empty milestones dir for deriveState
|
|
560
547
|
const state = await deriveState(base);
|
|
561
548
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
549
|
+
assert.ok(state.requirements !== undefined, 'requirements: requirements object exists');
|
|
550
|
+
assert.deepStrictEqual(state.requirements?.active, 2, 'requirements: active = 2');
|
|
551
|
+
assert.deepStrictEqual(state.requirements?.validated, 1, 'requirements: validated = 1');
|
|
552
|
+
assert.deepStrictEqual(state.requirements?.deferred, 2, 'requirements: deferred = 2');
|
|
553
|
+
assert.deepStrictEqual(state.requirements?.outOfScope, 1, 'requirements: outOfScope = 1');
|
|
554
|
+
assert.deepStrictEqual(state.requirements?.total, 6, 'requirements: total = 6 (sum of all)');
|
|
568
555
|
} finally {
|
|
569
556
|
cleanup(base);
|
|
570
557
|
}
|
|
571
|
-
}
|
|
558
|
+
});
|
|
572
559
|
|
|
573
560
|
// ─── Test 11: all slices [x], no summary → completing-milestone ────────
|
|
574
|
-
|
|
575
|
-
{
|
|
561
|
+
test('all slices [x], no summary → completing-milestone', async () => {
|
|
576
562
|
const base = createFixtureBase();
|
|
577
563
|
try {
|
|
578
564
|
writeRoadmap(base, 'M001', `# M001: Test Milestone
|
|
@@ -592,27 +578,26 @@ Continue from step 2.
|
|
|
592
578
|
|
|
593
579
|
const state = await deriveState(base);
|
|
594
580
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
581
|
+
assert.deepStrictEqual(state.phase, 'completing-milestone', 'completing-ms: phase is completing-milestone');
|
|
582
|
+
assert.ok(state.activeMilestone !== null, 'completing-ms: activeMilestone is not null');
|
|
583
|
+
assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'completing-ms: activeMilestone id is M001');
|
|
584
|
+
assert.deepStrictEqual(state.activeSlice, null, 'completing-ms: activeSlice is null');
|
|
585
|
+
assert.deepStrictEqual(state.activeTask, null, 'completing-ms: activeTask is null');
|
|
586
|
+
assert.deepStrictEqual(state.registry.length, 1, 'completing-ms: registry has 1 entry');
|
|
587
|
+
assert.deepStrictEqual(state.registry[0]?.status, 'active', 'completing-ms: registry[0] status is active (not complete)');
|
|
588
|
+
assert.deepStrictEqual(state.progress?.slices?.done, 2, 'completing-ms: slices done = 2');
|
|
589
|
+
assert.deepStrictEqual(state.progress?.slices?.total, 2, 'completing-ms: slices total = 2');
|
|
590
|
+
assert.ok(
|
|
605
591
|
state.nextAction.toLowerCase().includes('summary') || state.nextAction.toLowerCase().includes('complete'),
|
|
606
592
|
'completing-ms: nextAction mentions summary or complete'
|
|
607
593
|
);
|
|
608
594
|
} finally {
|
|
609
595
|
cleanup(base);
|
|
610
596
|
}
|
|
611
|
-
}
|
|
597
|
+
});
|
|
612
598
|
|
|
613
599
|
// ─── Test 12: all slices [x], summary exists → complete ───────────────
|
|
614
|
-
|
|
615
|
-
{
|
|
600
|
+
test('all slices [x], summary exists → complete', async () => {
|
|
616
601
|
const base = createFixtureBase();
|
|
617
602
|
try {
|
|
618
603
|
writeRoadmap(base, 'M001', `# M001: Test Milestone
|
|
@@ -630,19 +615,18 @@ Continue from step 2.
|
|
|
630
615
|
|
|
631
616
|
const state = await deriveState(base);
|
|
632
617
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
618
|
+
assert.deepStrictEqual(state.phase, 'complete', 'summary-exists: phase is complete');
|
|
619
|
+
assert.deepStrictEqual(state.registry.length, 1, 'summary-exists: registry has 1 entry');
|
|
620
|
+
assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'summary-exists: registry[0] status is complete');
|
|
621
|
+
assert.deepStrictEqual(state.activeSlice, null, 'summary-exists: activeSlice is null');
|
|
622
|
+
assert.deepStrictEqual(state.activeTask, null, 'summary-exists: activeTask is null');
|
|
638
623
|
} finally {
|
|
639
624
|
cleanup(base);
|
|
640
625
|
}
|
|
641
|
-
}
|
|
626
|
+
});
|
|
642
627
|
|
|
643
628
|
// ─── Test 13: multi-milestone completing-milestone ─────────────────────
|
|
644
|
-
|
|
645
|
-
{
|
|
629
|
+
test('multi-milestone completing-milestone', async () => {
|
|
646
630
|
const base = createFixtureBase();
|
|
647
631
|
try {
|
|
648
632
|
// M001: all slices done + summary exists → complete
|
|
@@ -687,29 +671,28 @@ Continue from step 2.
|
|
|
687
671
|
|
|
688
672
|
const state = await deriveState(base);
|
|
689
673
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
674
|
+
assert.deepStrictEqual(state.phase, 'completing-milestone', 'multi-completing: phase is completing-milestone');
|
|
675
|
+
assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'multi-completing: activeMilestone is M002');
|
|
676
|
+
assert.deepStrictEqual(state.activeSlice, null, 'multi-completing: activeSlice is null');
|
|
677
|
+
assert.deepStrictEqual(state.activeTask, null, 'multi-completing: activeTask is null');
|
|
678
|
+
assert.deepStrictEqual(state.registry.length, 3, 'multi-completing: registry has 3 entries');
|
|
679
|
+
assert.deepStrictEqual(state.registry[0]?.id, 'M001', 'multi-completing: registry[0] is M001');
|
|
680
|
+
assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'multi-completing: M001 is complete');
|
|
681
|
+
assert.deepStrictEqual(state.registry[1]?.id, 'M002', 'multi-completing: registry[1] is M002');
|
|
682
|
+
assert.deepStrictEqual(state.registry[1]?.status, 'active', 'multi-completing: M002 is active (completing-milestone)');
|
|
683
|
+
assert.deepStrictEqual(state.registry[2]?.id, 'M003', 'multi-completing: registry[2] is M003');
|
|
684
|
+
assert.deepStrictEqual(state.registry[2]?.status, 'pending', 'multi-completing: M003 is pending');
|
|
685
|
+
assert.deepStrictEqual(state.progress?.milestones?.done, 1, 'multi-completing: milestones done = 1');
|
|
686
|
+
assert.deepStrictEqual(state.progress?.milestones?.total, 3, 'multi-completing: milestones total = 3');
|
|
687
|
+
assert.deepStrictEqual(state.progress?.slices?.done, 2, 'multi-completing: slices done = 2');
|
|
688
|
+
assert.deepStrictEqual(state.progress?.slices?.total, 2, 'multi-completing: slices total = 2');
|
|
705
689
|
} finally {
|
|
706
690
|
cleanup(base);
|
|
707
691
|
}
|
|
708
|
-
}
|
|
692
|
+
});
|
|
709
693
|
|
|
710
694
|
// ═══ Milestone with summary but no roadmap → complete ═══════════════════
|
|
711
695
|
{
|
|
712
|
-
console.log('\n=== milestone with summary and no roadmap → complete ===');
|
|
713
696
|
const base = createFixtureBase();
|
|
714
697
|
try {
|
|
715
698
|
// M001, M002: completed milestones with summaries but no roadmaps
|
|
@@ -726,17 +709,17 @@ Continue from step 2.
|
|
|
726
709
|
|
|
727
710
|
const state = await deriveState(base);
|
|
728
711
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
712
|
+
assert.deepStrictEqual(state.phase, 'planning', 'summary-no-roadmap: phase is planning (active is M003)');
|
|
713
|
+
assert.deepStrictEqual(state.activeMilestone?.id, 'M003', 'summary-no-roadmap: active milestone is M003');
|
|
714
|
+
assert.deepStrictEqual(state.activeMilestone?.title, 'Polish', 'summary-no-roadmap: active title is Polish');
|
|
715
|
+
assert.deepStrictEqual(state.registry.length, 3, 'summary-no-roadmap: registry has 3 entries');
|
|
716
|
+
assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'summary-no-roadmap: M001 is complete');
|
|
717
|
+
assert.deepStrictEqual(state.registry[0]?.title, 'Bootstrap', 'summary-no-roadmap: M001 title from summary');
|
|
718
|
+
assert.deepStrictEqual(state.registry[1]?.status, 'complete', 'summary-no-roadmap: M002 is complete');
|
|
719
|
+
assert.deepStrictEqual(state.registry[1]?.title, 'Core Features', 'summary-no-roadmap: M002 title from summary');
|
|
720
|
+
assert.deepStrictEqual(state.registry[2]?.status, 'active', 'summary-no-roadmap: M003 is active');
|
|
721
|
+
assert.deepStrictEqual(state.progress?.milestones?.done, 2, 'summary-no-roadmap: milestones done = 2');
|
|
722
|
+
assert.deepStrictEqual(state.progress?.milestones?.total, 3, 'summary-no-roadmap: milestones total = 3');
|
|
740
723
|
} finally {
|
|
741
724
|
cleanup(base);
|
|
742
725
|
}
|
|
@@ -744,7 +727,6 @@ Continue from step 2.
|
|
|
744
727
|
|
|
745
728
|
// ═══ All milestones have summary but no roadmap → complete ═════════════
|
|
746
729
|
{
|
|
747
|
-
console.log('\n=== all milestones summary-only → complete ===');
|
|
748
730
|
const base = createFixtureBase();
|
|
749
731
|
try {
|
|
750
732
|
const m1dir = join(base, '.gsd', 'milestones', 'M001');
|
|
@@ -752,16 +734,15 @@ Continue from step 2.
|
|
|
752
734
|
writeFileSync(join(m1dir, 'M001-SUMMARY.md'), '---\ntitle: Done\n---\nAll done.');
|
|
753
735
|
|
|
754
736
|
const state = await deriveState(base);
|
|
755
|
-
|
|
756
|
-
|
|
737
|
+
assert.deepStrictEqual(state.phase, 'complete', 'all-summary-only: phase is complete');
|
|
738
|
+
assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'all-summary-only: M001 is complete');
|
|
757
739
|
} finally {
|
|
758
740
|
cleanup(base);
|
|
759
741
|
}
|
|
760
742
|
}
|
|
761
743
|
|
|
762
744
|
// ─── Empty plan (zero tasks) stays in planning, not summarizing (#454) ──
|
|
763
|
-
|
|
764
|
-
{
|
|
745
|
+
test('empty plan → planning (not summarizing)', async () => {
|
|
765
746
|
const base = createFixtureBase();
|
|
766
747
|
try {
|
|
767
748
|
writeRoadmap(base, 'M001', `---
|
|
@@ -786,17 +767,16 @@ slice: S01
|
|
|
786
767
|
## Tasks
|
|
787
768
|
`);
|
|
788
769
|
const state = await deriveState(base);
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
770
|
+
assert.deepStrictEqual(state.phase, 'planning', 'empty plan stays in planning');
|
|
771
|
+
assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'active slice is S01');
|
|
772
|
+
assert.deepStrictEqual(state.activeTask, null, 'no active task');
|
|
792
773
|
} finally {
|
|
793
774
|
cleanup(base);
|
|
794
775
|
}
|
|
795
|
-
}
|
|
776
|
+
});
|
|
796
777
|
|
|
797
778
|
// ─── Test: completed M001 (summary, no validation) skipped for active M003 (#864) ────
|
|
798
|
-
|
|
799
|
-
{
|
|
779
|
+
test('completed milestone with summary but no validation is not active (#864)', async () => {
|
|
800
780
|
const base = createFixtureBase();
|
|
801
781
|
try {
|
|
802
782
|
// M001: all slices done, has summary, no validation
|
|
@@ -806,17 +786,16 @@ slice: S01
|
|
|
806
786
|
writeRoadmap(base, 'M003', `# M003: Active Milestone\n\n**Vision:** Do stuff.\n\n## Slices\n\n- [ ] **S01: Work slice** \`risk:low\` \`depends:[]\`\n > Needs work.\n`);
|
|
807
787
|
|
|
808
788
|
const state = await deriveState(base);
|
|
809
|
-
|
|
789
|
+
assert.deepStrictEqual(state.activeMilestone?.id, 'M003', 'active milestone is M003, not completed M001');
|
|
810
790
|
const m001Entry = state.registry.find(e => e.id === 'M001');
|
|
811
|
-
|
|
791
|
+
assert.deepStrictEqual(m001Entry?.status, 'complete', 'M001 is marked complete despite no validation');
|
|
812
792
|
} finally {
|
|
813
793
|
cleanup(base);
|
|
814
794
|
}
|
|
815
|
-
}
|
|
795
|
+
});
|
|
816
796
|
|
|
817
797
|
// ─── Test: completed M001 with summary AND validation is complete (#864) ────
|
|
818
|
-
|
|
819
|
-
{
|
|
798
|
+
test('completed milestone with summary and validation is complete', async () => {
|
|
820
799
|
const base = createFixtureBase();
|
|
821
800
|
try {
|
|
822
801
|
writeRoadmap(base, 'M001', `# M001: First Milestone\n\n**Vision:** Done.\n\n## Slices\n\n- [x] **S01: Done slice** \`risk:low\` \`depends:[]\`\n > Completed.\n`);
|
|
@@ -825,32 +804,30 @@ slice: S01
|
|
|
825
804
|
writeRoadmap(base, 'M003', `# M003: Active Milestone\n\n**Vision:** Do stuff.\n\n## Slices\n\n- [ ] **S01: Work slice** \`risk:low\` \`depends:[]\`\n > Needs work.\n`);
|
|
826
805
|
|
|
827
806
|
const state = await deriveState(base);
|
|
828
|
-
|
|
807
|
+
assert.deepStrictEqual(state.activeMilestone?.id, 'M003', 'active milestone is M003');
|
|
829
808
|
const m001Entry = state.registry.find(e => e.id === 'M001');
|
|
830
|
-
|
|
809
|
+
assert.deepStrictEqual(m001Entry?.status, 'complete', 'M001 with both summary and validation is complete');
|
|
831
810
|
} finally {
|
|
832
811
|
cleanup(base);
|
|
833
812
|
}
|
|
834
|
-
}
|
|
813
|
+
});
|
|
835
814
|
|
|
836
815
|
// ─── Test: all slices done, no summary, no validation → needs validation (#864) ────
|
|
837
|
-
|
|
838
|
-
{
|
|
816
|
+
test('all slices done, no summary, no validation → validating-milestone', async () => {
|
|
839
817
|
const base = createFixtureBase();
|
|
840
818
|
try {
|
|
841
819
|
writeRoadmap(base, 'M001', `# M001: First Milestone\n\n**Vision:** Validate me.\n\n## Slices\n\n- [x] **S01: Done slice** \`risk:low\` \`depends:[]\`\n > Completed.\n`);
|
|
842
820
|
// No summary, no validation — this should be active for validation
|
|
843
821
|
|
|
844
822
|
const state = await deriveState(base);
|
|
845
|
-
|
|
823
|
+
assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'M001 is active for validation');
|
|
846
824
|
} finally {
|
|
847
825
|
cleanup(base);
|
|
848
826
|
}
|
|
849
|
-
}
|
|
827
|
+
});
|
|
850
828
|
|
|
851
829
|
// ─── Test: all slices done, validation pass, no summary → needs completion (#864) ────
|
|
852
|
-
|
|
853
|
-
{
|
|
830
|
+
test('all slices done, validation pass, no summary → completing-milestone', async () => {
|
|
854
831
|
const base = createFixtureBase();
|
|
855
832
|
try {
|
|
856
833
|
writeRoadmap(base, 'M001', `# M001: First Milestone\n\n**Vision:** Complete me.\n\n## Slices\n\n- [x] **S01: Done slice** \`risk:low\` \`depends:[]\`\n > Completed.\n`);
|
|
@@ -858,15 +835,14 @@ slice: S01
|
|
|
858
835
|
// No summary — validated but not yet completed
|
|
859
836
|
|
|
860
837
|
const state = await deriveState(base);
|
|
861
|
-
|
|
838
|
+
assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'M001 is active for completion');
|
|
862
839
|
} finally {
|
|
863
840
|
cleanup(base);
|
|
864
841
|
}
|
|
865
|
-
}
|
|
842
|
+
});
|
|
866
843
|
|
|
867
844
|
// ─── Test: unchecked roadmap slices + summary → complete (summary is terminal) ────
|
|
868
|
-
|
|
869
|
-
{
|
|
845
|
+
test('unchecked roadmap slices + summary → complete (summary is terminal)', async () => {
|
|
870
846
|
const base = createFixtureBase();
|
|
871
847
|
try {
|
|
872
848
|
// M001: roadmap has unchecked slices but a summary exists — should be complete
|
|
@@ -877,16 +853,15 @@ slice: S01
|
|
|
877
853
|
|
|
878
854
|
const state = await deriveState(base);
|
|
879
855
|
const m001Entry = state.registry.find(e => e.id === 'M001');
|
|
880
|
-
|
|
881
|
-
|
|
856
|
+
assert.deepStrictEqual(m001Entry?.status, 'complete', 'M001 with unchecked roadmap + summary is complete');
|
|
857
|
+
assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'active milestone is M002, not M001');
|
|
882
858
|
} finally {
|
|
883
859
|
cleanup(base);
|
|
884
860
|
}
|
|
885
|
-
}
|
|
861
|
+
});
|
|
886
862
|
|
|
887
863
|
// ─── Test: unchecked roadmap + summary counts toward completeMilestoneIds (deps) ────
|
|
888
|
-
|
|
889
|
-
{
|
|
864
|
+
test('unchecked roadmap + summary satisfies dependency', async () => {
|
|
890
865
|
const base = createFixtureBase();
|
|
891
866
|
try {
|
|
892
867
|
// M001: unchecked roadmap + summary → complete
|
|
@@ -899,17 +874,16 @@ slice: S01
|
|
|
899
874
|
writeFileSync(join(contextDir, 'M002-CONTEXT.md'), '---\ndepends_on:\n - M001\n---\n\n# M002 Context\n\nDepends on M001.');
|
|
900
875
|
|
|
901
876
|
const state = await deriveState(base);
|
|
902
|
-
|
|
877
|
+
assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'M002 is active — M001 dependency satisfied via summary');
|
|
903
878
|
const m002Entry = state.registry.find(e => e.id === 'M002');
|
|
904
|
-
|
|
879
|
+
assert.deepStrictEqual(m002Entry?.status, 'active', 'M002 status is active, not pending');
|
|
905
880
|
} finally {
|
|
906
881
|
cleanup(base);
|
|
907
882
|
}
|
|
908
|
-
}
|
|
883
|
+
});
|
|
909
884
|
|
|
910
885
|
// ─── Test: ghost milestone (only META.json) is skipped ───────────────
|
|
911
|
-
|
|
912
|
-
{
|
|
886
|
+
test('ghost milestone (only META.json) is skipped', async () => {
|
|
913
887
|
const base = createFixtureBase();
|
|
914
888
|
try {
|
|
915
889
|
// Create a ghost milestone directory with only META.json
|
|
@@ -918,21 +892,20 @@ slice: S01
|
|
|
918
892
|
writeFileSync(join(ghostDir, 'META.json'), JSON.stringify({ id: 'M001' }));
|
|
919
893
|
|
|
920
894
|
// isGhostMilestone should detect it
|
|
921
|
-
|
|
895
|
+
assert.ok(isGhostMilestone(base, 'M001'), 'M001 is a ghost milestone');
|
|
922
896
|
|
|
923
897
|
// deriveState should treat this as pre-planning (no real milestones)
|
|
924
898
|
const state = await deriveState(base);
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
899
|
+
assert.deepStrictEqual(state.phase, 'pre-planning', 'ghost-only: phase is pre-planning');
|
|
900
|
+
assert.deepStrictEqual(state.activeMilestone, null, 'ghost-only: no active milestone');
|
|
901
|
+
assert.deepStrictEqual(state.registry.length, 0, 'ghost-only: registry is empty');
|
|
928
902
|
} finally {
|
|
929
903
|
cleanup(base);
|
|
930
904
|
}
|
|
931
|
-
}
|
|
905
|
+
});
|
|
932
906
|
|
|
933
907
|
// ─── Test: ghost milestone skipped when real milestones exist ──────────
|
|
934
|
-
|
|
935
|
-
{
|
|
908
|
+
test('ghost milestone skipped alongside real milestones', async () => {
|
|
936
909
|
const base = createFixtureBase();
|
|
937
910
|
try {
|
|
938
911
|
// M001: ghost (only META.json)
|
|
@@ -946,20 +919,19 @@ slice: S01
|
|
|
946
919
|
writeFileSync(join(realDir, 'M002-CONTEXT.md'), '# Real Milestone\n\nThis has content.');
|
|
947
920
|
|
|
948
921
|
const state = await deriveState(base);
|
|
949
|
-
|
|
922
|
+
assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'ghost+real: active milestone is M002');
|
|
950
923
|
// Ghost M001 should not appear in the registry
|
|
951
924
|
const m001Entry = state.registry.find(e => e.id === 'M001');
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
925
|
+
assert.deepStrictEqual(m001Entry, undefined, 'ghost+real: M001 not in registry');
|
|
926
|
+
assert.deepStrictEqual(state.registry.length, 1, 'ghost+real: registry has 1 entry');
|
|
927
|
+
assert.deepStrictEqual(state.registry[0]?.status, 'active', 'ghost+real: M002 is active');
|
|
955
928
|
} finally {
|
|
956
929
|
cleanup(base);
|
|
957
930
|
}
|
|
958
|
-
}
|
|
931
|
+
});
|
|
959
932
|
|
|
960
933
|
// ─── Test: zero-slice roadmap → pre-planning, not blocked (#1785) ────
|
|
961
|
-
|
|
962
|
-
{
|
|
934
|
+
test('zero-slice roadmap → pre-planning, not blocked (#1785)', async () => {
|
|
963
935
|
const base = createFixtureBase();
|
|
964
936
|
try {
|
|
965
937
|
// Write a stub roadmap with zero slices (placeholder text, no slice definitions)
|
|
@@ -967,22 +939,15 @@ slice: S01
|
|
|
967
939
|
|
|
968
940
|
const state = await deriveState(base);
|
|
969
941
|
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
942
|
+
assert.deepStrictEqual(state.phase, 'pre-planning', 'phase is pre-planning when roadmap has zero slices');
|
|
943
|
+
assert.ok(state.activeMilestone !== null, 'activeMilestone is set');
|
|
944
|
+
assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'activeMilestone is M001');
|
|
945
|
+
assert.deepStrictEqual(state.activeSlice, null, 'activeSlice is null');
|
|
946
|
+
assert.deepStrictEqual(state.activeTask, null, 'activeTask is null');
|
|
947
|
+
assert.deepStrictEqual(state.blockers.length, 0, 'no blockers reported');
|
|
948
|
+
assert.ok(state.nextAction.includes('M001'), 'nextAction references M001');
|
|
977
949
|
} finally {
|
|
978
950
|
cleanup(base);
|
|
979
951
|
}
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
report();
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
main().catch((error) => {
|
|
986
|
-
console.error(error);
|
|
987
|
-
process.exit(1);
|
|
952
|
+
});
|
|
988
953
|
});
|