gsd-pi 2.44.0-dev.848dd4c → 2.44.0
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 +12 -30
- package/dist/resources/extensions/gsd/auto-start.js +0 -10
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +0 -5
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- 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 +3 -3
- 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 +1 -1
- 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 +4 -4
- 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 +4 -4
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- 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 +14 -14
- 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.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/app/_not-found/{page-f2a7482d42a5614b.js → page-2f24283c162b6ab3.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-a16c7a7ecdf0c2cf.js → layout-9ecfd95f343793f0.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-7e9530a7122506c5.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +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/pi-coding-agent/dist/core/auth-storage.test.js +8 -6
- 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 +26 -24
- 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 +48 -29
- package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +44 -34
- 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 +34 -30
- package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +12 -10
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +47 -43
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
- package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
- package/packages/pi-coding-agent/src/core/fs-utils.test.ts +43 -31
- package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +45 -40
- package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
- package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
- package/src/resources/extensions/gsd/auto-start.ts +0 -14
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +0 -8
- 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 +16 -14
- package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +57 -43
- package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +13 -11
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +523 -465
- package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +75 -73
- package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +56 -34
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +656 -533
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +143 -165
- package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +52 -29
- package/src/resources/extensions/gsd/tests/captures.test.ts +176 -148
- package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +33 -32
- package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +143 -141
- 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 +59 -38
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +263 -228
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +302 -250
- package/src/resources/extensions/gsd/tests/context-store.test.ts +367 -354
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +72 -68
- package/src/resources/extensions/gsd/tests/cost-projection.test.ts +106 -92
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +35 -27
- package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +237 -220
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +420 -390
- package/src/resources/extensions/gsd/tests/definition-loader.test.ts +92 -76
- package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +83 -68
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +183 -152
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +101 -78
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +227 -192
- package/src/resources/extensions/gsd/tests/detection.test.ts +278 -232
- package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +34 -30
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +180 -164
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +49 -43
- package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +32 -28
- package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +29 -27
- package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +38 -34
- package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +75 -54
- package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +32 -21
- package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +97 -72
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +44 -38
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +145 -104
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +106 -84
- package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +60 -54
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +93 -72
- package/src/resources/extensions/gsd/tests/doctor.test.ts +134 -104
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +131 -123
- package/src/resources/extensions/gsd/tests/exit-command.test.ts +24 -20
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +57 -48
- package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +7 -5
- package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +42 -30
- package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +206 -198
- package/src/resources/extensions/gsd/tests/git-locale.test.ts +27 -13
- package/src/resources/extensions/gsd/tests/git-service.test.ts +388 -285
- package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +39 -31
- package/src/resources/extensions/gsd/tests/graph-operations.test.ts +69 -63
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +264 -255
- package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +119 -108
- package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +103 -81
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +262 -229
- package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +37 -29
- package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +102 -81
- package/src/resources/extensions/gsd/tests/init-wizard.test.ts +18 -16
- package/src/resources/extensions/gsd/tests/integration-edge.test.ts +46 -41
- package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +53 -42
- package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +91 -75
- package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +194 -150
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +125 -101
- package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +54 -45
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +93 -80
- package/src/resources/extensions/gsd/tests/migrate-command.test.ts +66 -57
- package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +93 -83
- package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +170 -161
- package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +141 -125
- package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +131 -107
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +96 -87
- package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +164 -125
- package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +94 -81
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +36 -35
- package/src/resources/extensions/gsd/tests/overrides.test.ts +106 -99
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +47 -40
- package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +28 -25
- package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +83 -66
- package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +77 -54
- package/src/resources/extensions/gsd/tests/park-milestone.test.ts +115 -68
- package/src/resources/extensions/gsd/tests/parsers.test.ts +611 -546
- package/src/resources/extensions/gsd/tests/paths.test.ts +87 -72
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +117 -77
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
- package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +119 -93
- package/src/resources/extensions/gsd/tests/queue-order.test.ts +82 -70
- package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +55 -42
- package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +73 -45
- package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +38 -28
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +80 -73
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +74 -71
- package/src/resources/extensions/gsd/tests/requirements.test.ts +75 -70
- package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +66 -44
- package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +181 -114
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +65 -63
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +128 -66
- package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +25 -18
- package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +44 -37
- package/src/resources/extensions/gsd/tests/shared-wal.test.ts +26 -19
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +8 -6
- package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +28 -22
- package/src/resources/extensions/gsd/tests/token-savings.test.ts +56 -54
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +25 -23
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +11 -9
- package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +82 -66
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +47 -46
- package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +22 -20
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +86 -84
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +43 -41
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +96 -94
- package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +13 -11
- package/src/resources/extensions/gsd/tests/worker-registry.test.ts +29 -27
- package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +52 -50
- package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +13 -10
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +18 -14
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +39 -38
- package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +21 -17
- package/src/resources/extensions/gsd/tests/worktree-health.test.ts +30 -25
- package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +37 -30
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +22 -15
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +66 -59
- package/src/resources/extensions/gsd/tests/worktree.test.ts +50 -44
- package/dist/web/standalone/.next/static/chunks/app/page-b9367c5ae13b99c6.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +0 -1
- package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +0 -100
- package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +0 -63
- /package/dist/web/standalone/.next/static/{-zps1Q9mQmioAKLcQiCr8 → mgkxN0mGP6gSUmGPEzbk_}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{-zps1Q9mQmioAKLcQiCr8 → mgkxN0mGP6gSUmGPEzbk_}/_ssgManifest.js +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { describe, test } from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
1
|
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
4
2
|
import { join } from 'node:path';
|
|
5
3
|
import { tmpdir } from 'node:os';
|
|
6
4
|
|
|
7
5
|
import { deriveState, isSliceComplete, isMilestoneComplete, isGhostMilestone } from '../state.ts';
|
|
6
|
+
import { createTestContext } from './test-helpers.ts';
|
|
7
|
+
|
|
8
|
+
const { assertEq, assertTrue, report } = createTestContext();
|
|
8
9
|
// ─── Fixture Helpers ───────────────────────────────────────────────────────
|
|
9
10
|
|
|
10
11
|
function createFixtureBase(): string {
|
|
@@ -64,28 +65,30 @@ function cleanup(base: string): void {
|
|
|
64
65
|
// Test Groups
|
|
65
66
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
66
67
|
|
|
67
|
-
|
|
68
|
+
async function main(): Promise<void> {
|
|
68
69
|
|
|
69
70
|
// ─── Test 1: empty milestones dir → pre-planning ───────────────────────
|
|
70
|
-
|
|
71
|
+
console.log('\n=== empty milestones dir → pre-planning ===');
|
|
72
|
+
{
|
|
71
73
|
const base = createFixtureBase();
|
|
72
74
|
try {
|
|
73
75
|
const state = await deriveState(base);
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
assertEq(state.phase, 'pre-planning', 'phase is pre-planning');
|
|
78
|
+
assertEq(state.activeMilestone, null, 'activeMilestone is null');
|
|
79
|
+
assertEq(state.activeSlice, null, 'activeSlice is null');
|
|
80
|
+
assertEq(state.activeTask, null, 'activeTask is null');
|
|
81
|
+
assertEq(state.registry, [], 'registry is empty');
|
|
82
|
+
assertEq(state.progress?.milestones?.done, 0, 'milestones done = 0');
|
|
83
|
+
assertEq(state.progress?.milestones?.total, 0, 'milestones total = 0');
|
|
82
84
|
} finally {
|
|
83
85
|
cleanup(base);
|
|
84
86
|
}
|
|
85
|
-
}
|
|
87
|
+
}
|
|
86
88
|
|
|
87
89
|
// ─── Test 2: milestone dir exists but no roadmap → pre-planning ────────
|
|
88
|
-
|
|
90
|
+
console.log('\n=== milestone dir exists but no roadmap → pre-planning ===');
|
|
91
|
+
{
|
|
89
92
|
const base = createFixtureBase();
|
|
90
93
|
try {
|
|
91
94
|
// Create M001 directory with CONTEXT but no roadmap file
|
|
@@ -94,20 +97,21 @@ describe('derive-state', async () => {
|
|
|
94
97
|
|
|
95
98
|
const state = await deriveState(base);
|
|
96
99
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
assertEq(state.phase, 'pre-planning', 'phase is pre-planning');
|
|
101
|
+
assertTrue(state.activeMilestone !== null, 'activeMilestone is not null');
|
|
102
|
+
assertEq(state.activeMilestone?.id, 'M001', 'activeMilestone id is M001');
|
|
103
|
+
assertEq(state.activeSlice, null, 'activeSlice is null');
|
|
104
|
+
assertEq(state.activeTask, null, 'activeTask is null');
|
|
105
|
+
assertEq(state.registry.length, 1, 'registry has 1 entry');
|
|
106
|
+
assertEq(state.registry[0]?.status, 'active', 'registry entry status is active');
|
|
104
107
|
} finally {
|
|
105
108
|
cleanup(base);
|
|
106
109
|
}
|
|
107
|
-
}
|
|
110
|
+
}
|
|
108
111
|
|
|
109
112
|
// ─── Test 3: roadmap with incomplete slice, no plan → planning ─────────
|
|
110
|
-
|
|
113
|
+
console.log('\n=== roadmap with incomplete slice, no plan → planning ===');
|
|
114
|
+
{
|
|
111
115
|
const base = createFixtureBase();
|
|
112
116
|
try {
|
|
113
117
|
writeRoadmap(base, 'M001', `# M001: Test Milestone
|
|
@@ -122,19 +126,20 @@ describe('derive-state', async () => {
|
|
|
122
126
|
|
|
123
127
|
const state = await deriveState(base);
|
|
124
128
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
assertEq(state.phase, 'planning', 'phase is planning');
|
|
130
|
+
assertTrue(state.activeSlice !== null, 'activeSlice is not null');
|
|
131
|
+
assertEq(state.activeSlice?.id, 'S01', 'activeSlice id is S01');
|
|
132
|
+
assertEq(state.activeTask, null, 'activeTask is null');
|
|
133
|
+
assertEq(state.progress?.slices?.done, 0, 'slices done = 0');
|
|
134
|
+
assertEq(state.progress?.slices?.total, 1, 'slices total = 1');
|
|
131
135
|
} finally {
|
|
132
136
|
cleanup(base);
|
|
133
137
|
}
|
|
134
|
-
}
|
|
138
|
+
}
|
|
135
139
|
|
|
136
140
|
// ─── Test 4: roadmap + plan with incomplete tasks → executing ──────────
|
|
137
|
-
|
|
141
|
+
console.log('\n=== roadmap + plan with incomplete tasks → executing ===');
|
|
142
|
+
{
|
|
138
143
|
const base = createFixtureBase();
|
|
139
144
|
try {
|
|
140
145
|
writeRoadmap(base, 'M001', `# M001: Test Milestone
|
|
@@ -163,18 +168,19 @@ describe('derive-state', async () => {
|
|
|
163
168
|
|
|
164
169
|
const state = await deriveState(base);
|
|
165
170
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
+
assertEq(state.phase, 'executing', 'phase is executing');
|
|
172
|
+
assertTrue(state.activeTask !== null, 'activeTask is not null');
|
|
173
|
+
assertEq(state.activeTask?.id, 'T01', 'activeTask id is T01');
|
|
174
|
+
assertEq(state.progress?.tasks?.done, 0, 'tasks done = 0');
|
|
175
|
+
assertEq(state.progress?.tasks?.total, 2, 'tasks total = 2');
|
|
171
176
|
} finally {
|
|
172
177
|
cleanup(base);
|
|
173
178
|
}
|
|
174
|
-
}
|
|
179
|
+
}
|
|
175
180
|
|
|
176
181
|
// ─── Test 5: executing + continue file → resume message ─────────────
|
|
177
|
-
|
|
182
|
+
console.log('\n=== executing + continue file → resume message ===');
|
|
183
|
+
{
|
|
178
184
|
const base = createFixtureBase();
|
|
179
185
|
try {
|
|
180
186
|
writeRoadmap(base, 'M001', `# M001: Test Milestone
|
|
@@ -222,20 +228,21 @@ Continue from step 2.
|
|
|
222
228
|
|
|
223
229
|
const state = await deriveState(base);
|
|
224
230
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
231
|
+
assertEq(state.phase, 'executing', 'interrupted: phase is executing');
|
|
232
|
+
assertTrue(state.activeTask !== null, 'interrupted: activeTask is not null');
|
|
233
|
+
assertEq(state.activeTask?.id, 'T01', 'interrupted: activeTask id is T01');
|
|
234
|
+
assertTrue(
|
|
229
235
|
state.nextAction.includes('Resume') || state.nextAction.includes('resume') || state.nextAction.includes('continue.md'),
|
|
230
236
|
'interrupted: nextAction mentions Resume/resume/continue.md'
|
|
231
237
|
);
|
|
232
238
|
} finally {
|
|
233
239
|
cleanup(base);
|
|
234
240
|
}
|
|
235
|
-
}
|
|
241
|
+
}
|
|
236
242
|
|
|
237
243
|
// ─── Test 6: all tasks done, slice not [x] → summarizing ──────────────
|
|
238
|
-
|
|
244
|
+
console.log('\n=== all tasks done, slice not [x] → summarizing ===');
|
|
245
|
+
{
|
|
239
246
|
const base = createFixtureBase();
|
|
240
247
|
try {
|
|
241
248
|
writeRoadmap(base, 'M001', `# M001: Test Milestone
|
|
@@ -264,23 +271,24 @@ Continue from step 2.
|
|
|
264
271
|
|
|
265
272
|
const state = await deriveState(base);
|
|
266
273
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
274
|
+
assertEq(state.phase, 'summarizing', 'summarizing: phase is summarizing');
|
|
275
|
+
assertTrue(state.activeSlice !== null, 'summarizing: activeSlice is not null');
|
|
276
|
+
assertEq(state.activeSlice?.id, 'S01', 'summarizing: activeSlice id is S01');
|
|
277
|
+
assertEq(state.activeTask, null, 'summarizing: activeTask is null');
|
|
278
|
+
assertTrue(
|
|
272
279
|
state.nextAction.toLowerCase().includes('summary') || state.nextAction.toLowerCase().includes('complete'),
|
|
273
280
|
'summarizing: nextAction mentions summary or complete'
|
|
274
281
|
);
|
|
275
|
-
|
|
276
|
-
|
|
282
|
+
assertEq(state.progress?.tasks?.done, 2, 'summarizing: tasks done = 2');
|
|
283
|
+
assertEq(state.progress?.tasks?.total, 2, 'summarizing: tasks total = 2');
|
|
277
284
|
} finally {
|
|
278
285
|
cleanup(base);
|
|
279
286
|
}
|
|
280
|
-
}
|
|
287
|
+
}
|
|
281
288
|
|
|
282
289
|
// ─── Test 7: all milestones complete → complete ────────────────────────
|
|
283
|
-
|
|
290
|
+
console.log('\n=== all milestones complete → complete ===');
|
|
291
|
+
{
|
|
284
292
|
const base = createFixtureBase();
|
|
285
293
|
try {
|
|
286
294
|
writeRoadmap(base, 'M001', `# M001: Test Milestone
|
|
@@ -298,22 +306,23 @@ Continue from step 2.
|
|
|
298
306
|
|
|
299
307
|
const state = await deriveState(base);
|
|
300
308
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
309
|
+
assertEq(state.phase, 'complete', 'complete: phase is complete');
|
|
310
|
+
assertEq(state.activeSlice, null, 'complete: activeSlice is null');
|
|
311
|
+
assertEq(state.activeTask, null, 'complete: activeTask is null');
|
|
312
|
+
assertTrue(
|
|
305
313
|
state.nextAction.toLowerCase().includes('complete'),
|
|
306
314
|
'complete: nextAction mentions complete'
|
|
307
315
|
);
|
|
308
|
-
|
|
309
|
-
|
|
316
|
+
assertEq(state.registry.length, 1, 'complete: registry has 1 entry');
|
|
317
|
+
assertEq(state.registry[0]?.status, 'complete', 'complete: registry[0] status is complete');
|
|
310
318
|
} finally {
|
|
311
319
|
cleanup(base);
|
|
312
320
|
}
|
|
313
|
-
}
|
|
321
|
+
}
|
|
314
322
|
|
|
315
323
|
// ─── Test 7b: complete with active requirements → surfaces unmapped reqs ──
|
|
316
|
-
|
|
324
|
+
console.log('\n=== complete with active requirements → surfaces unmapped reqs ===');
|
|
325
|
+
{
|
|
317
326
|
const base = createFixtureBase();
|
|
318
327
|
try {
|
|
319
328
|
writeRoadmap(base, 'M001', `# M001: Test Milestone
|
|
@@ -346,22 +355,23 @@ Continue from step 2.
|
|
|
346
355
|
|
|
347
356
|
const state = await deriveState(base);
|
|
348
357
|
|
|
349
|
-
|
|
350
|
-
|
|
358
|
+
assertEq(state.phase, 'complete', 'complete-with-reqs: phase is complete');
|
|
359
|
+
assertTrue(
|
|
351
360
|
state.nextAction.includes('2 active requirements'),
|
|
352
361
|
'complete-with-reqs: nextAction mentions 2 active requirements'
|
|
353
362
|
);
|
|
354
|
-
|
|
363
|
+
assertTrue(
|
|
355
364
|
state.nextAction.includes('REQUIREMENTS.md'),
|
|
356
365
|
'complete-with-reqs: nextAction mentions REQUIREMENTS.md'
|
|
357
366
|
);
|
|
358
367
|
} finally {
|
|
359
368
|
cleanup(base);
|
|
360
369
|
}
|
|
361
|
-
}
|
|
370
|
+
}
|
|
362
371
|
|
|
363
372
|
// ─── Test 7c: complete with no active requirements → standard message ──
|
|
364
|
-
|
|
373
|
+
console.log('\n=== complete with no active requirements → standard message ===');
|
|
374
|
+
{
|
|
365
375
|
const base = createFixtureBase();
|
|
366
376
|
try {
|
|
367
377
|
writeRoadmap(base, 'M001', `# M001: Test Milestone
|
|
@@ -386,15 +396,16 @@ Continue from step 2.
|
|
|
386
396
|
|
|
387
397
|
const state = await deriveState(base);
|
|
388
398
|
|
|
389
|
-
|
|
390
|
-
|
|
399
|
+
assertEq(state.phase, 'complete', 'complete-no-active-reqs: phase is complete');
|
|
400
|
+
assertEq(state.nextAction, 'All milestones complete.', 'complete-no-active-reqs: standard completion message');
|
|
391
401
|
} finally {
|
|
392
402
|
cleanup(base);
|
|
393
403
|
}
|
|
394
|
-
}
|
|
404
|
+
}
|
|
395
405
|
|
|
396
406
|
// ─── Test 8: blocked dependencies ──────────────────────────────────────
|
|
397
|
-
|
|
407
|
+
console.log('\n=== blocked dependencies ===');
|
|
408
|
+
{
|
|
398
409
|
// Case A: S01 active (deps satisfied), S02 blocked on S01
|
|
399
410
|
const base1 = createFixtureBase();
|
|
400
411
|
try {
|
|
@@ -425,8 +436,8 @@ Continue from step 2.
|
|
|
425
436
|
|
|
426
437
|
const state1 = await deriveState(base1);
|
|
427
438
|
|
|
428
|
-
|
|
429
|
-
|
|
439
|
+
assertEq(state1.phase, 'executing', 'blocked-A: phase is executing (S01 active)');
|
|
440
|
+
assertEq(state1.activeSlice?.id, 'S01', 'blocked-A: activeSlice is S01');
|
|
430
441
|
} finally {
|
|
431
442
|
cleanup(base1);
|
|
432
443
|
}
|
|
@@ -446,16 +457,17 @@ Continue from step 2.
|
|
|
446
457
|
|
|
447
458
|
const state2 = await deriveState(base2);
|
|
448
459
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
460
|
+
assertEq(state2.phase, 'blocked', 'blocked-B: phase is blocked');
|
|
461
|
+
assertEq(state2.activeSlice, null, 'blocked-B: activeSlice is null');
|
|
462
|
+
assertTrue(state2.blockers.length > 0, 'blocked-B: blockers array is non-empty');
|
|
452
463
|
} finally {
|
|
453
464
|
cleanup(base2);
|
|
454
465
|
}
|
|
455
|
-
}
|
|
466
|
+
}
|
|
456
467
|
|
|
457
468
|
// ─── Test 9: multi-milestone registry ──────────────────────────────────
|
|
458
|
-
|
|
469
|
+
console.log('\n=== multi-milestone registry ===');
|
|
470
|
+
{
|
|
459
471
|
const base = createFixtureBase();
|
|
460
472
|
try {
|
|
461
473
|
// M001: complete (all slices done)
|
|
@@ -489,23 +501,24 @@ Continue from step 2.
|
|
|
489
501
|
|
|
490
502
|
const state = await deriveState(base);
|
|
491
503
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
504
|
+
assertEq(state.registry.length, 3, 'multi-ms: registry has 3 entries');
|
|
505
|
+
assertEq(state.registry[0]?.id, 'M001', 'multi-ms: registry[0] is M001');
|
|
506
|
+
assertEq(state.registry[0]?.status, 'complete', 'multi-ms: M001 is complete');
|
|
507
|
+
assertEq(state.registry[1]?.id, 'M002', 'multi-ms: registry[1] is M002');
|
|
508
|
+
assertEq(state.registry[1]?.status, 'active', 'multi-ms: M002 is active');
|
|
509
|
+
assertEq(state.registry[2]?.id, 'M003', 'multi-ms: registry[2] is M003');
|
|
510
|
+
assertEq(state.registry[2]?.status, 'pending', 'multi-ms: M003 is pending');
|
|
511
|
+
assertEq(state.activeMilestone?.id, 'M002', 'multi-ms: activeMilestone is M002');
|
|
512
|
+
assertEq(state.progress?.milestones?.done, 1, 'multi-ms: milestones done = 1');
|
|
513
|
+
assertEq(state.progress?.milestones?.total, 3, 'multi-ms: milestones total = 3');
|
|
502
514
|
} finally {
|
|
503
515
|
cleanup(base);
|
|
504
516
|
}
|
|
505
|
-
}
|
|
517
|
+
}
|
|
506
518
|
|
|
507
519
|
// ─── Test 10: requirements integration ─────────────────────────────────
|
|
508
|
-
|
|
520
|
+
console.log('\n=== requirements integration ===');
|
|
521
|
+
{
|
|
509
522
|
const base = createFixtureBase();
|
|
510
523
|
try {
|
|
511
524
|
writeRequirements(base, `# Requirements
|
|
@@ -546,19 +559,20 @@ Continue from step 2.
|
|
|
546
559
|
// Need at least an empty milestones dir for deriveState
|
|
547
560
|
const state = await deriveState(base);
|
|
548
561
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
562
|
+
assertTrue(state.requirements !== undefined, 'requirements: requirements object exists');
|
|
563
|
+
assertEq(state.requirements?.active, 2, 'requirements: active = 2');
|
|
564
|
+
assertEq(state.requirements?.validated, 1, 'requirements: validated = 1');
|
|
565
|
+
assertEq(state.requirements?.deferred, 2, 'requirements: deferred = 2');
|
|
566
|
+
assertEq(state.requirements?.outOfScope, 1, 'requirements: outOfScope = 1');
|
|
567
|
+
assertEq(state.requirements?.total, 6, 'requirements: total = 6 (sum of all)');
|
|
555
568
|
} finally {
|
|
556
569
|
cleanup(base);
|
|
557
570
|
}
|
|
558
|
-
}
|
|
571
|
+
}
|
|
559
572
|
|
|
560
573
|
// ─── Test 11: all slices [x], no summary → completing-milestone ────────
|
|
561
|
-
|
|
574
|
+
console.log('\n=== all slices [x], no summary → completing-milestone ===');
|
|
575
|
+
{
|
|
562
576
|
const base = createFixtureBase();
|
|
563
577
|
try {
|
|
564
578
|
writeRoadmap(base, 'M001', `# M001: Test Milestone
|
|
@@ -578,26 +592,27 @@ Continue from step 2.
|
|
|
578
592
|
|
|
579
593
|
const state = await deriveState(base);
|
|
580
594
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
595
|
+
assertEq(state.phase, 'completing-milestone', 'completing-ms: phase is completing-milestone');
|
|
596
|
+
assertTrue(state.activeMilestone !== null, 'completing-ms: activeMilestone is not null');
|
|
597
|
+
assertEq(state.activeMilestone?.id, 'M001', 'completing-ms: activeMilestone id is M001');
|
|
598
|
+
assertEq(state.activeSlice, null, 'completing-ms: activeSlice is null');
|
|
599
|
+
assertEq(state.activeTask, null, 'completing-ms: activeTask is null');
|
|
600
|
+
assertEq(state.registry.length, 1, 'completing-ms: registry has 1 entry');
|
|
601
|
+
assertEq(state.registry[0]?.status, 'active', 'completing-ms: registry[0] status is active (not complete)');
|
|
602
|
+
assertEq(state.progress?.slices?.done, 2, 'completing-ms: slices done = 2');
|
|
603
|
+
assertEq(state.progress?.slices?.total, 2, 'completing-ms: slices total = 2');
|
|
604
|
+
assertTrue(
|
|
591
605
|
state.nextAction.toLowerCase().includes('summary') || state.nextAction.toLowerCase().includes('complete'),
|
|
592
606
|
'completing-ms: nextAction mentions summary or complete'
|
|
593
607
|
);
|
|
594
608
|
} finally {
|
|
595
609
|
cleanup(base);
|
|
596
610
|
}
|
|
597
|
-
}
|
|
611
|
+
}
|
|
598
612
|
|
|
599
613
|
// ─── Test 12: all slices [x], summary exists → complete ───────────────
|
|
600
|
-
|
|
614
|
+
console.log('\n=== all slices [x], summary exists → complete ===');
|
|
615
|
+
{
|
|
601
616
|
const base = createFixtureBase();
|
|
602
617
|
try {
|
|
603
618
|
writeRoadmap(base, 'M001', `# M001: Test Milestone
|
|
@@ -615,18 +630,19 @@ Continue from step 2.
|
|
|
615
630
|
|
|
616
631
|
const state = await deriveState(base);
|
|
617
632
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
633
|
+
assertEq(state.phase, 'complete', 'summary-exists: phase is complete');
|
|
634
|
+
assertEq(state.registry.length, 1, 'summary-exists: registry has 1 entry');
|
|
635
|
+
assertEq(state.registry[0]?.status, 'complete', 'summary-exists: registry[0] status is complete');
|
|
636
|
+
assertEq(state.activeSlice, null, 'summary-exists: activeSlice is null');
|
|
637
|
+
assertEq(state.activeTask, null, 'summary-exists: activeTask is null');
|
|
623
638
|
} finally {
|
|
624
639
|
cleanup(base);
|
|
625
640
|
}
|
|
626
|
-
}
|
|
641
|
+
}
|
|
627
642
|
|
|
628
643
|
// ─── Test 13: multi-milestone completing-milestone ─────────────────────
|
|
629
|
-
|
|
644
|
+
console.log('\n=== multi-milestone completing-milestone ===');
|
|
645
|
+
{
|
|
630
646
|
const base = createFixtureBase();
|
|
631
647
|
try {
|
|
632
648
|
// M001: all slices done + summary exists → complete
|
|
@@ -671,28 +687,29 @@ Continue from step 2.
|
|
|
671
687
|
|
|
672
688
|
const state = await deriveState(base);
|
|
673
689
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
690
|
+
assertEq(state.phase, 'completing-milestone', 'multi-completing: phase is completing-milestone');
|
|
691
|
+
assertEq(state.activeMilestone?.id, 'M002', 'multi-completing: activeMilestone is M002');
|
|
692
|
+
assertEq(state.activeSlice, null, 'multi-completing: activeSlice is null');
|
|
693
|
+
assertEq(state.activeTask, null, 'multi-completing: activeTask is null');
|
|
694
|
+
assertEq(state.registry.length, 3, 'multi-completing: registry has 3 entries');
|
|
695
|
+
assertEq(state.registry[0]?.id, 'M001', 'multi-completing: registry[0] is M001');
|
|
696
|
+
assertEq(state.registry[0]?.status, 'complete', 'multi-completing: M001 is complete');
|
|
697
|
+
assertEq(state.registry[1]?.id, 'M002', 'multi-completing: registry[1] is M002');
|
|
698
|
+
assertEq(state.registry[1]?.status, 'active', 'multi-completing: M002 is active (completing-milestone)');
|
|
699
|
+
assertEq(state.registry[2]?.id, 'M003', 'multi-completing: registry[2] is M003');
|
|
700
|
+
assertEq(state.registry[2]?.status, 'pending', 'multi-completing: M003 is pending');
|
|
701
|
+
assertEq(state.progress?.milestones?.done, 1, 'multi-completing: milestones done = 1');
|
|
702
|
+
assertEq(state.progress?.milestones?.total, 3, 'multi-completing: milestones total = 3');
|
|
703
|
+
assertEq(state.progress?.slices?.done, 2, 'multi-completing: slices done = 2');
|
|
704
|
+
assertEq(state.progress?.slices?.total, 2, 'multi-completing: slices total = 2');
|
|
689
705
|
} finally {
|
|
690
706
|
cleanup(base);
|
|
691
707
|
}
|
|
692
|
-
}
|
|
708
|
+
}
|
|
693
709
|
|
|
694
710
|
// ═══ Milestone with summary but no roadmap → complete ═══════════════════
|
|
695
711
|
{
|
|
712
|
+
console.log('\n=== milestone with summary and no roadmap → complete ===');
|
|
696
713
|
const base = createFixtureBase();
|
|
697
714
|
try {
|
|
698
715
|
// M001, M002: completed milestones with summaries but no roadmaps
|
|
@@ -709,17 +726,17 @@ Continue from step 2.
|
|
|
709
726
|
|
|
710
727
|
const state = await deriveState(base);
|
|
711
728
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
729
|
+
assertEq(state.phase, 'planning', 'summary-no-roadmap: phase is planning (active is M003)');
|
|
730
|
+
assertEq(state.activeMilestone?.id, 'M003', 'summary-no-roadmap: active milestone is M003');
|
|
731
|
+
assertEq(state.activeMilestone?.title, 'Polish', 'summary-no-roadmap: active title is Polish');
|
|
732
|
+
assertEq(state.registry.length, 3, 'summary-no-roadmap: registry has 3 entries');
|
|
733
|
+
assertEq(state.registry[0]?.status, 'complete', 'summary-no-roadmap: M001 is complete');
|
|
734
|
+
assertEq(state.registry[0]?.title, 'Bootstrap', 'summary-no-roadmap: M001 title from summary');
|
|
735
|
+
assertEq(state.registry[1]?.status, 'complete', 'summary-no-roadmap: M002 is complete');
|
|
736
|
+
assertEq(state.registry[1]?.title, 'Core Features', 'summary-no-roadmap: M002 title from summary');
|
|
737
|
+
assertEq(state.registry[2]?.status, 'active', 'summary-no-roadmap: M003 is active');
|
|
738
|
+
assertEq(state.progress?.milestones?.done, 2, 'summary-no-roadmap: milestones done = 2');
|
|
739
|
+
assertEq(state.progress?.milestones?.total, 3, 'summary-no-roadmap: milestones total = 3');
|
|
723
740
|
} finally {
|
|
724
741
|
cleanup(base);
|
|
725
742
|
}
|
|
@@ -727,6 +744,7 @@ Continue from step 2.
|
|
|
727
744
|
|
|
728
745
|
// ═══ All milestones have summary but no roadmap → complete ═════════════
|
|
729
746
|
{
|
|
747
|
+
console.log('\n=== all milestones summary-only → complete ===');
|
|
730
748
|
const base = createFixtureBase();
|
|
731
749
|
try {
|
|
732
750
|
const m1dir = join(base, '.gsd', 'milestones', 'M001');
|
|
@@ -734,15 +752,16 @@ Continue from step 2.
|
|
|
734
752
|
writeFileSync(join(m1dir, 'M001-SUMMARY.md'), '---\ntitle: Done\n---\nAll done.');
|
|
735
753
|
|
|
736
754
|
const state = await deriveState(base);
|
|
737
|
-
|
|
738
|
-
|
|
755
|
+
assertEq(state.phase, 'complete', 'all-summary-only: phase is complete');
|
|
756
|
+
assertEq(state.registry[0]?.status, 'complete', 'all-summary-only: M001 is complete');
|
|
739
757
|
} finally {
|
|
740
758
|
cleanup(base);
|
|
741
759
|
}
|
|
742
760
|
}
|
|
743
761
|
|
|
744
762
|
// ─── Empty plan (zero tasks) stays in planning, not summarizing (#454) ──
|
|
745
|
-
|
|
763
|
+
console.log('\n=== empty plan → planning (not summarizing) ===');
|
|
764
|
+
{
|
|
746
765
|
const base = createFixtureBase();
|
|
747
766
|
try {
|
|
748
767
|
writeRoadmap(base, 'M001', `---
|
|
@@ -767,16 +786,17 @@ slice: S01
|
|
|
767
786
|
## Tasks
|
|
768
787
|
`);
|
|
769
788
|
const state = await deriveState(base);
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
789
|
+
assertEq(state.phase, 'planning', 'empty plan stays in planning');
|
|
790
|
+
assertEq(state.activeSlice?.id, 'S01', 'active slice is S01');
|
|
791
|
+
assertEq(state.activeTask, null, 'no active task');
|
|
773
792
|
} finally {
|
|
774
793
|
cleanup(base);
|
|
775
794
|
}
|
|
776
|
-
}
|
|
795
|
+
}
|
|
777
796
|
|
|
778
797
|
// ─── Test: completed M001 (summary, no validation) skipped for active M003 (#864) ────
|
|
779
|
-
|
|
798
|
+
console.log('\n=== completed milestone with summary but no validation is not active (#864) ===');
|
|
799
|
+
{
|
|
780
800
|
const base = createFixtureBase();
|
|
781
801
|
try {
|
|
782
802
|
// M001: all slices done, has summary, no validation
|
|
@@ -786,16 +806,17 @@ slice: S01
|
|
|
786
806
|
writeRoadmap(base, 'M003', `# M003: Active Milestone\n\n**Vision:** Do stuff.\n\n## Slices\n\n- [ ] **S01: Work slice** \`risk:low\` \`depends:[]\`\n > Needs work.\n`);
|
|
787
807
|
|
|
788
808
|
const state = await deriveState(base);
|
|
789
|
-
|
|
809
|
+
assertEq(state.activeMilestone?.id, 'M003', 'active milestone is M003, not completed M001');
|
|
790
810
|
const m001Entry = state.registry.find(e => e.id === 'M001');
|
|
791
|
-
|
|
811
|
+
assertEq(m001Entry?.status, 'complete', 'M001 is marked complete despite no validation');
|
|
792
812
|
} finally {
|
|
793
813
|
cleanup(base);
|
|
794
814
|
}
|
|
795
|
-
}
|
|
815
|
+
}
|
|
796
816
|
|
|
797
817
|
// ─── Test: completed M001 with summary AND validation is complete (#864) ────
|
|
798
|
-
|
|
818
|
+
console.log('\n=== completed milestone with summary and validation is complete ===');
|
|
819
|
+
{
|
|
799
820
|
const base = createFixtureBase();
|
|
800
821
|
try {
|
|
801
822
|
writeRoadmap(base, 'M001', `# M001: First Milestone\n\n**Vision:** Done.\n\n## Slices\n\n- [x] **S01: Done slice** \`risk:low\` \`depends:[]\`\n > Completed.\n`);
|
|
@@ -804,30 +825,32 @@ slice: S01
|
|
|
804
825
|
writeRoadmap(base, 'M003', `# M003: Active Milestone\n\n**Vision:** Do stuff.\n\n## Slices\n\n- [ ] **S01: Work slice** \`risk:low\` \`depends:[]\`\n > Needs work.\n`);
|
|
805
826
|
|
|
806
827
|
const state = await deriveState(base);
|
|
807
|
-
|
|
828
|
+
assertEq(state.activeMilestone?.id, 'M003', 'active milestone is M003');
|
|
808
829
|
const m001Entry = state.registry.find(e => e.id === 'M001');
|
|
809
|
-
|
|
830
|
+
assertEq(m001Entry?.status, 'complete', 'M001 with both summary and validation is complete');
|
|
810
831
|
} finally {
|
|
811
832
|
cleanup(base);
|
|
812
833
|
}
|
|
813
|
-
}
|
|
834
|
+
}
|
|
814
835
|
|
|
815
836
|
// ─── Test: all slices done, no summary, no validation → needs validation (#864) ────
|
|
816
|
-
|
|
837
|
+
console.log('\n=== all slices done, no summary, no validation → validating-milestone ===');
|
|
838
|
+
{
|
|
817
839
|
const base = createFixtureBase();
|
|
818
840
|
try {
|
|
819
841
|
writeRoadmap(base, 'M001', `# M001: First Milestone\n\n**Vision:** Validate me.\n\n## Slices\n\n- [x] **S01: Done slice** \`risk:low\` \`depends:[]\`\n > Completed.\n`);
|
|
820
842
|
// No summary, no validation — this should be active for validation
|
|
821
843
|
|
|
822
844
|
const state = await deriveState(base);
|
|
823
|
-
|
|
845
|
+
assertEq(state.activeMilestone?.id, 'M001', 'M001 is active for validation');
|
|
824
846
|
} finally {
|
|
825
847
|
cleanup(base);
|
|
826
848
|
}
|
|
827
|
-
}
|
|
849
|
+
}
|
|
828
850
|
|
|
829
851
|
// ─── Test: all slices done, validation pass, no summary → needs completion (#864) ────
|
|
830
|
-
|
|
852
|
+
console.log('\n=== all slices done, validation pass, no summary → completing-milestone ===');
|
|
853
|
+
{
|
|
831
854
|
const base = createFixtureBase();
|
|
832
855
|
try {
|
|
833
856
|
writeRoadmap(base, 'M001', `# M001: First Milestone\n\n**Vision:** Complete me.\n\n## Slices\n\n- [x] **S01: Done slice** \`risk:low\` \`depends:[]\`\n > Completed.\n`);
|
|
@@ -835,14 +858,15 @@ slice: S01
|
|
|
835
858
|
// No summary — validated but not yet completed
|
|
836
859
|
|
|
837
860
|
const state = await deriveState(base);
|
|
838
|
-
|
|
861
|
+
assertEq(state.activeMilestone?.id, 'M001', 'M001 is active for completion');
|
|
839
862
|
} finally {
|
|
840
863
|
cleanup(base);
|
|
841
864
|
}
|
|
842
|
-
}
|
|
865
|
+
}
|
|
843
866
|
|
|
844
867
|
// ─── Test: unchecked roadmap slices + summary → complete (summary is terminal) ────
|
|
845
|
-
|
|
868
|
+
console.log('\n=== unchecked roadmap slices + summary → complete (summary is terminal) ===');
|
|
869
|
+
{
|
|
846
870
|
const base = createFixtureBase();
|
|
847
871
|
try {
|
|
848
872
|
// M001: roadmap has unchecked slices but a summary exists — should be complete
|
|
@@ -853,15 +877,16 @@ slice: S01
|
|
|
853
877
|
|
|
854
878
|
const state = await deriveState(base);
|
|
855
879
|
const m001Entry = state.registry.find(e => e.id === 'M001');
|
|
856
|
-
|
|
857
|
-
|
|
880
|
+
assertEq(m001Entry?.status, 'complete', 'M001 with unchecked roadmap + summary is complete');
|
|
881
|
+
assertEq(state.activeMilestone?.id, 'M002', 'active milestone is M002, not M001');
|
|
858
882
|
} finally {
|
|
859
883
|
cleanup(base);
|
|
860
884
|
}
|
|
861
|
-
}
|
|
885
|
+
}
|
|
862
886
|
|
|
863
887
|
// ─── Test: unchecked roadmap + summary counts toward completeMilestoneIds (deps) ────
|
|
864
|
-
|
|
888
|
+
console.log('\n=== unchecked roadmap + summary satisfies dependency ===');
|
|
889
|
+
{
|
|
865
890
|
const base = createFixtureBase();
|
|
866
891
|
try {
|
|
867
892
|
// M001: unchecked roadmap + summary → complete
|
|
@@ -874,16 +899,17 @@ slice: S01
|
|
|
874
899
|
writeFileSync(join(contextDir, 'M002-CONTEXT.md'), '---\ndepends_on:\n - M001\n---\n\n# M002 Context\n\nDepends on M001.');
|
|
875
900
|
|
|
876
901
|
const state = await deriveState(base);
|
|
877
|
-
|
|
902
|
+
assertEq(state.activeMilestone?.id, 'M002', 'M002 is active — M001 dependency satisfied via summary');
|
|
878
903
|
const m002Entry = state.registry.find(e => e.id === 'M002');
|
|
879
|
-
|
|
904
|
+
assertEq(m002Entry?.status, 'active', 'M002 status is active, not pending');
|
|
880
905
|
} finally {
|
|
881
906
|
cleanup(base);
|
|
882
907
|
}
|
|
883
|
-
}
|
|
908
|
+
}
|
|
884
909
|
|
|
885
910
|
// ─── Test: ghost milestone (only META.json) is skipped ───────────────
|
|
886
|
-
|
|
911
|
+
console.log('\n=== ghost milestone (only META.json) is skipped ===');
|
|
912
|
+
{
|
|
887
913
|
const base = createFixtureBase();
|
|
888
914
|
try {
|
|
889
915
|
// Create a ghost milestone directory with only META.json
|
|
@@ -892,20 +918,21 @@ slice: S01
|
|
|
892
918
|
writeFileSync(join(ghostDir, 'META.json'), JSON.stringify({ id: 'M001' }));
|
|
893
919
|
|
|
894
920
|
// isGhostMilestone should detect it
|
|
895
|
-
|
|
921
|
+
assertTrue(isGhostMilestone(base, 'M001'), 'M001 is a ghost milestone');
|
|
896
922
|
|
|
897
923
|
// deriveState should treat this as pre-planning (no real milestones)
|
|
898
924
|
const state = await deriveState(base);
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
925
|
+
assertEq(state.phase, 'pre-planning', 'ghost-only: phase is pre-planning');
|
|
926
|
+
assertEq(state.activeMilestone, null, 'ghost-only: no active milestone');
|
|
927
|
+
assertEq(state.registry.length, 0, 'ghost-only: registry is empty');
|
|
902
928
|
} finally {
|
|
903
929
|
cleanup(base);
|
|
904
930
|
}
|
|
905
|
-
}
|
|
931
|
+
}
|
|
906
932
|
|
|
907
933
|
// ─── Test: ghost milestone skipped when real milestones exist ──────────
|
|
908
|
-
|
|
934
|
+
console.log('\n=== ghost milestone skipped alongside real milestones ===');
|
|
935
|
+
{
|
|
909
936
|
const base = createFixtureBase();
|
|
910
937
|
try {
|
|
911
938
|
// M001: ghost (only META.json)
|
|
@@ -919,19 +946,20 @@ slice: S01
|
|
|
919
946
|
writeFileSync(join(realDir, 'M002-CONTEXT.md'), '# Real Milestone\n\nThis has content.');
|
|
920
947
|
|
|
921
948
|
const state = await deriveState(base);
|
|
922
|
-
|
|
949
|
+
assertEq(state.activeMilestone?.id, 'M002', 'ghost+real: active milestone is M002');
|
|
923
950
|
// Ghost M001 should not appear in the registry
|
|
924
951
|
const m001Entry = state.registry.find(e => e.id === 'M001');
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
952
|
+
assertEq(m001Entry, undefined, 'ghost+real: M001 not in registry');
|
|
953
|
+
assertEq(state.registry.length, 1, 'ghost+real: registry has 1 entry');
|
|
954
|
+
assertEq(state.registry[0]?.status, 'active', 'ghost+real: M002 is active');
|
|
928
955
|
} finally {
|
|
929
956
|
cleanup(base);
|
|
930
957
|
}
|
|
931
|
-
}
|
|
958
|
+
}
|
|
932
959
|
|
|
933
960
|
// ─── Test: zero-slice roadmap → pre-planning, not blocked (#1785) ────
|
|
934
|
-
|
|
961
|
+
console.log('\n=== zero-slice roadmap → pre-planning, not blocked (#1785) ===');
|
|
962
|
+
{
|
|
935
963
|
const base = createFixtureBase();
|
|
936
964
|
try {
|
|
937
965
|
// Write a stub roadmap with zero slices (placeholder text, no slice definitions)
|
|
@@ -939,15 +967,22 @@ slice: S01
|
|
|
939
967
|
|
|
940
968
|
const state = await deriveState(base);
|
|
941
969
|
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
970
|
+
assertEq(state.phase, 'pre-planning', 'phase is pre-planning when roadmap has zero slices');
|
|
971
|
+
assertTrue(state.activeMilestone !== null, 'activeMilestone is set');
|
|
972
|
+
assertEq(state.activeMilestone?.id, 'M001', 'activeMilestone is M001');
|
|
973
|
+
assertEq(state.activeSlice, null, 'activeSlice is null');
|
|
974
|
+
assertEq(state.activeTask, null, 'activeTask is null');
|
|
975
|
+
assertEq(state.blockers.length, 0, 'no blockers reported');
|
|
976
|
+
assertTrue(state.nextAction.includes('M001'), 'nextAction references M001');
|
|
949
977
|
} finally {
|
|
950
978
|
cleanup(base);
|
|
951
979
|
}
|
|
952
|
-
}
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
report();
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
main().catch((error) => {
|
|
986
|
+
console.error(error);
|
|
987
|
+
process.exit(1);
|
|
953
988
|
});
|