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
|
@@ -13,12 +13,11 @@
|
|
|
13
13
|
* - Cost projection with budget ceiling awareness
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import { describe, test } from 'node:test';
|
|
17
|
-
import assert from 'node:assert/strict';
|
|
18
16
|
import { mkdtempSync, mkdirSync, rmSync, writeFileSync, readFileSync } from 'node:fs';
|
|
19
17
|
import { join } from 'node:path';
|
|
20
18
|
import { tmpdir } from 'node:os';
|
|
21
19
|
|
|
20
|
+
import { createTestContext } from './test-helpers.ts';
|
|
22
21
|
import {
|
|
23
22
|
registerWorker,
|
|
24
23
|
updateWorker,
|
|
@@ -44,6 +43,8 @@ import {
|
|
|
44
43
|
predictRemainingCost,
|
|
45
44
|
} from '../metrics.ts';
|
|
46
45
|
|
|
46
|
+
const { assertEq, assertTrue, assertMatch, report } = createTestContext();
|
|
47
|
+
|
|
47
48
|
// ─── Fixture helpers ──────────────────────────────────────────────────────────
|
|
48
49
|
|
|
49
50
|
function createFixtureBase(): string {
|
|
@@ -82,9 +83,9 @@ function cleanup(base: string): void {
|
|
|
82
83
|
|
|
83
84
|
// ─── E2E: Parallel workers across M001 and M002 ──────────────────────────────
|
|
84
85
|
|
|
86
|
+
console.log("\n=== E2E: Parallel workers across milestones ===");
|
|
85
87
|
|
|
86
|
-
|
|
87
|
-
test('E2E: Parallel workers across milestones', () => {
|
|
88
|
+
{
|
|
88
89
|
resetWorkerRegistry();
|
|
89
90
|
const base = createFixtureBase();
|
|
90
91
|
|
|
@@ -98,49 +99,52 @@ test('E2E: Parallel workers across milestones', () => {
|
|
|
98
99
|
const w2 = registerWorker("researcher", "Research M001 APIs", 1, 3, batch1Id);
|
|
99
100
|
const w3 = registerWorker("worker", "Implement M001 feature", 2, 3, batch1Id);
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
assertEq(getActiveWorkers().length, 3, "M001: 3 parallel workers registered");
|
|
103
|
+
assertTrue(hasActiveWorkers(), "M001: has active workers");
|
|
103
104
|
|
|
104
105
|
const batches1 = getWorkerBatches();
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
assertEq(batches1.size, 1, "M001: single batch");
|
|
107
|
+
assertEq(batches1.get(batch1Id)!.length, 3, "M001: batch has 3 workers");
|
|
107
108
|
|
|
108
109
|
// Complete M001 workers
|
|
109
110
|
updateWorker(w1, "completed");
|
|
110
111
|
updateWorker(w2, "completed");
|
|
111
112
|
updateWorker(w3, "completed");
|
|
112
|
-
|
|
113
|
+
assertTrue(!hasActiveWorkers(), "M001: no active workers after completion");
|
|
113
114
|
|
|
114
115
|
// Simulate M002 parallel workers (batch 2) — overlapping with M001 cleanup
|
|
115
116
|
const batch2Id = "batch-m002";
|
|
116
117
|
const w4 = registerWorker("scout", "Explore M002 codebase", 0, 2, batch2Id);
|
|
117
118
|
const w5 = registerWorker("worker", "Implement M002 feature", 1, 2, batch2Id);
|
|
118
119
|
|
|
119
|
-
|
|
120
|
+
assertTrue(hasActiveWorkers(), "M002: has active workers");
|
|
120
121
|
const batches2 = getWorkerBatches();
|
|
121
122
|
// M001 workers may still be in cleanup window (5s timeout), M002 workers are active
|
|
122
|
-
|
|
123
|
-
|
|
123
|
+
assertTrue(batches2.has(batch2Id), "M002: batch exists");
|
|
124
|
+
assertEq(batches2.get(batch2Id)!.length, 2, "M002: batch has 2 workers");
|
|
124
125
|
|
|
125
126
|
// One worker fails in M002
|
|
126
127
|
updateWorker(w4, "completed");
|
|
127
128
|
updateWorker(w5, "failed");
|
|
128
|
-
|
|
129
|
+
assertTrue(!hasActiveWorkers(), "M002: no active workers after all finish");
|
|
129
130
|
|
|
130
131
|
// Verify worker statuses reflect correctly
|
|
131
132
|
const allWorkers = getActiveWorkers();
|
|
132
133
|
const m002Workers = allWorkers.filter(w => w.batchId === batch2Id);
|
|
133
134
|
if (m002Workers.length > 0) {
|
|
134
135
|
const failedWorker = m002Workers.find(w => w.status === "failed");
|
|
135
|
-
|
|
136
|
-
|
|
136
|
+
assertTrue(failedWorker !== undefined, "M002: failed worker tracked");
|
|
137
|
+
assertEq(failedWorker?.agent, "worker", "M002: failed worker is 'worker'");
|
|
137
138
|
}
|
|
138
139
|
|
|
139
140
|
cleanup(base);
|
|
140
|
-
}
|
|
141
|
+
}
|
|
141
142
|
|
|
142
143
|
// ─── E2E: Metrics accumulation across milestones ──────────────────────────────
|
|
143
|
-
|
|
144
|
+
|
|
145
|
+
console.log("\n=== E2E: Metrics across milestones ===");
|
|
146
|
+
|
|
147
|
+
{
|
|
144
148
|
const base = createFixtureBase();
|
|
145
149
|
|
|
146
150
|
// Build a ledger spanning two milestones
|
|
@@ -171,84 +175,90 @@ test('E2E: Metrics across milestones', () => {
|
|
|
171
175
|
|
|
172
176
|
// Verify totals
|
|
173
177
|
const totals = getProjectTotals(loaded.units);
|
|
174
|
-
|
|
178
|
+
assertEq(totals.units, 13, "metrics: 13 total units across M001+M002");
|
|
175
179
|
const totalCost = loaded.units.reduce((sum, u) => sum + u.cost, 0);
|
|
176
|
-
|
|
180
|
+
assertTrue(Math.abs(totals.cost - totalCost) < 0.001, "metrics: total cost matches sum");
|
|
177
181
|
|
|
178
182
|
// Verify phase aggregation
|
|
179
183
|
const phases = aggregateByPhase(loaded.units);
|
|
180
184
|
const research = phases.find(p => p.phase === "research");
|
|
181
|
-
|
|
182
|
-
|
|
185
|
+
assertTrue(research !== undefined, "metrics: research phase exists");
|
|
186
|
+
assertEq(research!.units, 2, "metrics: 2 research units (M001 + M002)");
|
|
183
187
|
|
|
184
188
|
const execution = phases.find(p => p.phase === "execution");
|
|
185
|
-
|
|
186
|
-
|
|
189
|
+
assertTrue(execution !== undefined, "metrics: execution phase exists");
|
|
190
|
+
assertEq(execution!.units, 4, "metrics: 4 execution units across both milestones");
|
|
187
191
|
|
|
188
192
|
// Verify slice aggregation
|
|
189
193
|
const slices = aggregateBySlice(loaded.units);
|
|
190
|
-
|
|
194
|
+
assertTrue(slices.length >= 4, "metrics: at least 4 slice aggregates (M001/S01, M001/S02, M002/S01, milestone-level)");
|
|
191
195
|
|
|
192
196
|
const m001s01 = slices.find(s => s.sliceId === "M001/S01");
|
|
193
|
-
|
|
197
|
+
assertTrue(m001s01 !== undefined, "metrics: M001/S01 slice aggregate exists");
|
|
194
198
|
// M001/S01 has: plan-slice + T01 + T02 + complete-slice = 4 units
|
|
195
|
-
|
|
199
|
+
assertEq(m001s01!.units, 4, "metrics: M001/S01 has 4 units");
|
|
196
200
|
|
|
197
201
|
// Cost projection
|
|
198
202
|
const projLines = formatCostProjection(slices, 3, 2.0);
|
|
199
|
-
|
|
200
|
-
|
|
203
|
+
assertTrue(projLines.length >= 1, "metrics: cost projection generated");
|
|
204
|
+
assertMatch(projLines[0], /Projected remaining/, "metrics: projection line text");
|
|
201
205
|
|
|
202
206
|
cleanup(base);
|
|
203
|
-
}
|
|
207
|
+
}
|
|
204
208
|
|
|
205
209
|
// ─── E2E: Budget alert progression through all thresholds ─────────────────────
|
|
206
|
-
|
|
210
|
+
|
|
211
|
+
console.log("\n=== E2E: Budget alert progression 0→75→80→90→100 ===");
|
|
212
|
+
|
|
213
|
+
{
|
|
207
214
|
// Simulate spending progression against a $10 budget ceiling
|
|
208
215
|
const ceiling = 10.0;
|
|
209
216
|
|
|
210
217
|
// Start: 50% spent
|
|
211
218
|
let lastLevel = getBudgetAlertLevel(5.0 / ceiling);
|
|
212
|
-
|
|
213
|
-
|
|
219
|
+
assertEq(lastLevel, 0, "budget: 50% → level 0");
|
|
220
|
+
assertEq(getNewBudgetAlertLevel(0, 5.0 / ceiling), null, "budget: no alert at 50%");
|
|
214
221
|
|
|
215
222
|
// Spend to 75%
|
|
216
223
|
let newLevel = getNewBudgetAlertLevel(lastLevel, 7.5 / ceiling);
|
|
217
|
-
|
|
224
|
+
assertEq(newLevel, 75, "budget: alert fires at 75%");
|
|
218
225
|
lastLevel = newLevel!;
|
|
219
226
|
|
|
220
227
|
// Spend to 78% — no alert (between 75 and 80)
|
|
221
|
-
|
|
228
|
+
assertEq(getNewBudgetAlertLevel(lastLevel, 7.8 / ceiling), null, "budget: no alert at 78%");
|
|
222
229
|
|
|
223
230
|
// Spend to 80% — 80% approach alert
|
|
224
231
|
newLevel = getNewBudgetAlertLevel(lastLevel, 8.0 / ceiling);
|
|
225
|
-
|
|
232
|
+
assertEq(newLevel, 80, "budget: approach alert fires at 80%");
|
|
226
233
|
lastLevel = newLevel!;
|
|
227
234
|
|
|
228
235
|
// Spend to 85% — no alert (still at 80 level)
|
|
229
|
-
|
|
236
|
+
assertEq(getNewBudgetAlertLevel(lastLevel, 8.5 / ceiling), null, "budget: no alert at 85%");
|
|
230
237
|
|
|
231
238
|
// Spend to 90%
|
|
232
239
|
newLevel = getNewBudgetAlertLevel(lastLevel, 9.0 / ceiling);
|
|
233
|
-
|
|
240
|
+
assertEq(newLevel, 90, "budget: alert fires at 90%");
|
|
234
241
|
lastLevel = newLevel!;
|
|
235
242
|
|
|
236
243
|
// Spend to 100%
|
|
237
244
|
newLevel = getNewBudgetAlertLevel(lastLevel, 10.0 / ceiling);
|
|
238
|
-
|
|
245
|
+
assertEq(newLevel, 100, "budget: alert fires at 100%");
|
|
239
246
|
lastLevel = newLevel!;
|
|
240
247
|
|
|
241
248
|
// Over budget — no re-emission
|
|
242
|
-
|
|
249
|
+
assertEq(getNewBudgetAlertLevel(lastLevel, 12.0 / ceiling), null, "budget: no re-alert over 100%");
|
|
243
250
|
|
|
244
251
|
// Enforcement at 80% — still "none" (enforcement only at 100%)
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
}
|
|
252
|
+
assertEq(getBudgetEnforcementAction("pause", 0.80), "none", "budget: no enforcement at 80%");
|
|
253
|
+
assertEq(getBudgetEnforcementAction("halt", 0.80), "none", "budget: no enforcement at 80%");
|
|
254
|
+
assertEq(getBudgetEnforcementAction("warn", 0.80), "none", "budget: no enforcement at 80%");
|
|
255
|
+
}
|
|
249
256
|
|
|
250
257
|
// ─── E2E: Budget prediction with multi-milestone cost data ────────────────────
|
|
251
|
-
|
|
258
|
+
|
|
259
|
+
console.log("\n=== E2E: Budget prediction across milestones ===");
|
|
260
|
+
|
|
261
|
+
{
|
|
252
262
|
const units: UnitMetrics[] = [
|
|
253
263
|
makeUnit({ type: "execute-task", id: "M001/S01/T01", cost: 0.10 }),
|
|
254
264
|
makeUnit({ type: "execute-task", id: "M001/S01/T02", cost: 0.15 }),
|
|
@@ -258,27 +268,30 @@ test('E2E: Budget prediction across milestones', () => {
|
|
|
258
268
|
];
|
|
259
269
|
|
|
260
270
|
const avgCosts = getAverageCostPerUnitType(units);
|
|
261
|
-
|
|
262
|
-
|
|
271
|
+
assertTrue(avgCosts.has("execute-task"), "prediction: has execute-task average");
|
|
272
|
+
assertTrue(avgCosts.has("plan-slice"), "prediction: has plan-slice average");
|
|
263
273
|
|
|
264
274
|
// Average execute-task cost: (0.10 + 0.15 + 0.20) / 3 = 0.15
|
|
265
275
|
const execAvg = avgCosts.get("execute-task")!;
|
|
266
|
-
|
|
276
|
+
assertTrue(Math.abs(execAvg - 0.15) < 0.001, `prediction: execute-task avg is $0.15 (got ${execAvg})`);
|
|
267
277
|
|
|
268
278
|
// Average plan-slice cost: (0.05 + 0.08) / 2 = 0.065
|
|
269
279
|
const planAvg = avgCosts.get("plan-slice")!;
|
|
270
|
-
|
|
280
|
+
assertTrue(Math.abs(planAvg - 0.065) < 0.001, `prediction: plan-slice avg is $0.065 (got ${planAvg})`);
|
|
271
281
|
|
|
272
282
|
// Predict remaining cost for 3 more execute-tasks and 1 plan-slice
|
|
273
283
|
const remaining = predictRemainingCost(avgCosts, [
|
|
274
284
|
"execute-task", "execute-task", "execute-task", "plan-slice",
|
|
275
285
|
]);
|
|
276
286
|
// Expected: 3 * 0.15 + 1 * 0.065 = 0.515
|
|
277
|
-
|
|
278
|
-
}
|
|
287
|
+
assertTrue(Math.abs(remaining - 0.515) < 0.001, `prediction: remaining cost ~$0.515 (got ${remaining})`);
|
|
288
|
+
}
|
|
279
289
|
|
|
280
290
|
// ─── E2E: Parallel workers + budget alerts combined scenario ──────────────────
|
|
281
|
-
|
|
291
|
+
|
|
292
|
+
console.log("\n=== E2E: Combined parallel workers + budget monitoring ===");
|
|
293
|
+
|
|
294
|
+
{
|
|
282
295
|
resetWorkerRegistry();
|
|
283
296
|
|
|
284
297
|
// Simulate a scenario: 3 parallel workers running while budget is at 78%
|
|
@@ -290,31 +303,34 @@ test('E2E: Combined parallel workers + budget monitoring', () => {
|
|
|
290
303
|
// Budget is at 78% — no alert yet (between 75 and 80)
|
|
291
304
|
const ceiling = 10.0;
|
|
292
305
|
let lastLevel: ReturnType<typeof getBudgetAlertLevel> = 75; // already got 75% alert
|
|
293
|
-
|
|
294
|
-
|
|
306
|
+
assertEq(getNewBudgetAlertLevel(lastLevel, 7.8 / ceiling), null, "combined: no alert at 78% with workers running");
|
|
307
|
+
assertTrue(hasActiveWorkers(), "combined: workers running during budget check");
|
|
295
308
|
|
|
296
309
|
// First worker completes, cost rises to 80%
|
|
297
310
|
updateWorker(w1, "completed");
|
|
298
311
|
const level80 = getNewBudgetAlertLevel(lastLevel, 8.0 / ceiling);
|
|
299
|
-
|
|
312
|
+
assertEq(level80, 80, "combined: 80% approach alert fires after worker completes");
|
|
300
313
|
lastLevel = level80!;
|
|
301
314
|
|
|
302
315
|
// Second worker completes, cost rises to 88%
|
|
303
316
|
updateWorker(w2, "completed");
|
|
304
|
-
|
|
317
|
+
assertEq(getNewBudgetAlertLevel(lastLevel, 8.8 / ceiling), null, "combined: no alert at 88%");
|
|
305
318
|
|
|
306
319
|
// Third worker completes, cost reaches 90%
|
|
307
320
|
updateWorker(w3, "completed");
|
|
308
321
|
const level90 = getNewBudgetAlertLevel(lastLevel, 9.0 / ceiling);
|
|
309
|
-
|
|
322
|
+
assertEq(level90, 90, "combined: 90% alert fires after all workers complete");
|
|
310
323
|
|
|
311
|
-
|
|
324
|
+
assertTrue(!hasActiveWorkers(), "combined: no active workers at end");
|
|
312
325
|
|
|
313
326
|
resetWorkerRegistry();
|
|
314
|
-
}
|
|
327
|
+
}
|
|
315
328
|
|
|
316
329
|
// ─── E2E: formatCostProjection with budget ceiling warnings ───────────────────
|
|
317
|
-
|
|
330
|
+
|
|
331
|
+
console.log("\n=== E2E: Cost projection ceiling warnings ===");
|
|
332
|
+
|
|
333
|
+
{
|
|
318
334
|
const slices = [
|
|
319
335
|
{ sliceId: "M001/S01", units: 4, tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, cost: 3.0, duration: 10000 },
|
|
320
336
|
{ sliceId: "M001/S02", units: 3, tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, cost: 4.0, duration: 8000 },
|
|
@@ -323,15 +339,16 @@ test('E2E: Cost projection ceiling warnings', () => {
|
|
|
323
339
|
|
|
324
340
|
// With ceiling NOT yet reached
|
|
325
341
|
const proj1 = formatCostProjection(slices, 2, 20.0);
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
342
|
+
assertTrue(proj1.length >= 1, "projection: has projection line");
|
|
343
|
+
assertMatch(proj1[0], /Projected remaining/, "projection: shows projection");
|
|
344
|
+
assertTrue(proj1.length === 1, "projection: no ceiling warning when under budget");
|
|
329
345
|
|
|
330
346
|
// With ceiling reached (spent 12.0 >= ceiling 10.0)
|
|
331
347
|
const proj2 = formatCostProjection(slices, 2, 10.0);
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
}
|
|
348
|
+
assertTrue(proj2.length >= 2, "projection: has ceiling warning when over budget");
|
|
349
|
+
assertMatch(proj2[1], /ceiling/, "projection: ceiling warning text");
|
|
350
|
+
}
|
|
335
351
|
|
|
336
352
|
// ─── Summary ──────────────────────────────────────────────────────────────────
|
|
337
|
-
|
|
353
|
+
|
|
354
|
+
report();
|
|
@@ -12,8 +12,6 @@
|
|
|
12
12
|
* 8. Discard milestone that has depends_on on others
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { describe, test } from 'node:test';
|
|
16
|
-
import assert from 'node:assert/strict';
|
|
17
15
|
import { mkdtempSync, mkdirSync, rmSync, writeFileSync, readFileSync } from 'node:fs';
|
|
18
16
|
import { join } from 'node:path';
|
|
19
17
|
import { tmpdir } from 'node:os';
|
|
@@ -22,6 +20,16 @@ import { deriveState, invalidateStateCache } from '../state.ts';
|
|
|
22
20
|
import { clearPathCache } from '../paths.ts';
|
|
23
21
|
import { parkMilestone, unparkMilestone, discardMilestone } from '../milestone-actions.ts';
|
|
24
22
|
|
|
23
|
+
let passed = 0;
|
|
24
|
+
let failed = 0;
|
|
25
|
+
|
|
26
|
+
function assert(condition: boolean, message: string): void {
|
|
27
|
+
if (condition) { passed++; } else { failed++; console.error(` FAIL: ${message}`); }
|
|
28
|
+
}
|
|
29
|
+
function assertEq<T>(actual: T, expected: T, message: string): void {
|
|
30
|
+
if (JSON.stringify(actual) === JSON.stringify(expected)) { passed++; }
|
|
31
|
+
else { failed++; console.error(` FAIL: ${message} — expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`); }
|
|
32
|
+
}
|
|
25
33
|
|
|
26
34
|
function createFixture(): string {
|
|
27
35
|
const b = mkdtempSync(join(tmpdir(), 'gsd-edge-'));
|
|
@@ -53,10 +61,11 @@ function createM(b: string, mid: string, opts?: { roadmap?: boolean; summary?: b
|
|
|
53
61
|
function clear(): void { clearPathCache(); invalidateStateCache(); }
|
|
54
62
|
function cleanup(b: string): void { rmSync(b, { recursive: true, force: true }); }
|
|
55
63
|
|
|
56
|
-
|
|
64
|
+
async function main(): Promise<void> {
|
|
57
65
|
|
|
58
|
-
|
|
59
|
-
|
|
66
|
+
// ─── EDGE 1: Discard breaks depends_on → downstream is BLOCKED ────────
|
|
67
|
+
console.log('\n=== EDGE 1: Discard breaks depends_on chain ===');
|
|
68
|
+
{
|
|
60
69
|
const b = createFixture();
|
|
61
70
|
try {
|
|
62
71
|
createM(b, 'M001', { roadmap: true, summary: true }); // complete
|
|
@@ -69,16 +78,17 @@ test('EDGE 1: Discard breaks depends_on chain', async () => {
|
|
|
69
78
|
|
|
70
79
|
// M003 depends on M002 which no longer exists.
|
|
71
80
|
// M002 is not in completeMilestoneIds → dep is unmet → M003 stays pending
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
assert
|
|
81
|
+
assertEq(s.registry.find(e => e.id === 'M003')?.status, 'pending', 'M003 stays pending after dep discarded');
|
|
82
|
+
assertEq(s.phase, 'blocked', 'system is blocked (unmet dep on deleted milestone)');
|
|
83
|
+
assert(s.blockers.length > 0, 'blockers list is not empty');
|
|
75
84
|
} finally {
|
|
76
85
|
cleanup(b);
|
|
77
86
|
}
|
|
78
|
-
}
|
|
87
|
+
}
|
|
79
88
|
|
|
80
89
|
// ─── EDGE 2: Park blocks depends_on chain ────────────────────────────
|
|
81
|
-
|
|
90
|
+
console.log('\n=== EDGE 2: Park blocks depends_on chain ===');
|
|
91
|
+
{
|
|
82
92
|
const b = createFixture();
|
|
83
93
|
try {
|
|
84
94
|
createM(b, 'M001', { roadmap: true, summary: true });
|
|
@@ -88,16 +98,17 @@ test('EDGE 2: Park blocks depends_on chain', async () => {
|
|
|
88
98
|
|
|
89
99
|
parkMilestone(b, 'M002', 'testing');
|
|
90
100
|
const s = await deriveState(b);
|
|
91
|
-
|
|
101
|
+
assertEq(s.registry.find(e => e.id === 'M003')?.status, 'pending', 'M003 pending when M002 parked');
|
|
92
102
|
// System should be blocked since M003 deps unmet and M002 is parked
|
|
93
|
-
assert
|
|
103
|
+
assert(s.activeMilestone === null, 'no active milestone (M002 parked, M003 dep-blocked)');
|
|
94
104
|
} finally {
|
|
95
105
|
cleanup(b);
|
|
96
106
|
}
|
|
97
|
-
}
|
|
107
|
+
}
|
|
98
108
|
|
|
99
109
|
// ─── EDGE 3: Discard active, next (no deps) activates ────────────────
|
|
100
|
-
|
|
110
|
+
console.log('\n=== EDGE 3: Discard active → next activates ===');
|
|
111
|
+
{
|
|
101
112
|
const b = createFixture();
|
|
102
113
|
try {
|
|
103
114
|
createM(b, 'M001', { roadmap: true });
|
|
@@ -106,15 +117,16 @@ test('EDGE 3: Discard active → next activates', async () => {
|
|
|
106
117
|
|
|
107
118
|
discardMilestone(b, 'M001');
|
|
108
119
|
const s = await deriveState(b);
|
|
109
|
-
|
|
110
|
-
assert
|
|
120
|
+
assertEq(s.activeMilestone?.id, 'M002', 'M002 becomes active');
|
|
121
|
+
assert(s.phase !== 'blocked', 'not blocked');
|
|
111
122
|
} finally {
|
|
112
123
|
cleanup(b);
|
|
113
124
|
}
|
|
114
|
-
}
|
|
125
|
+
}
|
|
115
126
|
|
|
116
127
|
// ─── EDGE 4: Park all + discard all → clean pre-planning ─────────────
|
|
117
|
-
|
|
128
|
+
console.log('\n=== EDGE 4: Park all → discard all → clean state ===');
|
|
129
|
+
{
|
|
118
130
|
const b = createFixture();
|
|
119
131
|
try {
|
|
120
132
|
createM(b, 'M001', { roadmap: true });
|
|
@@ -126,28 +138,30 @@ test('EDGE 4: Park all → discard all → clean state', async () => {
|
|
|
126
138
|
discardMilestone(b, 'M001');
|
|
127
139
|
discardMilestone(b, 'M002');
|
|
128
140
|
const s = await deriveState(b);
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
assert
|
|
141
|
+
assertEq(s.activeMilestone, null, 'no active milestone');
|
|
142
|
+
assertEq(s.phase, 'pre-planning', 'phase is pre-planning');
|
|
143
|
+
assertEq(s.registry.length, 0, 'empty registry');
|
|
144
|
+
assert(s.nextAction.includes('No milestones'), 'nextAction mentions no milestones');
|
|
133
145
|
} finally {
|
|
134
146
|
cleanup(b);
|
|
135
147
|
}
|
|
136
|
-
}
|
|
148
|
+
}
|
|
137
149
|
|
|
138
150
|
// ─── EDGE 5: Discard non-existent → graceful false ───────────────────
|
|
139
|
-
|
|
151
|
+
console.log('\n=== EDGE 5: Discard non-existent ===');
|
|
152
|
+
{
|
|
140
153
|
const b = createFixture();
|
|
141
154
|
try {
|
|
142
155
|
const result = discardMilestone(b, 'M999');
|
|
143
|
-
assert
|
|
156
|
+
assert(!result, 'returns false for non-existent');
|
|
144
157
|
} finally {
|
|
145
158
|
cleanup(b);
|
|
146
159
|
}
|
|
147
|
-
}
|
|
160
|
+
}
|
|
148
161
|
|
|
149
162
|
// ─── EDGE 6: Queue order survives discards ───────────────────────────
|
|
150
|
-
|
|
163
|
+
console.log('\n=== EDGE 6: Queue order after discard ===');
|
|
164
|
+
{
|
|
151
165
|
const b = createFixture();
|
|
152
166
|
try {
|
|
153
167
|
createM(b, 'M001', { roadmap: true });
|
|
@@ -162,23 +176,24 @@ test('EDGE 6: Queue order after discard', async () => {
|
|
|
162
176
|
|
|
163
177
|
// With custom queue order, M003 should be active first
|
|
164
178
|
let s = await deriveState(b);
|
|
165
|
-
|
|
179
|
+
assertEq(s.activeMilestone?.id, 'M003', 'M003 active (custom queue order)');
|
|
166
180
|
|
|
167
181
|
// Discard M003 → M001 should be next per queue order
|
|
168
182
|
discardMilestone(b, 'M003');
|
|
169
183
|
s = await deriveState(b);
|
|
170
|
-
|
|
184
|
+
assertEq(s.activeMilestone?.id, 'M001', 'M001 active after M003 discarded');
|
|
171
185
|
|
|
172
186
|
// Verify queue order file was updated
|
|
173
187
|
const order = JSON.parse(readFileSync(join(b, '.gsd', 'QUEUE-ORDER.json'), 'utf-8'));
|
|
174
|
-
assert
|
|
188
|
+
assert(!order.order.includes('M003'), 'M003 removed from QUEUE-ORDER.json');
|
|
175
189
|
} finally {
|
|
176
190
|
cleanup(b);
|
|
177
191
|
}
|
|
178
|
-
}
|
|
192
|
+
}
|
|
179
193
|
|
|
180
194
|
// ─── EDGE 7: Discard milestone that has deps on others ───────────────
|
|
181
|
-
|
|
195
|
+
console.log('\n=== EDGE 7: Discard a milestone that depends on others ===');
|
|
196
|
+
{
|
|
182
197
|
const b = createFixture();
|
|
183
198
|
try {
|
|
184
199
|
createM(b, 'M001', { roadmap: true });
|
|
@@ -188,22 +203,23 @@ test('EDGE 7: Discard a milestone that depends on others', async () => {
|
|
|
188
203
|
|
|
189
204
|
// M002 depends on M001, so M001 is active, M002 is pending
|
|
190
205
|
let s = await deriveState(b);
|
|
191
|
-
|
|
192
|
-
|
|
206
|
+
assertEq(s.activeMilestone?.id, 'M001', 'M001 is active');
|
|
207
|
+
assertEq(s.registry.find(e => e.id === 'M002')?.status, 'pending', 'M002 pending (dep on M001)');
|
|
193
208
|
|
|
194
209
|
// Discard M002 (the one WITH deps) — should be fine, M003 becomes pending
|
|
195
210
|
discardMilestone(b, 'M002');
|
|
196
211
|
s = await deriveState(b);
|
|
197
|
-
|
|
198
|
-
assert
|
|
199
|
-
|
|
212
|
+
assertEq(s.activeMilestone?.id, 'M001', 'M001 still active');
|
|
213
|
+
assert(!s.registry.some(e => e.id === 'M002'), 'M002 gone from registry');
|
|
214
|
+
assertEq(s.registry.find(e => e.id === 'M003')?.status, 'pending', 'M003 is pending (after M001)');
|
|
200
215
|
} finally {
|
|
201
216
|
cleanup(b);
|
|
202
217
|
}
|
|
203
|
-
}
|
|
218
|
+
}
|
|
204
219
|
|
|
205
220
|
// ─── EDGE 8: Park → Discard → state transitions ─────────────────────
|
|
206
|
-
|
|
221
|
+
console.log('\n=== EDGE 8: Park then discard same milestone ===');
|
|
222
|
+
{
|
|
207
223
|
const b = createFixture();
|
|
208
224
|
try {
|
|
209
225
|
createM(b, 'M001', { roadmap: true });
|
|
@@ -212,21 +228,22 @@ test('EDGE 8: Park then discard same milestone', async () => {
|
|
|
212
228
|
|
|
213
229
|
parkMilestone(b, 'M001', 'temp');
|
|
214
230
|
let s = await deriveState(b);
|
|
215
|
-
|
|
231
|
+
assertEq(s.activeMilestone?.id, 'M002', 'M002 active while M001 parked');
|
|
216
232
|
|
|
217
233
|
// Now discard the parked milestone
|
|
218
234
|
discardMilestone(b, 'M001');
|
|
219
235
|
s = await deriveState(b);
|
|
220
|
-
|
|
221
|
-
assert
|
|
222
|
-
|
|
236
|
+
assertEq(s.activeMilestone?.id, 'M002', 'M002 still active');
|
|
237
|
+
assert(!s.registry.some(e => e.id === 'M001'), 'M001 gone completely');
|
|
238
|
+
assertEq(s.registry.length, 1, 'only M002 in registry');
|
|
223
239
|
} finally {
|
|
224
240
|
cleanup(b);
|
|
225
241
|
}
|
|
226
|
-
}
|
|
242
|
+
}
|
|
227
243
|
|
|
228
244
|
// ─── EDGE 9: Complete + parked + pending coexist ─────────────────────
|
|
229
|
-
|
|
245
|
+
console.log('\n=== EDGE 9: Mixed states — complete + parked + active ===');
|
|
246
|
+
{
|
|
230
247
|
const b = createFixture();
|
|
231
248
|
try {
|
|
232
249
|
createM(b, 'M001', { roadmap: true, summary: true }); // complete
|
|
@@ -237,17 +254,23 @@ test('EDGE 9: Mixed states — complete + parked + active', async () => {
|
|
|
237
254
|
|
|
238
255
|
parkMilestone(b, 'M002', 'parked');
|
|
239
256
|
const s = await deriveState(b);
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
257
|
+
assertEq(s.registry.find(e => e.id === 'M001')?.status, 'complete', 'M001 complete');
|
|
258
|
+
assertEq(s.registry.find(e => e.id === 'M002')?.status, 'parked', 'M002 parked');
|
|
259
|
+
assertEq(s.registry.find(e => e.id === 'M003')?.status, 'active', 'M003 active');
|
|
260
|
+
assertEq(s.registry.find(e => e.id === 'M004')?.status, 'pending', 'M004 pending');
|
|
261
|
+
assertEq(s.activeMilestone?.id, 'M003', 'M003 is the active milestone');
|
|
262
|
+
assertEq(s.progress?.milestones.done, 1, '1 done');
|
|
263
|
+
assertEq(s.progress?.milestones.total, 4, '4 total');
|
|
247
264
|
} finally {
|
|
248
265
|
cleanup(b);
|
|
249
266
|
}
|
|
250
|
-
}
|
|
267
|
+
}
|
|
251
268
|
|
|
252
|
-
|
|
269
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
270
|
+
console.log(`\n${'='.repeat(50)}`);
|
|
271
|
+
console.log(`Results: ${passed} passed, ${failed} failed`);
|
|
272
|
+
if (failed > 0) process.exit(1);
|
|
273
|
+
else console.log('All edge cases passed!');
|
|
274
|
+
}
|
|
253
275
|
|
|
276
|
+
main().catch(e => { console.error(e); process.exit(1); });
|