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
|
@@ -22,72 +22,72 @@ import {
|
|
|
22
22
|
|
|
23
23
|
import { renderPreferencesForSystemPrompt } from '../preferences.ts';
|
|
24
24
|
import type { GSDPreferences } from '../preferences.ts';
|
|
25
|
-
import {
|
|
25
|
+
import { describe, test } from 'node:test';
|
|
26
|
+
import assert from 'node:assert/strict';
|
|
26
27
|
|
|
27
28
|
|
|
28
|
-
const { assertEq, assertTrue, assertMatch, report } = createTestContext();
|
|
29
29
|
// ─── Tests ─────────────────────────────────────────────────────────────────
|
|
30
30
|
|
|
31
|
-
async
|
|
31
|
+
describe('unique-milestone-ids', async () => {
|
|
32
32
|
console.log('unique-milestone-ids tests');
|
|
33
33
|
|
|
34
34
|
// (a) MILESTONE_ID_RE
|
|
35
35
|
{
|
|
36
36
|
console.log(' (a) MILESTONE_ID_RE');
|
|
37
37
|
// Should match
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
assert.ok(MILESTONE_ID_RE.test('M001'), 'matches M001');
|
|
39
|
+
assert.ok(MILESTONE_ID_RE.test('M999'), 'matches M999');
|
|
40
|
+
assert.ok(MILESTONE_ID_RE.test('M001-abc123'), 'matches M001-abc123');
|
|
41
|
+
assert.ok(MILESTONE_ID_RE.test('M042-z9a8b7'), 'matches M042-z9a8b7');
|
|
42
42
|
|
|
43
43
|
// Should reject
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
44
|
+
assert.ok(!MILESTONE_ID_RE.test('M1'), 'rejects M1 (too few digits)');
|
|
45
|
+
assert.ok(!MILESTONE_ID_RE.test('M0001'), 'rejects M0001 (too many digits)');
|
|
46
|
+
assert.ok(!MILESTONE_ID_RE.test('M001-ABCDEF'), 'rejects M001-ABCDEF (uppercase prefix)');
|
|
47
|
+
assert.ok(!MILESTONE_ID_RE.test('M001-short'), 'rejects M001-short (5-char prefix)');
|
|
48
|
+
assert.ok(!MILESTONE_ID_RE.test('M001-toolong1'), 'rejects M001-toolong1 (>6-char prefix)');
|
|
49
|
+
assert.ok(!MILESTONE_ID_RE.test('IM001'), 'rejects IM001 (prefix before M)');
|
|
50
|
+
assert.ok(!MILESTONE_ID_RE.test(''), 'rejects empty string');
|
|
51
|
+
assert.ok(!MILESTONE_ID_RE.test('M001extra'), 'rejects M001extra (trailing chars)');
|
|
52
|
+
assert.ok(!MILESTONE_ID_RE.test('notes'), 'rejects non-milestone string');
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
// (b) extractMilestoneSeq
|
|
56
56
|
{
|
|
57
57
|
console.log(' (b) extractMilestoneSeq');
|
|
58
58
|
// Old format
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
assert.deepStrictEqual(extractMilestoneSeq('M001'), 1, 'M001 → 1');
|
|
60
|
+
assert.deepStrictEqual(extractMilestoneSeq('M042'), 42, 'M042 → 42');
|
|
61
|
+
assert.deepStrictEqual(extractMilestoneSeq('M999'), 999, 'M999 → 999');
|
|
62
62
|
|
|
63
63
|
// Unique format
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
assert.deepStrictEqual(extractMilestoneSeq('M001-abc123'), 1, 'M001-abc123 → 1');
|
|
65
|
+
assert.deepStrictEqual(extractMilestoneSeq('M042-z9a8b7'), 42, 'M042-z9a8b7 → 42');
|
|
66
66
|
|
|
67
67
|
// Invalid → 0
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
assert.deepStrictEqual(extractMilestoneSeq(''), 0, 'empty → 0');
|
|
69
|
+
assert.deepStrictEqual(extractMilestoneSeq('notes'), 0, 'notes → 0');
|
|
70
|
+
assert.deepStrictEqual(extractMilestoneSeq('M1'), 0, 'M1 → 0');
|
|
71
|
+
assert.deepStrictEqual(extractMilestoneSeq('.DS_Store'), 0, '.DS_Store → 0');
|
|
72
|
+
assert.deepStrictEqual(extractMilestoneSeq('M-ABC-001'), 0, 'M-ABC-001 (old format) → 0');
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
// (c) parseMilestoneId
|
|
76
76
|
{
|
|
77
77
|
console.log(' (c) parseMilestoneId');
|
|
78
78
|
// Old format — no suffix
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
assert.deepStrictEqual(parseMilestoneId('M001'), { num: 1 }, 'M001 → { num: 1 }');
|
|
80
|
+
assert.deepStrictEqual(parseMilestoneId('M042'), { num: 42 }, 'M042 → { num: 42 }');
|
|
81
81
|
|
|
82
82
|
// Unique format — with suffix
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
assert.deepStrictEqual(parseMilestoneId('M001-abc123'), { suffix: 'abc123', num: 1 }, 'M001-abc123 → { suffix, num }');
|
|
84
|
+
assert.deepStrictEqual(parseMilestoneId('M042-z9a8b7'), { suffix: 'z9a8b7', num: 42 }, 'M042-z9a8b7 → { suffix, num }');
|
|
85
85
|
|
|
86
86
|
// Invalid → { num: 0 }
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
assert.deepStrictEqual(parseMilestoneId(''), { num: 0 }, 'empty → { num: 0 }');
|
|
88
|
+
assert.deepStrictEqual(parseMilestoneId('notes'), { num: 0 }, 'notes → { num: 0 }');
|
|
89
|
+
assert.deepStrictEqual(parseMilestoneId('M001-ABCDEF'), { num: 0 }, 'uppercase suffix → { num: 0 }');
|
|
90
|
+
assert.deepStrictEqual(parseMilestoneId('M1'), { num: 0 }, 'M1 → { num: 0 }');
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
// (d) milestoneIdSort
|
|
@@ -95,81 +95,81 @@ async function main(): Promise<void> {
|
|
|
95
95
|
console.log(' (d) milestoneIdSort');
|
|
96
96
|
const mixed = ['M003-abc123', 'M001', 'M002-z9a8b7'];
|
|
97
97
|
const sorted = [...mixed].sort(milestoneIdSort);
|
|
98
|
-
|
|
98
|
+
assert.deepStrictEqual(sorted, ['M001', 'M002-z9a8b7', 'M003-abc123'], 'sorts mixed IDs by sequence number');
|
|
99
99
|
|
|
100
100
|
// All old format
|
|
101
101
|
const oldOnly = ['M003', 'M001', 'M002'];
|
|
102
|
-
|
|
102
|
+
assert.deepStrictEqual([...oldOnly].sort(milestoneIdSort), ['M001', 'M002', 'M003'], 'sorts old-format IDs');
|
|
103
103
|
|
|
104
104
|
// Invalid entries sort to front (seq 0)
|
|
105
105
|
const withInvalid = ['M002', 'notes', 'M001'];
|
|
106
|
-
|
|
106
|
+
assert.deepStrictEqual([...withInvalid].sort(milestoneIdSort), ['notes', 'M001', 'M002'], 'invalid entries (seq 0) sort first');
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
// (e) generateMilestoneSuffix
|
|
110
110
|
{
|
|
111
111
|
console.log(' (e) generateMilestoneSuffix');
|
|
112
112
|
const suffix1 = generateMilestoneSuffix();
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
assert.deepStrictEqual(suffix1.length, 6, 'suffix length is 6');
|
|
114
|
+
assert.match(suffix1, /^[a-z0-9]{6}$/, 'suffix matches [a-z0-9]{6}');
|
|
115
115
|
|
|
116
116
|
const suffix2 = generateMilestoneSuffix();
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
assert.deepStrictEqual(suffix2.length, 6, 'second suffix length is 6');
|
|
118
|
+
assert.match(suffix2, /^[a-z0-9]{6}$/, 'second suffix matches [a-z0-9]{6}');
|
|
119
119
|
|
|
120
120
|
// Two calls should produce different results (36^6 = ~2.2B possibilities)
|
|
121
|
-
|
|
121
|
+
assert.ok(suffix1 !== suffix2, 'two calls produce different suffixes');
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
// (f) nextMilestoneId
|
|
125
125
|
{
|
|
126
126
|
console.log(' (f) nextMilestoneId');
|
|
127
127
|
// uniqueEnabled=false (default) → old format
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
128
|
+
assert.deepStrictEqual(nextMilestoneId([]), 'M001', 'empty + uniqueEnabled=false → M001');
|
|
129
|
+
assert.deepStrictEqual(nextMilestoneId(['M001', 'M002']), 'M003', 'sequential + uniqueEnabled=false → M003');
|
|
130
|
+
assert.deepStrictEqual(nextMilestoneId(['M001', 'M002'], false), 'M003', 'explicit false → M003');
|
|
131
131
|
|
|
132
132
|
// uniqueEnabled=true → unique format
|
|
133
133
|
const newId = nextMilestoneId([], true);
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
134
|
+
assert.match(newId, MILESTONE_ID_RE, 'uniqueEnabled=true produces valid ID');
|
|
135
|
+
assert.ok(newId.startsWith('M001-'), 'uniqueEnabled=true starts with M001-');
|
|
136
|
+
assert.match(newId, /^M001-[a-z0-9]{6}$/, 'empty + uniqueEnabled=true → M001-{rand6}');
|
|
137
137
|
|
|
138
138
|
// Mixed array with uniqueEnabled=true
|
|
139
139
|
const mixedIds = ['M001', 'M003-abc123', 'M002'];
|
|
140
140
|
const nextNew = nextMilestoneId(mixedIds, true);
|
|
141
|
-
|
|
142
|
-
|
|
141
|
+
assert.match(nextNew, MILESTONE_ID_RE, 'mixed array + uniqueEnabled=true → valid ID');
|
|
142
|
+
assert.match(nextNew, /^M004-[a-z0-9]{6}$/, 'mixed array max=3 → M004-{rand6}');
|
|
143
143
|
|
|
144
144
|
// Mixed array with uniqueEnabled=false
|
|
145
|
-
|
|
145
|
+
assert.deepStrictEqual(nextMilestoneId(mixedIds, false), 'M004', 'mixed array + uniqueEnabled=false → M004');
|
|
146
146
|
|
|
147
147
|
// Correct sequential number from mixed arrays
|
|
148
148
|
const mixedIds2 = ['M005-xyz999', 'M002'];
|
|
149
|
-
|
|
149
|
+
assert.deepStrictEqual(nextMilestoneId(mixedIds2, false), 'M006', 'mixed max=5 → M006');
|
|
150
150
|
const nextNew2 = nextMilestoneId(mixedIds2, true);
|
|
151
|
-
|
|
151
|
+
assert.match(nextNew2, /^M006-[a-z0-9]{6}$/, 'mixed max=5 + unique → M006-{rand6}');
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
// (g) maxMilestoneNum
|
|
155
155
|
{
|
|
156
156
|
console.log(' (g) maxMilestoneNum');
|
|
157
157
|
// Empty
|
|
158
|
-
|
|
158
|
+
assert.deepStrictEqual(maxMilestoneNum([]), 0, 'empty → 0');
|
|
159
159
|
|
|
160
160
|
// Old format only
|
|
161
|
-
|
|
161
|
+
assert.deepStrictEqual(maxMilestoneNum(['M001', 'M002', 'M003']), 3, 'old format only → 3');
|
|
162
162
|
|
|
163
163
|
// Unique format only — must not return NaN
|
|
164
|
-
|
|
165
|
-
|
|
164
|
+
assert.deepStrictEqual(maxMilestoneNum(['M001-abc123', 'M002-def456']), 2, 'unique format only → 2');
|
|
165
|
+
assert.ok(!Number.isNaN(maxMilestoneNum(['M001-abc123'])), 'unique format does not return NaN');
|
|
166
166
|
|
|
167
167
|
// Mixed formats
|
|
168
|
-
|
|
168
|
+
assert.deepStrictEqual(maxMilestoneNum(['M001', 'M003-abc123', 'M002']), 3, 'mixed → 3');
|
|
169
169
|
|
|
170
170
|
// Non-matching entries ignored
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
assert.deepStrictEqual(maxMilestoneNum(['M001', 'notes', '.DS_Store', 'M003']), 3, 'non-matching ignored → 3');
|
|
172
|
+
assert.deepStrictEqual(maxMilestoneNum(['notes', '.DS_Store']), 0, 'all non-matching → 0');
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
// (h) Preferences round-trip via renderPreferencesForSystemPrompt
|
|
@@ -179,41 +179,25 @@ async function main(): Promise<void> {
|
|
|
179
179
|
// validate { unique_milestone_ids: true } → field preserved (no validation error)
|
|
180
180
|
const prefsTrue: GSDPreferences = { unique_milestone_ids: true };
|
|
181
181
|
const renderedTrue = renderPreferencesForSystemPrompt(prefsTrue);
|
|
182
|
-
|
|
182
|
+
assert.ok(!renderedTrue.includes('some preference values were ignored'), 'unique_milestone_ids: true validates without error');
|
|
183
183
|
|
|
184
184
|
// validate { unique_milestone_ids: undefined } → field absent (no error)
|
|
185
185
|
const prefsUndefined: GSDPreferences = {};
|
|
186
186
|
const renderedUndefined = renderPreferencesForSystemPrompt(prefsUndefined);
|
|
187
|
-
|
|
187
|
+
assert.ok(!renderedUndefined.includes('some preference values were ignored'), 'undefined unique_milestone_ids validates without error');
|
|
188
188
|
|
|
189
189
|
// validate { unique_milestone_ids: false } → also valid
|
|
190
190
|
const prefsFalse: GSDPreferences = { unique_milestone_ids: false };
|
|
191
191
|
const renderedFalse = renderPreferencesForSystemPrompt(prefsFalse);
|
|
192
|
-
|
|
192
|
+
assert.ok(!renderedFalse.includes('some preference values were ignored'), 'unique_milestone_ids: false validates without error');
|
|
193
193
|
|
|
194
194
|
// validate coercion: truthy non-boolean → coerced to boolean (no crash)
|
|
195
195
|
const prefsCoerced: GSDPreferences = { unique_milestone_ids: 1 as unknown as boolean };
|
|
196
196
|
const renderedCoerced = renderPreferencesForSystemPrompt(prefsCoerced);
|
|
197
|
-
|
|
197
|
+
assert.ok(!renderedCoerced.includes('some preference values were ignored'), 'truthy non-boolean coerces without validation error');
|
|
198
198
|
|
|
199
199
|
// GSDPreferences interface accepts the field (compile-time check — if this compiles, it works)
|
|
200
200
|
const prefs: GSDPreferences = { unique_milestone_ids: true, version: 1 };
|
|
201
|
-
|
|
201
|
+
assert.ok(prefs.unique_milestone_ids === true, 'GSDPreferences interface accepts unique_milestone_ids');
|
|
202
202
|
}
|
|
203
|
-
|
|
204
|
-
report();
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// When run via vitest, wrap in test(); when run via tsx, call directly.
|
|
208
|
-
const isVitest = typeof globalThis !== 'undefined' && (globalThis as any).__vitest_worker__?.config?.defines != null && 'vitest' in (globalThis as any).__vitest_worker__.config.defines || process.env.VITEST;
|
|
209
|
-
if (isVitest) {
|
|
210
|
-
const { test } = await import('node:test');
|
|
211
|
-
test('unique-milestone-ids: all ID primitives handle both formats', async () => {
|
|
212
|
-
await main();
|
|
213
|
-
});
|
|
214
|
-
} else {
|
|
215
|
-
main().catch((error) => {
|
|
216
|
-
console.error(error);
|
|
217
|
-
process.exit(1);
|
|
218
|
-
});
|
|
219
|
-
}
|
|
203
|
+
});
|
|
@@ -9,9 +9,9 @@ import {
|
|
|
9
9
|
writeUnitRuntimeRecord,
|
|
10
10
|
} from "../unit-runtime.ts";
|
|
11
11
|
import { clearPathCache } from '../paths.ts';
|
|
12
|
-
import {
|
|
12
|
+
import { test } from 'node:test';
|
|
13
|
+
import assert from 'node:assert/strict';
|
|
13
14
|
|
|
14
|
-
const { assertEq, assertTrue, report } = createTestContext();
|
|
15
15
|
const base = mkdtempSync(join(tmpdir(), "gsd-unit-runtime-test-"));
|
|
16
16
|
const tasksDir = join(base, ".gsd", "milestones", "M100", "slices", "S02", "tasks");
|
|
17
17
|
mkdirSync(tasksDir, { recursive: true });
|
|
@@ -25,22 +25,22 @@ writeFileSync(
|
|
|
25
25
|
console.log("\n=== runtime record write/read/update ===");
|
|
26
26
|
{
|
|
27
27
|
const first = writeUnitRuntimeRecord(base, "execute-task", "M100/S02/T09", 1000, { phase: "dispatched" });
|
|
28
|
-
|
|
28
|
+
assert.deepStrictEqual(first.phase, "dispatched", "initial phase");
|
|
29
29
|
const second = writeUnitRuntimeRecord(base, "execute-task", "M100/S02/T09", 1000, { phase: "wrapup-warning-sent", wrapupWarningSent: true });
|
|
30
|
-
|
|
30
|
+
assert.deepStrictEqual(second.wrapupWarningSent, true, "warning persisted");
|
|
31
31
|
const loaded = readUnitRuntimeRecord(base, "execute-task", "M100/S02/T09");
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
assert.ok(loaded !== null, "record readable");
|
|
33
|
+
assert.deepStrictEqual(loaded!.phase, "wrapup-warning-sent", "updated phase readable");
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
console.log("\n=== execute-task durability inspection ===");
|
|
37
37
|
{
|
|
38
38
|
let status = await inspectExecuteTaskDurability(base, "M100/S02/T09");
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
assert.ok(status !== null, "status exists");
|
|
40
|
+
assert.deepStrictEqual(status!.summaryExists, false, "summary initially missing");
|
|
41
|
+
assert.deepStrictEqual(status!.taskChecked, false, "task initially unchecked");
|
|
42
|
+
assert.deepStrictEqual(status!.nextActionAdvanced, false, "next action initially stale");
|
|
43
|
+
assert.ok(/summary missing/i.test(formatExecuteTaskRecoveryStatus(status!)), "diagnostic mentions summary");
|
|
44
44
|
|
|
45
45
|
writeFileSync(join(tasksDir, "T09-SUMMARY.md"), "# done\n", "utf-8");
|
|
46
46
|
writeFileSync(
|
|
@@ -52,17 +52,17 @@ console.log("\n=== execute-task durability inspection ===");
|
|
|
52
52
|
clearPathCache();
|
|
53
53
|
|
|
54
54
|
status = await inspectExecuteTaskDurability(base, "M100/S02/T09");
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
assert.deepStrictEqual(status!.summaryExists, true, "summary found after write");
|
|
56
|
+
assert.deepStrictEqual(status!.taskChecked, true, "task checked after update");
|
|
57
|
+
assert.deepStrictEqual(status!.nextActionAdvanced, true, "next action advanced after update");
|
|
58
|
+
assert.deepStrictEqual(formatExecuteTaskRecoveryStatus(status!), "all durable task artifacts present", "clean diagnostic when complete");
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
console.log("\n=== runtime record cleanup ===");
|
|
62
62
|
{
|
|
63
63
|
clearUnitRuntimeRecord(base, "execute-task", "M100/S02/T09");
|
|
64
64
|
const loaded = readUnitRuntimeRecord(base, "execute-task", "M100/S02/T09");
|
|
65
|
-
|
|
65
|
+
assert.deepStrictEqual(loaded, null, "record removed");
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
console.log("\n=== hook unit type sanitization (slash in unitType) ===");
|
|
@@ -70,23 +70,23 @@ console.log("\n=== hook unit type sanitization (slash in unitType) ===");
|
|
|
70
70
|
// Hook units have unitType like "hook/code-review" with a slash
|
|
71
71
|
// This should NOT create a subdirectory - the slash must be sanitized
|
|
72
72
|
const hookRecord = writeUnitRuntimeRecord(base, "hook/code-review", "M100/S02/T10", 2000, { phase: "dispatched" });
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
assert.deepStrictEqual(hookRecord.unitType, "hook/code-review", "unitType preserved in record");
|
|
74
|
+
assert.deepStrictEqual(hookRecord.unitId, "M100/S02/T10", "unitId preserved in record");
|
|
75
75
|
|
|
76
76
|
const loaded = readUnitRuntimeRecord(base, "hook/code-review", "M100/S02/T10");
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
assert.ok(loaded !== null, "hook record readable");
|
|
78
|
+
assert.deepStrictEqual(loaded!.phase, "dispatched", "hook phase correct");
|
|
79
79
|
|
|
80
80
|
// Verify the file is in the units dir, not in a subdirectory
|
|
81
81
|
const unitsDir = join(base, ".gsd", "runtime", "units");
|
|
82
82
|
const files = readdirSync(unitsDir);
|
|
83
83
|
const hookFile = files.find((f: string) => f.includes("hook-code-review"));
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
assert.ok(hookFile !== undefined, "hook file exists with sanitized name");
|
|
85
|
+
assert.ok(!files.some((f: string) => f === "hook"), "no 'hook' subdirectory created");
|
|
86
86
|
|
|
87
87
|
clearUnitRuntimeRecord(base, "hook/code-review", "M100/S02/T10");
|
|
88
88
|
const cleared = readUnitRuntimeRecord(base, "hook/code-review", "M100/S02/T10");
|
|
89
|
-
|
|
89
|
+
assert.deepStrictEqual(cleared, null, "hook record removed");
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
// ─── Must-have durability integration tests ───────────────────────────────
|
|
@@ -121,13 +121,13 @@ console.log("\n=== must-haves: all mentioned in summary ===");
|
|
|
121
121
|
writeFileSync(join(mhBase, ".gsd", "STATE.md"), "## Next Action\nExecute T02 for S01: next thing\n", "utf-8");
|
|
122
122
|
|
|
123
123
|
const status = await inspectExecuteTaskDurability(mhBase, "M200/S01/T01");
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
assert.ok(status !== null, "mh-all: status exists");
|
|
125
|
+
assert.deepStrictEqual(status!.mustHaveCount, 3, "mh-all: mustHaveCount is 3");
|
|
126
|
+
assert.deepStrictEqual(status!.mustHavesMentionedInSummary, 3, "mh-all: all 3 must-haves mentioned");
|
|
127
|
+
assert.deepStrictEqual(status!.summaryExists, true, "mh-all: summary exists");
|
|
128
|
+
assert.deepStrictEqual(status!.taskChecked, true, "mh-all: task checked");
|
|
129
129
|
const diag = formatExecuteTaskRecoveryStatus(status!);
|
|
130
|
-
|
|
130
|
+
assert.deepStrictEqual(diag, "all durable task artifacts present", "mh-all: diagnostic is clean when all must-haves met");
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
console.log("\n=== must-haves: partially mentioned in summary ===");
|
|
@@ -156,12 +156,12 @@ console.log("\n=== must-haves: partially mentioned in summary ===");
|
|
|
156
156
|
|
|
157
157
|
clearPathCache();
|
|
158
158
|
const status = await inspectExecuteTaskDurability(mhBase, "M200/S02/T01");
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
159
|
+
assert.ok(status !== null, "mh-partial: status exists");
|
|
160
|
+
assert.deepStrictEqual(status!.mustHaveCount, 3, "mh-partial: mustHaveCount is 3");
|
|
161
|
+
assert.deepStrictEqual(status!.mustHavesMentionedInSummary, 1, "mh-partial: only 1 must-have mentioned");
|
|
162
162
|
const diag = formatExecuteTaskRecoveryStatus(status!);
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
assert.ok(diag.includes("must-have gap"), "mh-partial: diagnostic includes 'must-have gap'");
|
|
164
|
+
assert.ok(diag.includes("1 of 3"), "mh-partial: diagnostic includes '1 of 3'");
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
console.log("\n=== must-haves: no task plan file ===");
|
|
@@ -184,9 +184,9 @@ console.log("\n=== must-haves: no task plan file ===");
|
|
|
184
184
|
|
|
185
185
|
clearPathCache();
|
|
186
186
|
const status = await inspectExecuteTaskDurability(mhBase, "M200/S03/T01");
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
187
|
+
assert.ok(status !== null, "mh-noplan: status exists");
|
|
188
|
+
assert.deepStrictEqual(status!.mustHaveCount, 0, "mh-noplan: mustHaveCount is 0 when no task plan");
|
|
189
|
+
assert.deepStrictEqual(status!.mustHavesMentionedInSummary, 0, "mh-noplan: mustHavesMentionedInSummary is 0");
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
console.log("\n=== must-haves: present but no summary file ===");
|
|
@@ -209,10 +209,10 @@ console.log("\n=== must-haves: present but no summary file ===");
|
|
|
209
209
|
|
|
210
210
|
clearPathCache();
|
|
211
211
|
const status = await inspectExecuteTaskDurability(mhBase, "M200/S04/T01");
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
212
|
+
assert.ok(status !== null, "mh-nosummary: status exists");
|
|
213
|
+
assert.deepStrictEqual(status!.mustHaveCount, 2, "mh-nosummary: mustHaveCount is 2");
|
|
214
|
+
assert.deepStrictEqual(status!.mustHavesMentionedInSummary, 0, "mh-nosummary: mustHavesMentionedInSummary is 0 with no summary");
|
|
215
|
+
assert.deepStrictEqual(status!.summaryExists, false, "mh-nosummary: summary doesn't exist");
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
console.log("\n=== must-haves: substring matching (no backtick tokens) ===");
|
|
@@ -241,18 +241,17 @@ console.log("\n=== must-haves: substring matching (no backtick tokens) ===");
|
|
|
241
241
|
|
|
242
242
|
clearPathCache();
|
|
243
243
|
const status = await inspectExecuteTaskDurability(mhBase, "M200/S05/T01");
|
|
244
|
-
|
|
245
|
-
|
|
244
|
+
assert.ok(status !== null, "mh-substr: status exists");
|
|
245
|
+
assert.deepStrictEqual(status!.mustHaveCount, 3, "mh-substr: mustHaveCount is 3");
|
|
246
246
|
// "heuristic" appears in summary for item 1, "diagnostic" for item 2,
|
|
247
247
|
// "assertions" appears in summary? No — let's check
|
|
248
248
|
// Item 3: "All assertions pass" — words: "assertions", "pass" (<4 chars excluded)
|
|
249
249
|
// summary doesn't contain "assertions" → not matched
|
|
250
|
-
|
|
250
|
+
assert.deepStrictEqual(status!.mustHavesMentionedInSummary, 2, "mh-substr: 2 of 3 matched via substring");
|
|
251
251
|
const diag = formatExecuteTaskRecoveryStatus(status!);
|
|
252
|
-
|
|
253
|
-
|
|
252
|
+
assert.ok(diag.includes("must-have gap"), "mh-substr: diagnostic includes gap info");
|
|
253
|
+
assert.ok(diag.includes("2 of 3"), "mh-substr: diagnostic includes '2 of 3'");
|
|
254
254
|
}
|
|
255
255
|
|
|
256
256
|
rmSync(mhBase, { recursive: true, force: true });
|
|
257
257
|
rmSync(base, { recursive: true, force: true });
|
|
258
|
-
report();
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
|
|
4
4
|
import { computeCriticalPath } from "../visualizer-data.js";
|
|
5
5
|
import type { VisualizerMilestone } from "../visualizer-data.js";
|
|
6
|
-
import {
|
|
6
|
+
import { test } from 'node:test';
|
|
7
|
+
import assert from 'node:assert/strict';
|
|
7
8
|
|
|
8
|
-
const { assertEq, assertTrue, report } = createTestContext();
|
|
9
9
|
|
|
10
10
|
function makeMs(id: string, status: "complete" | "active" | "pending", dependsOn: string[], slices: any[] = []): VisualizerMilestone {
|
|
11
11
|
return { id, title: id, status, dependsOn, slices };
|
|
@@ -31,11 +31,11 @@ console.log("\n=== Critical Path: Linear Chain ===");
|
|
|
31
31
|
];
|
|
32
32
|
|
|
33
33
|
const cp = computeCriticalPath(milestones);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
assert.ok(cp.milestonePath.length > 0, "linear chain has critical path");
|
|
35
|
+
assert.ok(cp.milestonePath.includes("M002"), "M002 is on critical path");
|
|
36
|
+
assert.ok(cp.milestonePath.includes("M003"), "M003 is on critical path");
|
|
37
|
+
assert.deepStrictEqual(cp.milestoneSlack.get("M002"), 0, "M002 has zero slack");
|
|
38
|
+
assert.deepStrictEqual(cp.milestoneSlack.get("M003"), 0, "M003 has zero slack");
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
// ─── Diamond DAG ────────────────────────────────────────────────────────────
|
|
@@ -60,14 +60,14 @@ console.log("\n=== Critical Path: Diamond DAG ===");
|
|
|
60
60
|
];
|
|
61
61
|
|
|
62
62
|
const cp = computeCriticalPath(milestones);
|
|
63
|
-
|
|
63
|
+
assert.ok(cp.milestonePath.length >= 2, "diamond DAG has critical path");
|
|
64
64
|
// M002 has weight 3 (3 incomplete), M003 has weight 1
|
|
65
65
|
// Critical path should go through M002 (longer)
|
|
66
|
-
|
|
66
|
+
assert.ok(cp.milestonePath.includes("M002"), "M002 (heavier) is on critical path");
|
|
67
67
|
|
|
68
68
|
// M003 should have non-zero slack since it's lighter
|
|
69
69
|
const m003Slack = cp.milestoneSlack.get("M003") ?? -1;
|
|
70
|
-
|
|
70
|
+
assert.ok(m003Slack > 0, "M003 has positive slack (lighter branch)");
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
// ─── Independent branches ───────────────────────────────────────────────────
|
|
@@ -83,9 +83,9 @@ console.log("\n=== Critical Path: Independent Branches ===");
|
|
|
83
83
|
];
|
|
84
84
|
|
|
85
85
|
const cp = computeCriticalPath(milestones);
|
|
86
|
-
|
|
86
|
+
assert.ok(cp.milestonePath.length >= 1, "independent branches have at least one critical node");
|
|
87
87
|
// M002 has the most incomplete slices, should be critical
|
|
88
|
-
|
|
88
|
+
assert.ok(cp.milestonePath.includes("M002"), "M002 (longest) is on critical path");
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
// ─── Slice-level critical path ──────────────────────────────────────────────
|
|
@@ -104,13 +104,13 @@ console.log("\n=== Critical Path: Slice-level ===");
|
|
|
104
104
|
];
|
|
105
105
|
|
|
106
106
|
const cp = computeCriticalPath(milestones);
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
assert.ok(cp.slicePath.length > 0, "has slice-level critical path");
|
|
108
|
+
assert.ok(cp.slicePath.includes("S02"), "S02 is on slice critical path");
|
|
109
|
+
assert.ok(cp.slicePath.includes("S04"), "S04 is on slice critical path");
|
|
110
110
|
|
|
111
111
|
// S03 should have non-zero slack (it's a shorter branch)
|
|
112
112
|
const s03Slack = cp.sliceSlack.get("S03") ?? -1;
|
|
113
|
-
|
|
113
|
+
assert.ok(s03Slack > 0, "S03 has positive slack (shorter branch)");
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
// ─── Empty milestones ───────────────────────────────────────────────────────
|
|
@@ -119,8 +119,8 @@ console.log("\n=== Critical Path: Empty ===");
|
|
|
119
119
|
|
|
120
120
|
{
|
|
121
121
|
const cp = computeCriticalPath([]);
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
assert.deepStrictEqual(cp.milestonePath.length, 0, "empty milestones produce empty path");
|
|
123
|
+
assert.deepStrictEqual(cp.slicePath.length, 0, "empty milestones produce empty slice path");
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
// ─── Single milestone ───────────────────────────────────────────────────────
|
|
@@ -136,10 +136,8 @@ console.log("\n=== Critical Path: Single Milestone ===");
|
|
|
136
136
|
];
|
|
137
137
|
|
|
138
138
|
const cp = computeCriticalPath(milestones);
|
|
139
|
-
|
|
140
|
-
|
|
139
|
+
assert.ok(cp.milestonePath.length === 1, "single milestone is its own critical path");
|
|
140
|
+
assert.deepStrictEqual(cp.milestonePath[0], "M001", "M001 is the critical node");
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
// ─── Report ─────────────────────────────────────────────────────────────────
|
|
144
|
-
|
|
145
|
-
report();
|