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
|
@@ -39,444 +39,400 @@ function cleanup(base: string): void {
|
|
|
39
39
|
|
|
40
40
|
// ─── resolveExpectedArtifactPath ──────────────────────────────────────────
|
|
41
41
|
|
|
42
|
-
test("resolveExpectedArtifactPath returns correct path for research-milestone", () => {
|
|
42
|
+
test("resolveExpectedArtifactPath returns correct path for research-milestone", (t) => {
|
|
43
43
|
const base = makeTmpBase();
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
cleanup(base);
|
|
51
|
-
}
|
|
44
|
+
t.after(() => cleanup(base));
|
|
45
|
+
|
|
46
|
+
const result = resolveExpectedArtifactPath("research-milestone", "M001", base);
|
|
47
|
+
assert.ok(result);
|
|
48
|
+
assert.ok(result!.includes("M001"));
|
|
49
|
+
assert.ok(result!.includes("RESEARCH"));
|
|
52
50
|
});
|
|
53
51
|
|
|
54
|
-
test("resolveExpectedArtifactPath returns correct path for execute-task", () => {
|
|
52
|
+
test("resolveExpectedArtifactPath returns correct path for execute-task", (t) => {
|
|
55
53
|
const base = makeTmpBase();
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
cleanup(base);
|
|
63
|
-
}
|
|
54
|
+
t.after(() => cleanup(base));
|
|
55
|
+
|
|
56
|
+
const result = resolveExpectedArtifactPath("execute-task", "M001/S01/T01", base);
|
|
57
|
+
assert.ok(result);
|
|
58
|
+
assert.ok(result!.includes("tasks"));
|
|
59
|
+
assert.ok(result!.includes("SUMMARY"));
|
|
64
60
|
});
|
|
65
61
|
|
|
66
|
-
test("resolveExpectedArtifactPath returns correct path for complete-slice", () => {
|
|
62
|
+
test("resolveExpectedArtifactPath returns correct path for complete-slice", (t) => {
|
|
67
63
|
const base = makeTmpBase();
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
cleanup(base);
|
|
74
|
-
}
|
|
64
|
+
t.after(() => cleanup(base));
|
|
65
|
+
|
|
66
|
+
const result = resolveExpectedArtifactPath("complete-slice", "M001/S01", base);
|
|
67
|
+
assert.ok(result);
|
|
68
|
+
assert.ok(result!.includes("SUMMARY"));
|
|
75
69
|
});
|
|
76
70
|
|
|
77
|
-
test("resolveExpectedArtifactPath returns correct path for plan-slice", () => {
|
|
71
|
+
test("resolveExpectedArtifactPath returns correct path for plan-slice", (t) => {
|
|
78
72
|
const base = makeTmpBase();
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
cleanup(base);
|
|
85
|
-
}
|
|
73
|
+
t.after(() => cleanup(base));
|
|
74
|
+
|
|
75
|
+
const result = resolveExpectedArtifactPath("plan-slice", "M001/S01", base);
|
|
76
|
+
assert.ok(result);
|
|
77
|
+
assert.ok(result!.includes("PLAN"));
|
|
86
78
|
});
|
|
87
79
|
|
|
88
|
-
test("resolveExpectedArtifactPath returns null for unknown type", () => {
|
|
80
|
+
test("resolveExpectedArtifactPath returns null for unknown type", (t) => {
|
|
89
81
|
const base = makeTmpBase();
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
cleanup(base);
|
|
95
|
-
}
|
|
82
|
+
t.after(() => cleanup(base));
|
|
83
|
+
|
|
84
|
+
const result = resolveExpectedArtifactPath("unknown-type", "M001", base);
|
|
85
|
+
assert.equal(result, null);
|
|
96
86
|
});
|
|
97
87
|
|
|
98
|
-
test("resolveExpectedArtifactPath returns correct path for all milestone-level types", () => {
|
|
88
|
+
test("resolveExpectedArtifactPath returns correct path for all milestone-level types", (t) => {
|
|
99
89
|
const base = makeTmpBase();
|
|
100
|
-
|
|
101
|
-
const planResult = resolveExpectedArtifactPath("plan-milestone", "M001", base);
|
|
102
|
-
assert.ok(planResult);
|
|
103
|
-
assert.ok(planResult!.includes("ROADMAP"));
|
|
90
|
+
t.after(() => cleanup(base));
|
|
104
91
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
92
|
+
const planResult = resolveExpectedArtifactPath("plan-milestone", "M001", base);
|
|
93
|
+
assert.ok(planResult);
|
|
94
|
+
assert.ok(planResult!.includes("ROADMAP"));
|
|
95
|
+
|
|
96
|
+
const completeResult = resolveExpectedArtifactPath("complete-milestone", "M001", base);
|
|
97
|
+
assert.ok(completeResult);
|
|
98
|
+
assert.ok(completeResult!.includes("SUMMARY"));
|
|
111
99
|
});
|
|
112
100
|
|
|
113
|
-
test("resolveExpectedArtifactPath returns correct path for all slice-level types", () => {
|
|
101
|
+
test("resolveExpectedArtifactPath returns correct path for all slice-level types", (t) => {
|
|
114
102
|
const base = makeTmpBase();
|
|
115
|
-
|
|
116
|
-
const researchResult = resolveExpectedArtifactPath("research-slice", "M001/S01", base);
|
|
117
|
-
assert.ok(researchResult);
|
|
118
|
-
assert.ok(researchResult!.includes("RESEARCH"));
|
|
103
|
+
t.after(() => cleanup(base));
|
|
119
104
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
105
|
+
const researchResult = resolveExpectedArtifactPath("research-slice", "M001/S01", base);
|
|
106
|
+
assert.ok(researchResult);
|
|
107
|
+
assert.ok(researchResult!.includes("RESEARCH"));
|
|
123
108
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
109
|
+
const assessResult = resolveExpectedArtifactPath("reassess-roadmap", "M001/S01", base);
|
|
110
|
+
assert.ok(assessResult);
|
|
111
|
+
assert.ok(assessResult!.includes("ASSESSMENT"));
|
|
112
|
+
|
|
113
|
+
const uatResult = resolveExpectedArtifactPath("run-uat", "M001/S01", base);
|
|
114
|
+
assert.ok(uatResult);
|
|
115
|
+
assert.ok(uatResult!.includes("UAT-RESULT"));
|
|
130
116
|
});
|
|
131
117
|
|
|
132
118
|
// ─── diagnoseExpectedArtifact ─────────────────────────────────────────────
|
|
133
119
|
|
|
134
|
-
test("diagnoseExpectedArtifact returns description for known types", () => {
|
|
120
|
+
test("diagnoseExpectedArtifact returns description for known types", (t) => {
|
|
135
121
|
const base = makeTmpBase();
|
|
136
|
-
|
|
137
|
-
const research = diagnoseExpectedArtifact("research-milestone", "M001", base);
|
|
138
|
-
assert.ok(research);
|
|
139
|
-
assert.ok(research!.includes("research"));
|
|
122
|
+
t.after(() => cleanup(base));
|
|
140
123
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
124
|
+
const research = diagnoseExpectedArtifact("research-milestone", "M001", base);
|
|
125
|
+
assert.ok(research);
|
|
126
|
+
assert.ok(research!.includes("research"));
|
|
144
127
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
128
|
+
const plan = diagnoseExpectedArtifact("plan-slice", "M001/S01", base);
|
|
129
|
+
assert.ok(plan);
|
|
130
|
+
assert.ok(plan!.includes("plan"));
|
|
131
|
+
|
|
132
|
+
const task = diagnoseExpectedArtifact("execute-task", "M001/S01/T01", base);
|
|
133
|
+
assert.ok(task);
|
|
134
|
+
assert.ok(task!.includes("T01"));
|
|
151
135
|
});
|
|
152
136
|
|
|
153
|
-
test("diagnoseExpectedArtifact returns null for unknown type", () => {
|
|
137
|
+
test("diagnoseExpectedArtifact returns null for unknown type", (t) => {
|
|
154
138
|
const base = makeTmpBase();
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
cleanup(base);
|
|
159
|
-
}
|
|
139
|
+
t.after(() => cleanup(base));
|
|
140
|
+
|
|
141
|
+
assert.equal(diagnoseExpectedArtifact("unknown", "M001", base), null);
|
|
160
142
|
});
|
|
161
143
|
|
|
162
144
|
// ─── buildLoopRemediationSteps ────────────────────────────────────────────
|
|
163
145
|
|
|
164
|
-
test("buildLoopRemediationSteps returns steps for execute-task", () => {
|
|
146
|
+
test("buildLoopRemediationSteps returns steps for execute-task", (t) => {
|
|
165
147
|
const base = makeTmpBase();
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
cleanup(base);
|
|
173
|
-
}
|
|
148
|
+
t.after(() => cleanup(base));
|
|
149
|
+
|
|
150
|
+
const steps = buildLoopRemediationSteps("execute-task", "M001/S01/T01", base);
|
|
151
|
+
assert.ok(steps);
|
|
152
|
+
assert.ok(steps!.includes("T01"));
|
|
153
|
+
assert.ok(steps!.includes("gsd undo-task"));
|
|
174
154
|
});
|
|
175
155
|
|
|
176
|
-
test("buildLoopRemediationSteps returns steps for plan-slice", () => {
|
|
156
|
+
test("buildLoopRemediationSteps returns steps for plan-slice", (t) => {
|
|
177
157
|
const base = makeTmpBase();
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
cleanup(base);
|
|
185
|
-
}
|
|
158
|
+
t.after(() => cleanup(base));
|
|
159
|
+
|
|
160
|
+
const steps = buildLoopRemediationSteps("plan-slice", "M001/S01", base);
|
|
161
|
+
assert.ok(steps);
|
|
162
|
+
assert.ok(steps!.includes("PLAN"));
|
|
163
|
+
assert.ok(steps!.includes("gsd recover"));
|
|
186
164
|
});
|
|
187
165
|
|
|
188
|
-
test("buildLoopRemediationSteps returns steps for complete-slice", () => {
|
|
166
|
+
test("buildLoopRemediationSteps returns steps for complete-slice", (t) => {
|
|
189
167
|
const base = makeTmpBase();
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
cleanup(base);
|
|
197
|
-
}
|
|
168
|
+
t.after(() => cleanup(base));
|
|
169
|
+
|
|
170
|
+
const steps = buildLoopRemediationSteps("complete-slice", "M001/S01", base);
|
|
171
|
+
assert.ok(steps);
|
|
172
|
+
assert.ok(steps!.includes("S01"));
|
|
173
|
+
assert.ok(steps!.includes("gsd reset-slice"));
|
|
198
174
|
});
|
|
199
175
|
|
|
200
|
-
test("buildLoopRemediationSteps returns null for unknown type", () => {
|
|
176
|
+
test("buildLoopRemediationSteps returns null for unknown type", (t) => {
|
|
201
177
|
const base = makeTmpBase();
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
cleanup(base);
|
|
206
|
-
}
|
|
178
|
+
t.after(() => cleanup(base));
|
|
179
|
+
|
|
180
|
+
assert.equal(buildLoopRemediationSteps("unknown", "M001", base), null);
|
|
207
181
|
});
|
|
208
182
|
|
|
209
183
|
// ─── verifyExpectedArtifact: parse cache collision regression ─────────────
|
|
210
184
|
|
|
211
|
-
test("verifyExpectedArtifact detects roadmap [x] change despite parse cache", () => {
|
|
185
|
+
test("verifyExpectedArtifact detects roadmap [x] change despite parse cache", (t) => {
|
|
212
186
|
// Regression test: cacheKey collision when [ ] → [x] doesn't change
|
|
213
187
|
// file length or first/last 100 chars. Without the fix, parseRoadmap
|
|
214
188
|
// returns stale cached data with done=false even though the file has [x].
|
|
215
189
|
const base = makeTmpBase();
|
|
216
|
-
|
|
217
|
-
// Build a roadmap long enough that the [x] change is outside the first/last 100 chars
|
|
218
|
-
const padding = "A".repeat(200);
|
|
219
|
-
const roadmapBefore = [
|
|
220
|
-
`# M001: Test Milestone ${padding}`,
|
|
221
|
-
"",
|
|
222
|
-
"## Slices",
|
|
223
|
-
"",
|
|
224
|
-
"- [ ] **S01: First slice** `risk:low`",
|
|
225
|
-
"",
|
|
226
|
-
`## Footer ${padding}`,
|
|
227
|
-
].join("\n");
|
|
228
|
-
const roadmapAfter = roadmapBefore.replace("- [ ] **S01:", "- [x] **S01:");
|
|
229
|
-
|
|
230
|
-
// Verify lengths are identical (the key collision condition)
|
|
231
|
-
assert.equal(roadmapBefore.length, roadmapAfter.length);
|
|
232
|
-
|
|
233
|
-
// Populate parse cache with the pre-edit roadmap
|
|
234
|
-
const before = parseRoadmap(roadmapBefore);
|
|
235
|
-
const sliceBefore = before.slices.find(s => s.id === "S01");
|
|
236
|
-
assert.ok(sliceBefore);
|
|
237
|
-
assert.equal(sliceBefore!.done, false);
|
|
238
|
-
|
|
239
|
-
// Now write the post-edit roadmap to disk and create required artifacts
|
|
240
|
-
const roadmapPath = join(base, ".gsd", "milestones", "M001", "M001-ROADMAP.md");
|
|
241
|
-
writeFileSync(roadmapPath, roadmapAfter);
|
|
242
|
-
const summaryPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-SUMMARY.md");
|
|
243
|
-
writeFileSync(summaryPath, "# Summary\nDone.");
|
|
244
|
-
const uatPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-UAT.md");
|
|
245
|
-
writeFileSync(uatPath, "# UAT\nPassed.");
|
|
246
|
-
|
|
247
|
-
// verifyExpectedArtifact should see the [x] despite the parse cache
|
|
248
|
-
// having the [ ] version. The fix clears the parse cache inside verify.
|
|
249
|
-
const verified = verifyExpectedArtifact("complete-slice", "M001/S01", base);
|
|
250
|
-
assert.equal(verified, true, "verifyExpectedArtifact should return true when roadmap has [x]");
|
|
251
|
-
} finally {
|
|
190
|
+
t.after(() => {
|
|
252
191
|
clearParseCache();
|
|
253
192
|
cleanup(base);
|
|
254
|
-
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Build a roadmap long enough that the [x] change is outside the first/last 100 chars
|
|
196
|
+
const padding = "A".repeat(200);
|
|
197
|
+
const roadmapBefore = [
|
|
198
|
+
`# M001: Test Milestone ${padding}`,
|
|
199
|
+
"",
|
|
200
|
+
"## Slices",
|
|
201
|
+
"",
|
|
202
|
+
"- [ ] **S01: First slice** `risk:low`",
|
|
203
|
+
"",
|
|
204
|
+
`## Footer ${padding}`,
|
|
205
|
+
].join("\n");
|
|
206
|
+
const roadmapAfter = roadmapBefore.replace("- [ ] **S01:", "- [x] **S01:");
|
|
207
|
+
|
|
208
|
+
// Verify lengths are identical (the key collision condition)
|
|
209
|
+
assert.equal(roadmapBefore.length, roadmapAfter.length);
|
|
210
|
+
|
|
211
|
+
// Populate parse cache with the pre-edit roadmap
|
|
212
|
+
const before = parseRoadmap(roadmapBefore);
|
|
213
|
+
const sliceBefore = before.slices.find(s => s.id === "S01");
|
|
214
|
+
assert.ok(sliceBefore);
|
|
215
|
+
assert.equal(sliceBefore!.done, false);
|
|
216
|
+
|
|
217
|
+
// Now write the post-edit roadmap to disk and create required artifacts
|
|
218
|
+
const roadmapPath = join(base, ".gsd", "milestones", "M001", "M001-ROADMAP.md");
|
|
219
|
+
writeFileSync(roadmapPath, roadmapAfter);
|
|
220
|
+
const summaryPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-SUMMARY.md");
|
|
221
|
+
writeFileSync(summaryPath, "# Summary\nDone.");
|
|
222
|
+
const uatPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-UAT.md");
|
|
223
|
+
writeFileSync(uatPath, "# UAT\nPassed.");
|
|
224
|
+
|
|
225
|
+
// verifyExpectedArtifact should see the [x] despite the parse cache
|
|
226
|
+
// having the [ ] version. The fix clears the parse cache inside verify.
|
|
227
|
+
const verified = verifyExpectedArtifact("complete-slice", "M001/S01", base);
|
|
228
|
+
assert.equal(verified, true, "verifyExpectedArtifact should return true when roadmap has [x]");
|
|
255
229
|
});
|
|
256
230
|
|
|
257
231
|
// ─── verifyExpectedArtifact: plan-slice empty scaffold regression (#699) ──
|
|
258
232
|
|
|
259
|
-
test("verifyExpectedArtifact rejects plan-slice with empty scaffold", () => {
|
|
233
|
+
test("verifyExpectedArtifact rejects plan-slice with empty scaffold", (t) => {
|
|
260
234
|
const base = makeTmpBase();
|
|
261
|
-
|
|
262
|
-
const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
|
|
263
|
-
mkdirSync(sliceDir, { recursive: true });
|
|
264
|
-
writeFileSync(join(sliceDir, "S01-PLAN.md"), "# S01: Test Slice\n\n## Tasks\n\n");
|
|
265
|
-
assert.strictEqual(
|
|
266
|
-
verifyExpectedArtifact("plan-slice", "M001/S01", base),
|
|
267
|
-
false,
|
|
268
|
-
"Empty scaffold should not be treated as completed artifact",
|
|
269
|
-
);
|
|
270
|
-
} finally {
|
|
271
|
-
cleanup(base);
|
|
272
|
-
}
|
|
273
|
-
});
|
|
235
|
+
t.after(() => cleanup(base));
|
|
274
236
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
"",
|
|
284
|
-
"## Tasks",
|
|
285
|
-
"",
|
|
286
|
-
"- [ ] **T01: Implement feature** `est:2h`",
|
|
287
|
-
"- [ ] **T02: Write tests** `est:1h`",
|
|
288
|
-
].join("\n"));
|
|
289
|
-
writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan");
|
|
290
|
-
writeFileSync(join(tasksDir, "T02-PLAN.md"), "# T02 Plan");
|
|
291
|
-
assert.strictEqual(
|
|
292
|
-
verifyExpectedArtifact("plan-slice", "M001/S01", base),
|
|
293
|
-
true,
|
|
294
|
-
"Plan with task entries should be treated as completed artifact",
|
|
295
|
-
);
|
|
296
|
-
} finally {
|
|
297
|
-
cleanup(base);
|
|
298
|
-
}
|
|
237
|
+
const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
|
|
238
|
+
mkdirSync(sliceDir, { recursive: true });
|
|
239
|
+
writeFileSync(join(sliceDir, "S01-PLAN.md"), "# S01: Test Slice\n\n## Tasks\n\n");
|
|
240
|
+
assert.strictEqual(
|
|
241
|
+
verifyExpectedArtifact("plan-slice", "M001/S01", base),
|
|
242
|
+
false,
|
|
243
|
+
"Empty scaffold should not be treated as completed artifact",
|
|
244
|
+
);
|
|
299
245
|
});
|
|
300
246
|
|
|
301
|
-
test("verifyExpectedArtifact accepts plan-slice with
|
|
247
|
+
test("verifyExpectedArtifact accepts plan-slice with actual tasks", (t) => {
|
|
302
248
|
const base = makeTmpBase();
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
]
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
249
|
+
t.after(() => cleanup(base));
|
|
250
|
+
|
|
251
|
+
const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
|
|
252
|
+
const tasksDir = join(sliceDir, "tasks");
|
|
253
|
+
mkdirSync(tasksDir, { recursive: true });
|
|
254
|
+
writeFileSync(join(sliceDir, "S01-PLAN.md"), [
|
|
255
|
+
"# S01: Test Slice",
|
|
256
|
+
"",
|
|
257
|
+
"## Tasks",
|
|
258
|
+
"",
|
|
259
|
+
"- [ ] **T01: Implement feature** `est:2h`",
|
|
260
|
+
"- [ ] **T02: Write tests** `est:1h`",
|
|
261
|
+
].join("\n"));
|
|
262
|
+
writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan");
|
|
263
|
+
writeFileSync(join(tasksDir, "T02-PLAN.md"), "# T02 Plan");
|
|
264
|
+
assert.strictEqual(
|
|
265
|
+
verifyExpectedArtifact("plan-slice", "M001/S01", base),
|
|
266
|
+
true,
|
|
267
|
+
"Plan with task entries should be treated as completed artifact",
|
|
268
|
+
);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test("verifyExpectedArtifact accepts plan-slice with completed tasks", (t) => {
|
|
272
|
+
const base = makeTmpBase();
|
|
273
|
+
t.after(() => cleanup(base));
|
|
274
|
+
|
|
275
|
+
const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
|
|
276
|
+
const tasksDir = join(sliceDir, "tasks");
|
|
277
|
+
mkdirSync(tasksDir, { recursive: true });
|
|
278
|
+
writeFileSync(join(sliceDir, "S01-PLAN.md"), [
|
|
279
|
+
"# S01: Test Slice",
|
|
280
|
+
"",
|
|
281
|
+
"## Tasks",
|
|
282
|
+
"",
|
|
283
|
+
"- [x] **T01: Implement feature** `est:2h`",
|
|
284
|
+
"- [ ] **T02: Write tests** `est:1h`",
|
|
285
|
+
].join("\n"));
|
|
286
|
+
writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan");
|
|
287
|
+
writeFileSync(join(tasksDir, "T02-PLAN.md"), "# T02 Plan");
|
|
288
|
+
assert.strictEqual(
|
|
289
|
+
verifyExpectedArtifact("plan-slice", "M001/S01", base),
|
|
290
|
+
true,
|
|
291
|
+
"Plan with completed task entries should be treated as completed artifact",
|
|
292
|
+
);
|
|
325
293
|
});
|
|
326
294
|
|
|
327
295
|
// ─── verifyExpectedArtifact: plan-slice task plan check (#739) ────────────
|
|
328
296
|
|
|
329
|
-
test("verifyExpectedArtifact plan-slice passes when all task plan files exist", () => {
|
|
297
|
+
test("verifyExpectedArtifact plan-slice passes when all task plan files exist", (t) => {
|
|
330
298
|
const base = makeTmpBase();
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
]
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
test("verifyExpectedArtifact plan-slice fails when a task plan file is missing (#739)", () => {
|
|
299
|
+
t.after(() => cleanup(base));
|
|
300
|
+
|
|
301
|
+
const tasksDir = join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks");
|
|
302
|
+
const planPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-PLAN.md");
|
|
303
|
+
const planContent = [
|
|
304
|
+
"# S01: Test Slice",
|
|
305
|
+
"",
|
|
306
|
+
"## Tasks",
|
|
307
|
+
"",
|
|
308
|
+
"- [ ] **T01: First task** `est:1h`",
|
|
309
|
+
"- [ ] **T02: Second task** `est:2h`",
|
|
310
|
+
].join("\n");
|
|
311
|
+
writeFileSync(planPath, planContent);
|
|
312
|
+
writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan\n\nDo the thing.");
|
|
313
|
+
writeFileSync(join(tasksDir, "T02-PLAN.md"), "# T02 Plan\n\nDo the other thing.");
|
|
314
|
+
|
|
315
|
+
const result = verifyExpectedArtifact("plan-slice", "M001/S01", base);
|
|
316
|
+
assert.equal(result, true, "should pass when all task plan files exist");
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
test("verifyExpectedArtifact plan-slice fails when a task plan file is missing (#739)", (t) => {
|
|
354
320
|
const base = makeTmpBase();
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
]
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
test("verifyExpectedArtifact plan-slice fails for plan with no tasks (#699)", () => {
|
|
321
|
+
t.after(() => cleanup(base));
|
|
322
|
+
|
|
323
|
+
const tasksDir = join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks");
|
|
324
|
+
const planPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-PLAN.md");
|
|
325
|
+
const planContent = [
|
|
326
|
+
"# S01: Test Slice",
|
|
327
|
+
"",
|
|
328
|
+
"## Tasks",
|
|
329
|
+
"",
|
|
330
|
+
"- [ ] **T01: First task** `est:1h`",
|
|
331
|
+
"- [ ] **T02: Second task** `est:2h`",
|
|
332
|
+
].join("\n");
|
|
333
|
+
writeFileSync(planPath, planContent);
|
|
334
|
+
// Only write T01-PLAN.md — T02 is missing
|
|
335
|
+
writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan\n\nDo the thing.");
|
|
336
|
+
|
|
337
|
+
const result = verifyExpectedArtifact("plan-slice", "M001/S01", base);
|
|
338
|
+
assert.equal(result, false, "should fail when T02-PLAN.md is missing");
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
test("verifyExpectedArtifact plan-slice fails for plan with no tasks (#699)", (t) => {
|
|
378
342
|
const base = makeTmpBase();
|
|
379
|
-
|
|
380
|
-
const planPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-PLAN.md");
|
|
381
|
-
const planContent = [
|
|
382
|
-
"# S01: Test Slice",
|
|
383
|
-
"",
|
|
384
|
-
"## Goal",
|
|
385
|
-
"",
|
|
386
|
-
"Just some documentation updates, no tasks.",
|
|
387
|
-
].join("\n");
|
|
388
|
-
writeFileSync(planPath, planContent);
|
|
343
|
+
t.after(() => cleanup(base));
|
|
389
344
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
345
|
+
const planPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-PLAN.md");
|
|
346
|
+
const planContent = [
|
|
347
|
+
"# S01: Test Slice",
|
|
348
|
+
"",
|
|
349
|
+
"## Goal",
|
|
350
|
+
"",
|
|
351
|
+
"Just some documentation updates, no tasks.",
|
|
352
|
+
].join("\n");
|
|
353
|
+
writeFileSync(planPath, planContent);
|
|
354
|
+
|
|
355
|
+
const result = verifyExpectedArtifact("plan-slice", "M001/S01", base);
|
|
356
|
+
assert.equal(result, false, "should fail when plan has no task entries (empty scaffold, #699)");
|
|
395
357
|
});
|
|
396
358
|
|
|
397
359
|
// ─── verifyExpectedArtifact: heading-style plan tasks (#1691) ─────────────
|
|
398
360
|
|
|
399
|
-
test("verifyExpectedArtifact accepts plan-slice with heading-style tasks (### T01 --)", () => {
|
|
361
|
+
test("verifyExpectedArtifact accepts plan-slice with heading-style tasks (### T01 --)", (t) => {
|
|
400
362
|
const base = makeTmpBase();
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
test("verifyExpectedArtifact accepts plan-slice with colon-style heading tasks (### T01:)", () => {
|
|
363
|
+
t.after(() => cleanup(base));
|
|
364
|
+
|
|
365
|
+
const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
|
|
366
|
+
const tasksDir = join(sliceDir, "tasks");
|
|
367
|
+
mkdirSync(tasksDir, { recursive: true });
|
|
368
|
+
writeFileSync(join(sliceDir, "S01-PLAN.md"), [
|
|
369
|
+
"# S01: Test Slice",
|
|
370
|
+
"",
|
|
371
|
+
"## Tasks",
|
|
372
|
+
"",
|
|
373
|
+
"### T01 -- Implement feature",
|
|
374
|
+
"",
|
|
375
|
+
"Feature description.",
|
|
376
|
+
"",
|
|
377
|
+
"### T02 -- Write tests",
|
|
378
|
+
"",
|
|
379
|
+
"Test description.",
|
|
380
|
+
].join("\n"));
|
|
381
|
+
writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan");
|
|
382
|
+
writeFileSync(join(tasksDir, "T02-PLAN.md"), "# T02 Plan");
|
|
383
|
+
assert.strictEqual(
|
|
384
|
+
verifyExpectedArtifact("plan-slice", "M001/S01", base),
|
|
385
|
+
true,
|
|
386
|
+
"Heading-style plan with task entries should be treated as completed artifact",
|
|
387
|
+
);
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
test("verifyExpectedArtifact accepts plan-slice with colon-style heading tasks (### T01:)", (t) => {
|
|
431
391
|
const base = makeTmpBase();
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
test("verifyExpectedArtifact execute-task passes for heading-style plan entry (#1691)", () => {
|
|
392
|
+
t.after(() => cleanup(base));
|
|
393
|
+
|
|
394
|
+
const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
|
|
395
|
+
const tasksDir = join(sliceDir, "tasks");
|
|
396
|
+
mkdirSync(tasksDir, { recursive: true });
|
|
397
|
+
writeFileSync(join(sliceDir, "S01-PLAN.md"), [
|
|
398
|
+
"# S01: Test Slice",
|
|
399
|
+
"",
|
|
400
|
+
"## Tasks",
|
|
401
|
+
"",
|
|
402
|
+
"### T01: Implement feature",
|
|
403
|
+
"",
|
|
404
|
+
"Feature description.",
|
|
405
|
+
].join("\n"));
|
|
406
|
+
writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan");
|
|
407
|
+
assert.strictEqual(
|
|
408
|
+
verifyExpectedArtifact("plan-slice", "M001/S01", base),
|
|
409
|
+
true,
|
|
410
|
+
"Colon heading-style plan should be treated as completed artifact",
|
|
411
|
+
);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
test("verifyExpectedArtifact execute-task passes for heading-style plan entry (#1691)", (t) => {
|
|
457
415
|
const base = makeTmpBase();
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
cleanup(base);
|
|
479
|
-
}
|
|
416
|
+
t.after(() => cleanup(base));
|
|
417
|
+
|
|
418
|
+
const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
|
|
419
|
+
const tasksDir = join(sliceDir, "tasks");
|
|
420
|
+
mkdirSync(tasksDir, { recursive: true });
|
|
421
|
+
writeFileSync(join(sliceDir, "S01-PLAN.md"), [
|
|
422
|
+
"# S01: Test Slice",
|
|
423
|
+
"",
|
|
424
|
+
"## Tasks",
|
|
425
|
+
"",
|
|
426
|
+
"### T01 -- Implement feature",
|
|
427
|
+
"",
|
|
428
|
+
"Feature description.",
|
|
429
|
+
].join("\n"));
|
|
430
|
+
writeFileSync(join(tasksDir, "T01-SUMMARY.md"), "# T01 Summary\n\nDone.");
|
|
431
|
+
assert.strictEqual(
|
|
432
|
+
verifyExpectedArtifact("execute-task", "M001/S01/T01", base),
|
|
433
|
+
true,
|
|
434
|
+
"execute-task should pass for heading-style plan entry when summary exists",
|
|
435
|
+
);
|
|
480
436
|
});
|
|
481
437
|
|
|
482
438
|
test("verifyExpectedArtifact plan-slice passes for rendered slice/task plan artifacts from DB", async () => {
|
|
@@ -618,83 +574,81 @@ test("verifyExpectedArtifact plan-slice fails after deleting a rendered task pla
|
|
|
618
574
|
|
|
619
575
|
// ─── selfHealRuntimeRecords — worktree base path (#769) ──────────────────
|
|
620
576
|
|
|
621
|
-
test("selfHealRuntimeRecords clears stale dispatched records (#769)", async () => {
|
|
577
|
+
test("selfHealRuntimeRecords clears stale dispatched records (#769)", async (t) => {
|
|
622
578
|
// selfHealRuntimeRecords now only clears stale dispatched records (>1h).
|
|
623
579
|
// No completedKeySet parameter — deriveState is sole authority.
|
|
624
580
|
const worktreeBase = makeTmpBase();
|
|
625
581
|
const mainBase = makeTmpBase();
|
|
626
|
-
|
|
627
|
-
|
|
582
|
+
t.after(() => {
|
|
583
|
+
cleanup(worktreeBase);
|
|
584
|
+
cleanup(mainBase);
|
|
585
|
+
});
|
|
628
586
|
|
|
629
|
-
|
|
630
|
-
writeUnitRuntimeRecord(worktreeBase, "run-uat", "M001/S01", Date.now() - 7200_000, {
|
|
631
|
-
phase: "dispatched",
|
|
632
|
-
});
|
|
587
|
+
const { writeUnitRuntimeRecord, readUnitRuntimeRecord } = await import("../unit-runtime.ts");
|
|
633
588
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
589
|
+
// Write a stale runtime record in the worktree .gsd/runtime/units/
|
|
590
|
+
writeUnitRuntimeRecord(worktreeBase, "run-uat", "M001/S01", Date.now() - 7200_000, {
|
|
591
|
+
phase: "dispatched",
|
|
592
|
+
});
|
|
637
593
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
ui: { notify: (msg: string) => { notifications.push(msg); } },
|
|
642
|
-
} as any;
|
|
594
|
+
// Verify the runtime record exists before heal
|
|
595
|
+
const before = readUnitRuntimeRecord(worktreeBase, "run-uat", "M001/S01");
|
|
596
|
+
assert.ok(before, "runtime record should exist before heal");
|
|
643
597
|
|
|
644
|
-
|
|
645
|
-
|
|
598
|
+
// Mock ExtensionContext with minimal notify
|
|
599
|
+
const notifications: string[] = [];
|
|
600
|
+
const mockCtx = {
|
|
601
|
+
ui: { notify: (msg: string) => { notifications.push(msg); } },
|
|
602
|
+
} as any;
|
|
646
603
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
assert.equal(after, null, "runtime record should be cleared after heal");
|
|
650
|
-
assert.ok(notifications.some(n => n.includes("Self-heal")), "should emit self-heal notification");
|
|
604
|
+
// Call selfHeal with worktreeBase — should clear the stale record
|
|
605
|
+
await selfHealRuntimeRecords(worktreeBase, mockCtx);
|
|
651
606
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
await selfHealRuntimeRecords(mainBase, mockCtx);
|
|
607
|
+
// The stale record should be cleared
|
|
608
|
+
const after = readUnitRuntimeRecord(worktreeBase, "run-uat", "M001/S01");
|
|
609
|
+
assert.equal(after, null, "runtime record should be cleared after heal");
|
|
610
|
+
assert.ok(notifications.some(n => n.includes("Self-heal")), "should emit self-heal notification");
|
|
657
611
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
612
|
+
// Write a stale record at mainBase
|
|
613
|
+
writeUnitRuntimeRecord(mainBase, "run-uat", "M001/S01", Date.now() - 7200_000, {
|
|
614
|
+
phase: "dispatched",
|
|
615
|
+
});
|
|
616
|
+
await selfHealRuntimeRecords(mainBase, mockCtx);
|
|
617
|
+
|
|
618
|
+
// The record at mainBase should also be cleared by the stale timeout (>1h)
|
|
619
|
+
const afterMain = readUnitRuntimeRecord(mainBase, "run-uat", "M001/S01");
|
|
620
|
+
assert.equal(afterMain, null, "stale record at main base should be cleared by timeout");
|
|
665
621
|
});
|
|
666
622
|
|
|
667
623
|
// ─── #1625: selfHealRuntimeRecords on resume clears paused-session leftovers ──
|
|
668
624
|
|
|
669
|
-
test("selfHealRuntimeRecords clears recently-paused dispatched records on resume (#1625)", async () => {
|
|
625
|
+
test("selfHealRuntimeRecords clears recently-paused dispatched records on resume (#1625)", async (t) => {
|
|
670
626
|
// When pauseAuto closes out a unit but clearUnitRuntimeRecord silently fails
|
|
671
627
|
// (e.g. permission error), selfHealRuntimeRecords on resume should still
|
|
672
628
|
// clean up stale dispatched records that are >1h old.
|
|
673
629
|
const base = makeTmpBase();
|
|
674
|
-
|
|
675
|
-
const { writeUnitRuntimeRecord, readUnitRuntimeRecord } = await import("../unit-runtime.ts");
|
|
630
|
+
t.after(() => cleanup(base));
|
|
676
631
|
|
|
677
|
-
|
|
678
|
-
writeUnitRuntimeRecord(base, "execute-task", "M001/S01/T01", Date.now() - 3700_000, {
|
|
679
|
-
phase: "dispatched",
|
|
680
|
-
});
|
|
632
|
+
const { writeUnitRuntimeRecord, readUnitRuntimeRecord } = await import("../unit-runtime.ts");
|
|
681
633
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
634
|
+
// Simulate a record left behind after a pause — aged >1h to be considered stale
|
|
635
|
+
writeUnitRuntimeRecord(base, "execute-task", "M001/S01/T01", Date.now() - 3700_000, {
|
|
636
|
+
phase: "dispatched",
|
|
637
|
+
});
|
|
685
638
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
} as any;
|
|
639
|
+
const before = readUnitRuntimeRecord(base, "execute-task", "M001/S01/T01");
|
|
640
|
+
assert.ok(before, "dispatched record should exist before resume heal");
|
|
641
|
+
assert.equal(before!.phase, "dispatched");
|
|
690
642
|
|
|
691
|
-
|
|
643
|
+
const notifications: string[] = [];
|
|
644
|
+
const mockCtx = {
|
|
645
|
+
ui: { notify: (msg: string) => { notifications.push(msg); } },
|
|
646
|
+
} as any;
|
|
692
647
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
}
|
|
648
|
+
await selfHealRuntimeRecords(base, mockCtx);
|
|
649
|
+
|
|
650
|
+
const after = readUnitRuntimeRecord(base, "execute-task", "M001/S01/T01");
|
|
651
|
+
assert.equal(after, null, "stale dispatched record should be cleared on resume (#1625)");
|
|
698
652
|
});
|
|
699
653
|
|
|
700
654
|
// ─── #793: invalidateAllCaches unblocks skip-loop ─────────────────────────
|
|
@@ -702,51 +656,49 @@ test("selfHealRuntimeRecords clears recently-paused dispatched records on resume
|
|
|
702
656
|
// just invalidateStateCache()) to clear path/parse caches that deriveState
|
|
703
657
|
// depends on. Without this, even after cache invalidation, deriveState reads
|
|
704
658
|
// stale directory listings and returns the same unit, looping forever.
|
|
705
|
-
test("#793: invalidateAllCaches clears all caches so deriveState sees fresh disk state", async () => {
|
|
659
|
+
test("#793: invalidateAllCaches clears all caches so deriveState sees fresh disk state", async (t) => {
|
|
706
660
|
const base = makeTmpBase();
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
cleanup(base);
|
|
749
|
-
}
|
|
661
|
+
t.after(() => cleanup(base));
|
|
662
|
+
|
|
663
|
+
const mid = "M001";
|
|
664
|
+
const sid = "S01";
|
|
665
|
+
const planDir = join(base, ".gsd", "milestones", mid, "slices", sid);
|
|
666
|
+
const tasksDir = join(planDir, "tasks");
|
|
667
|
+
mkdirSync(tasksDir, { recursive: true });
|
|
668
|
+
mkdirSync(join(base, ".gsd", "milestones", mid), { recursive: true });
|
|
669
|
+
|
|
670
|
+
writeFileSync(
|
|
671
|
+
join(base, ".gsd", "milestones", mid, `${mid}-ROADMAP.md`),
|
|
672
|
+
`# M001: Test Milestone\n\n**Vision:** test.\n\n## Slices\n\n- [ ] **${sid}: Slice One** \`risk:low\` \`depends:[]\`\n > After this: done.\n`,
|
|
673
|
+
);
|
|
674
|
+
const planUnchecked = `# ${sid}: Slice One\n\n**Goal:** test.\n\n## Tasks\n\n- [ ] **T01: Task One** \`est:10m\`\n- [ ] **T02: Task Two** \`est:10m\`\n`;
|
|
675
|
+
writeFileSync(join(planDir, `${sid}-PLAN.md`), planUnchecked);
|
|
676
|
+
writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01: Task One\n\n**Goal:** t\n\n## Steps\n- step\n\n## Verification\n- v\n");
|
|
677
|
+
writeFileSync(join(tasksDir, "T02-PLAN.md"), "# T02: Task Two\n\n**Goal:** t\n\n## Steps\n- step\n\n## Verification\n- v\n");
|
|
678
|
+
|
|
679
|
+
// Warm all caches
|
|
680
|
+
const state1 = await deriveState(base);
|
|
681
|
+
assert.equal(state1.activeTask?.id, "T01", "initial: T01 is active");
|
|
682
|
+
|
|
683
|
+
// Simulate task completion on disk (what the LLM does)
|
|
684
|
+
const planChecked = `# ${sid}: Slice One\n\n**Goal:** test.\n\n## Tasks\n\n- [x] **T01: Task One** \`est:10m\`\n- [ ] **T02: Task Two** \`est:10m\`\n`;
|
|
685
|
+
writeFileSync(join(planDir, `${sid}-PLAN.md`), planChecked);
|
|
686
|
+
writeFileSync(join(tasksDir, "T01-SUMMARY.md"), "---\nid: T01\n---\n# Summary\n");
|
|
687
|
+
|
|
688
|
+
// invalidateStateCache alone: _stateCache cleared but path/parse caches warm
|
|
689
|
+
invalidateStateCache();
|
|
690
|
+
|
|
691
|
+
// invalidateAllCaches: all caches cleared — deriveState must re-read disk
|
|
692
|
+
invalidateAllCaches();
|
|
693
|
+
const state2 = await deriveState(base);
|
|
694
|
+
|
|
695
|
+
// After full invalidation, T01 should be complete and T02 should be next
|
|
696
|
+
assert.notEqual(state2.activeTask?.id, "T01", "#793: T01 not re-dispatched after full invalidation");
|
|
697
|
+
|
|
698
|
+
// Verify the caches are truly cleared by calling clearParseCache and clearPathCache
|
|
699
|
+
// do not throw (they should be no-ops after invalidateAllCaches already cleared them)
|
|
700
|
+
clearParseCache(); // no-op, but should not throw
|
|
701
|
+
assert.ok(true, "clearParseCache after invalidateAllCaches is safe");
|
|
750
702
|
});
|
|
751
703
|
|
|
752
704
|
// ─── hasImplementationArtifacts (#1703) ───────────────────────────────────
|
|
@@ -766,88 +718,78 @@ function makeGitBase(): string {
|
|
|
766
718
|
return base;
|
|
767
719
|
}
|
|
768
720
|
|
|
769
|
-
test("hasImplementationArtifacts returns false when only .gsd/ files committed (#1703)", () => {
|
|
721
|
+
test("hasImplementationArtifacts returns false when only .gsd/ files committed (#1703)", (t) => {
|
|
770
722
|
const base = makeGitBase();
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
cleanup(base);
|
|
784
|
-
}
|
|
723
|
+
t.after(() => cleanup(base));
|
|
724
|
+
|
|
725
|
+
// Create a feature branch and commit only .gsd/ files
|
|
726
|
+
execFileSync("git", ["checkout", "-b", "feat/test-milestone"], { cwd: base, stdio: "ignore" });
|
|
727
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
|
|
728
|
+
writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-ROADMAP.md"), "# Roadmap");
|
|
729
|
+
writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-SUMMARY.md"), "# Summary");
|
|
730
|
+
execFileSync("git", ["add", "."], { cwd: base, stdio: "ignore" });
|
|
731
|
+
execFileSync("git", ["commit", "-m", "chore: add plan files"], { cwd: base, stdio: "ignore" });
|
|
732
|
+
|
|
733
|
+
const result = hasImplementationArtifacts(base);
|
|
734
|
+
assert.equal(result, false, "should return false when only .gsd/ files were committed");
|
|
785
735
|
});
|
|
786
736
|
|
|
787
|
-
test("hasImplementationArtifacts returns true when implementation files committed (#1703)", () => {
|
|
737
|
+
test("hasImplementationArtifacts returns true when implementation files committed (#1703)", (t) => {
|
|
788
738
|
const base = makeGitBase();
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
cleanup(base);
|
|
803
|
-
}
|
|
739
|
+
t.after(() => cleanup(base));
|
|
740
|
+
|
|
741
|
+
// Create a feature branch with both .gsd/ and implementation files
|
|
742
|
+
execFileSync("git", ["checkout", "-b", "feat/test-impl"], { cwd: base, stdio: "ignore" });
|
|
743
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
|
|
744
|
+
writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-ROADMAP.md"), "# Roadmap");
|
|
745
|
+
mkdirSync(join(base, "src"), { recursive: true });
|
|
746
|
+
writeFileSync(join(base, "src", "feature.ts"), "export function feature() {}");
|
|
747
|
+
execFileSync("git", ["add", "."], { cwd: base, stdio: "ignore" });
|
|
748
|
+
execFileSync("git", ["commit", "-m", "feat: add feature"], { cwd: base, stdio: "ignore" });
|
|
749
|
+
|
|
750
|
+
const result = hasImplementationArtifacts(base);
|
|
751
|
+
assert.equal(result, true, "should return true when implementation files are present");
|
|
804
752
|
});
|
|
805
753
|
|
|
806
|
-
test("hasImplementationArtifacts returns true on non-git directory (fail-open)", () => {
|
|
754
|
+
test("hasImplementationArtifacts returns true on non-git directory (fail-open)", (t) => {
|
|
807
755
|
const base = join(tmpdir(), `gsd-test-nogit-${randomUUID()}`);
|
|
808
756
|
mkdirSync(base, { recursive: true });
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
cleanup(base);
|
|
814
|
-
}
|
|
757
|
+
t.after(() => cleanup(base));
|
|
758
|
+
|
|
759
|
+
const result = hasImplementationArtifacts(base);
|
|
760
|
+
assert.equal(result, true, "should return true (fail-open) in non-git directory");
|
|
815
761
|
});
|
|
816
762
|
|
|
817
763
|
// ─── verifyExpectedArtifact: complete-milestone requires impl artifacts (#1703) ──
|
|
818
764
|
|
|
819
|
-
test("verifyExpectedArtifact complete-milestone fails with only .gsd/ files (#1703)", () => {
|
|
765
|
+
test("verifyExpectedArtifact complete-milestone fails with only .gsd/ files (#1703)", (t) => {
|
|
820
766
|
const base = makeGitBase();
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
cleanup(base);
|
|
833
|
-
}
|
|
767
|
+
t.after(() => cleanup(base));
|
|
768
|
+
|
|
769
|
+
// Create feature branch with only .gsd/ files
|
|
770
|
+
execFileSync("git", ["checkout", "-b", "feat/ms-only-gsd"], { cwd: base, stdio: "ignore" });
|
|
771
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
|
|
772
|
+
writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-SUMMARY.md"), "# Milestone Summary\nDone.");
|
|
773
|
+
execFileSync("git", ["add", "."], { cwd: base, stdio: "ignore" });
|
|
774
|
+
execFileSync("git", ["commit", "-m", "chore: milestone plan files"], { cwd: base, stdio: "ignore" });
|
|
775
|
+
|
|
776
|
+
const result = verifyExpectedArtifact("complete-milestone", "M001", base);
|
|
777
|
+
assert.equal(result, false, "complete-milestone should fail verification when only .gsd/ files present");
|
|
834
778
|
});
|
|
835
779
|
|
|
836
|
-
test("verifyExpectedArtifact complete-milestone passes with impl files (#1703)", () => {
|
|
780
|
+
test("verifyExpectedArtifact complete-milestone passes with impl files (#1703)", (t) => {
|
|
837
781
|
const base = makeGitBase();
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
cleanup(base);
|
|
852
|
-
}
|
|
782
|
+
t.after(() => cleanup(base));
|
|
783
|
+
|
|
784
|
+
// Create feature branch with implementation files AND milestone summary
|
|
785
|
+
execFileSync("git", ["checkout", "-b", "feat/ms-with-impl"], { cwd: base, stdio: "ignore" });
|
|
786
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
|
|
787
|
+
writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-SUMMARY.md"), "# Milestone Summary\nDone.");
|
|
788
|
+
mkdirSync(join(base, "src"), { recursive: true });
|
|
789
|
+
writeFileSync(join(base, "src", "app.ts"), "console.log('hello');");
|
|
790
|
+
execFileSync("git", ["add", "."], { cwd: base, stdio: "ignore" });
|
|
791
|
+
execFileSync("git", ["commit", "-m", "feat: implementation"], { cwd: base, stdio: "ignore" });
|
|
792
|
+
|
|
793
|
+
const result = verifyExpectedArtifact("complete-milestone", "M001", base);
|
|
794
|
+
assert.equal(result, true, "complete-milestone should pass verification with implementation files");
|
|
853
795
|
});
|