gsd-pi 2.43.0-next.8 → 2.44.0-dev.0b97ffd
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -12
- package/dist/cli.js +13 -1
- package/dist/help-text.js +24 -0
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +21 -8
- package/dist/resources/extensions/gsd/auto-prompts.js +130 -51
- package/dist/resources/extensions/gsd/auto-start.js +10 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +16 -2
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
- package/dist/resources/extensions/gsd/dispatch-guard.js +34 -10
- package/dist/resources/extensions/gsd/markdown-renderer.js +7 -5
- package/dist/resources/extensions/gsd/reactive-graph.js +13 -2
- package/dist/resources/extensions/gsd/skill-health.js +3 -1
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +2 -11
- package/dist/resources/extensions/gsd/tools/plan-slice.js +2 -10
- package/dist/resources/extensions/gsd/visualizer-data.js +45 -13
- package/dist/resources/extensions/gsd/workspace-index.js +46 -15
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +18 -18
- 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 +4 -4
- 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 +1 -1
- 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 +18 -18
- 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 +1 -0
- package/dist/web/standalone/.next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/page-b9367c5ae13b99c6.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{main-app-2f2ee7b85712c2bd.js → main-app-fdab67f7802d7832.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.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 +4 -4
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +3 -3
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +11 -34
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts +2 -2
- package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +2 -2
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +4 -4
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +18 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +37 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/fallback-resolver.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/fallback-resolver.js +2 -3
- package/packages/pi-coding-agent/dist/core/fallback-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/fallback-resolver.test.js +12 -2
- package/packages/pi-coding-agent/dist/core/fallback-resolver.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
- package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts +38 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +192 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +255 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +15 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +40 -3
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-commands.d.ts +25 -0
- package/packages/pi-coding-agent/dist/core/package-commands.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/package-commands.js +253 -0
- package/packages/pi-coding-agent/dist/core/package-commands.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/package-commands.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/package-commands.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/package-commands.test.js +225 -0
- package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +4 -0
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
- package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +3 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -0
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +11 -199
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +13 -37
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
- package/packages/pi-coding-agent/src/core/compaction/branch-summarization.ts +2 -2
- package/packages/pi-coding-agent/src/core/compaction/compaction.ts +3 -3
- package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +4 -4
- package/packages/pi-coding-agent/src/core/extensions/index.ts +5 -0
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +23 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
- package/packages/pi-coding-agent/src/core/extensions/types.ts +44 -0
- package/packages/pi-coding-agent/src/core/fallback-resolver.test.ts +15 -2
- package/packages/pi-coding-agent/src/core/fallback-resolver.ts +2 -3
- package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
- package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +274 -0
- package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +288 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +39 -3
- package/packages/pi-coding-agent/src/core/package-commands.test.ts +240 -0
- package/packages/pi-coding-agent/src/core/package-commands.ts +310 -0
- package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
- package/packages/pi-coding-agent/src/core/sdk.ts +4 -0
- 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/index.ts +7 -0
- package/packages/pi-coding-agent/src/main.ts +11 -232
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +22 -7
- package/src/resources/extensions/gsd/auto-prompts.ts +109 -42
- package/src/resources/extensions/gsd/auto-start.ts +14 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +16 -3
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
- package/src/resources/extensions/gsd/dispatch-guard.ts +28 -10
- package/src/resources/extensions/gsd/markdown-renderer.ts +7 -5
- package/src/resources/extensions/gsd/reactive-graph.ts +12 -2
- package/src/resources/extensions/gsd/skill-health.ts +2 -1
- package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
- package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
- package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
- package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +465 -523
- package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
- package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
- package/src/resources/extensions/gsd/tests/auto-stash-merge.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +533 -656
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
- package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
- package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
- package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
- package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
- package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
- package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +38 -59
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +228 -263
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +250 -302
- package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
- package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
- package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +390 -420
- package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
- package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +68 -83
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +152 -183
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
- package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
- package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +164 -180
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
- package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
- package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +27 -29
- package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
- package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +54 -75
- package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +21 -32
- package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +38 -44
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
- package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +54 -60
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
- package/src/resources/extensions/gsd/tests/doctor.test.ts +104 -134
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
- package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
- package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
- package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +30 -42
- package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +198 -206
- package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
- package/src/resources/extensions/gsd/tests/git-service.test.ts +285 -388
- package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
- package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
- package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
- package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +81 -103
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
- package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
- package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -102
- package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
- package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
- package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
- package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
- package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +150 -194
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
- package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +80 -93
- package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
- package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +83 -93
- package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
- package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
- package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +87 -96
- package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +125 -164
- package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +35 -36
- package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
- package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
- package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
- package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
- package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
- package/src/resources/extensions/gsd/tests/parsers.test.ts +546 -611
- package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
- package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
- package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
- package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
- package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
- package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
- package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
- package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
- package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
- package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
- package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
- package/src/resources/extensions/gsd/tests/shared-wal.test.ts +19 -26
- package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
- package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
- package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +9 -11
- package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
- package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
- package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
- package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
- package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
- package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
- package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
- package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
- package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
- package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +1 -18
- package/src/resources/extensions/gsd/tools/plan-slice.ts +1 -15
- package/src/resources/extensions/gsd/visualizer-data.ts +46 -14
- package/src/resources/extensions/gsd/workspace-index.ts +49 -18
- package/dist/web/standalone/.next/static/chunks/app/_not-found/page-e07acdb7dd069836.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/layout-745c6ed5fea5fb06.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/page-801b53eff6e83579.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-e6255954dccfcf0a.js +0 -1
- /package/dist/web/standalone/.next/static/{drUWS0zys9uepCfCwecJv → alS4hoANx0TK4UVZY27da}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{drUWS0zys9uepCfCwecJv → alS4hoANx0TK4UVZY27da}/_ssgManifest.js +0 -0
|
@@ -13,11 +13,12 @@
|
|
|
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';
|
|
16
18
|
import { mkdtempSync, mkdirSync, rmSync, writeFileSync, readFileSync } from 'node:fs';
|
|
17
19
|
import { join } from 'node:path';
|
|
18
20
|
import { tmpdir } from 'node:os';
|
|
19
21
|
|
|
20
|
-
import { createTestContext } from './test-helpers.ts';
|
|
21
22
|
import {
|
|
22
23
|
registerWorker,
|
|
23
24
|
updateWorker,
|
|
@@ -43,8 +44,6 @@ import {
|
|
|
43
44
|
predictRemainingCost,
|
|
44
45
|
} from '../metrics.ts';
|
|
45
46
|
|
|
46
|
-
const { assertEq, assertTrue, assertMatch, report } = createTestContext();
|
|
47
|
-
|
|
48
47
|
// ─── Fixture helpers ──────────────────────────────────────────────────────────
|
|
49
48
|
|
|
50
49
|
function createFixtureBase(): string {
|
|
@@ -83,9 +82,9 @@ function cleanup(base: string): void {
|
|
|
83
82
|
|
|
84
83
|
// ─── E2E: Parallel workers across M001 and M002 ──────────────────────────────
|
|
85
84
|
|
|
86
|
-
console.log("\n=== E2E: Parallel workers across milestones ===");
|
|
87
85
|
|
|
88
|
-
{
|
|
86
|
+
describe('parallel-workers-multi-milestone-e2e', () => {
|
|
87
|
+
test('E2E: Parallel workers across milestones', () => {
|
|
89
88
|
resetWorkerRegistry();
|
|
90
89
|
const base = createFixtureBase();
|
|
91
90
|
|
|
@@ -99,52 +98,49 @@ console.log("\n=== E2E: Parallel workers across milestones ===");
|
|
|
99
98
|
const w2 = registerWorker("researcher", "Research M001 APIs", 1, 3, batch1Id);
|
|
100
99
|
const w3 = registerWorker("worker", "Implement M001 feature", 2, 3, batch1Id);
|
|
101
100
|
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
assert.deepStrictEqual(getActiveWorkers().length, 3, "M001: 3 parallel workers registered");
|
|
102
|
+
assert.ok(hasActiveWorkers(), "M001: has active workers");
|
|
104
103
|
|
|
105
104
|
const batches1 = getWorkerBatches();
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
assert.deepStrictEqual(batches1.size, 1, "M001: single batch");
|
|
106
|
+
assert.deepStrictEqual(batches1.get(batch1Id)!.length, 3, "M001: batch has 3 workers");
|
|
108
107
|
|
|
109
108
|
// Complete M001 workers
|
|
110
109
|
updateWorker(w1, "completed");
|
|
111
110
|
updateWorker(w2, "completed");
|
|
112
111
|
updateWorker(w3, "completed");
|
|
113
|
-
|
|
112
|
+
assert.ok(!hasActiveWorkers(), "M001: no active workers after completion");
|
|
114
113
|
|
|
115
114
|
// Simulate M002 parallel workers (batch 2) — overlapping with M001 cleanup
|
|
116
115
|
const batch2Id = "batch-m002";
|
|
117
116
|
const w4 = registerWorker("scout", "Explore M002 codebase", 0, 2, batch2Id);
|
|
118
117
|
const w5 = registerWorker("worker", "Implement M002 feature", 1, 2, batch2Id);
|
|
119
118
|
|
|
120
|
-
|
|
119
|
+
assert.ok(hasActiveWorkers(), "M002: has active workers");
|
|
121
120
|
const batches2 = getWorkerBatches();
|
|
122
121
|
// M001 workers may still be in cleanup window (5s timeout), M002 workers are active
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
assert.ok(batches2.has(batch2Id), "M002: batch exists");
|
|
123
|
+
assert.deepStrictEqual(batches2.get(batch2Id)!.length, 2, "M002: batch has 2 workers");
|
|
125
124
|
|
|
126
125
|
// One worker fails in M002
|
|
127
126
|
updateWorker(w4, "completed");
|
|
128
127
|
updateWorker(w5, "failed");
|
|
129
|
-
|
|
128
|
+
assert.ok(!hasActiveWorkers(), "M002: no active workers after all finish");
|
|
130
129
|
|
|
131
130
|
// Verify worker statuses reflect correctly
|
|
132
131
|
const allWorkers = getActiveWorkers();
|
|
133
132
|
const m002Workers = allWorkers.filter(w => w.batchId === batch2Id);
|
|
134
133
|
if (m002Workers.length > 0) {
|
|
135
134
|
const failedWorker = m002Workers.find(w => w.status === "failed");
|
|
136
|
-
|
|
137
|
-
|
|
135
|
+
assert.ok(failedWorker !== undefined, "M002: failed worker tracked");
|
|
136
|
+
assert.deepStrictEqual(failedWorker?.agent, "worker", "M002: failed worker is 'worker'");
|
|
138
137
|
}
|
|
139
138
|
|
|
140
139
|
cleanup(base);
|
|
141
|
-
}
|
|
140
|
+
});
|
|
142
141
|
|
|
143
142
|
// ─── E2E: Metrics accumulation across milestones ──────────────────────────────
|
|
144
|
-
|
|
145
|
-
console.log("\n=== E2E: Metrics across milestones ===");
|
|
146
|
-
|
|
147
|
-
{
|
|
143
|
+
test('E2E: Metrics across milestones', () => {
|
|
148
144
|
const base = createFixtureBase();
|
|
149
145
|
|
|
150
146
|
// Build a ledger spanning two milestones
|
|
@@ -175,90 +171,84 @@ console.log("\n=== E2E: Metrics across milestones ===");
|
|
|
175
171
|
|
|
176
172
|
// Verify totals
|
|
177
173
|
const totals = getProjectTotals(loaded.units);
|
|
178
|
-
|
|
174
|
+
assert.deepStrictEqual(totals.units, 13, "metrics: 13 total units across M001+M002");
|
|
179
175
|
const totalCost = loaded.units.reduce((sum, u) => sum + u.cost, 0);
|
|
180
|
-
|
|
176
|
+
assert.ok(Math.abs(totals.cost - totalCost) < 0.001, "metrics: total cost matches sum");
|
|
181
177
|
|
|
182
178
|
// Verify phase aggregation
|
|
183
179
|
const phases = aggregateByPhase(loaded.units);
|
|
184
180
|
const research = phases.find(p => p.phase === "research");
|
|
185
|
-
|
|
186
|
-
|
|
181
|
+
assert.ok(research !== undefined, "metrics: research phase exists");
|
|
182
|
+
assert.deepStrictEqual(research!.units, 2, "metrics: 2 research units (M001 + M002)");
|
|
187
183
|
|
|
188
184
|
const execution = phases.find(p => p.phase === "execution");
|
|
189
|
-
|
|
190
|
-
|
|
185
|
+
assert.ok(execution !== undefined, "metrics: execution phase exists");
|
|
186
|
+
assert.deepStrictEqual(execution!.units, 4, "metrics: 4 execution units across both milestones");
|
|
191
187
|
|
|
192
188
|
// Verify slice aggregation
|
|
193
189
|
const slices = aggregateBySlice(loaded.units);
|
|
194
|
-
|
|
190
|
+
assert.ok(slices.length >= 4, "metrics: at least 4 slice aggregates (M001/S01, M001/S02, M002/S01, milestone-level)");
|
|
195
191
|
|
|
196
192
|
const m001s01 = slices.find(s => s.sliceId === "M001/S01");
|
|
197
|
-
|
|
193
|
+
assert.ok(m001s01 !== undefined, "metrics: M001/S01 slice aggregate exists");
|
|
198
194
|
// M001/S01 has: plan-slice + T01 + T02 + complete-slice = 4 units
|
|
199
|
-
|
|
195
|
+
assert.deepStrictEqual(m001s01!.units, 4, "metrics: M001/S01 has 4 units");
|
|
200
196
|
|
|
201
197
|
// Cost projection
|
|
202
198
|
const projLines = formatCostProjection(slices, 3, 2.0);
|
|
203
|
-
|
|
204
|
-
|
|
199
|
+
assert.ok(projLines.length >= 1, "metrics: cost projection generated");
|
|
200
|
+
assert.match(projLines[0], /Projected remaining/, "metrics: projection line text");
|
|
205
201
|
|
|
206
202
|
cleanup(base);
|
|
207
|
-
}
|
|
203
|
+
});
|
|
208
204
|
|
|
209
205
|
// ─── E2E: Budget alert progression through all thresholds ─────────────────────
|
|
210
|
-
|
|
211
|
-
console.log("\n=== E2E: Budget alert progression 0→75→80→90→100 ===");
|
|
212
|
-
|
|
213
|
-
{
|
|
206
|
+
test('E2E: Budget alert progression 0→75→80→90→100', () => {
|
|
214
207
|
// Simulate spending progression against a $10 budget ceiling
|
|
215
208
|
const ceiling = 10.0;
|
|
216
209
|
|
|
217
210
|
// Start: 50% spent
|
|
218
211
|
let lastLevel = getBudgetAlertLevel(5.0 / ceiling);
|
|
219
|
-
|
|
220
|
-
|
|
212
|
+
assert.deepStrictEqual(lastLevel, 0, "budget: 50% → level 0");
|
|
213
|
+
assert.deepStrictEqual(getNewBudgetAlertLevel(0, 5.0 / ceiling), null, "budget: no alert at 50%");
|
|
221
214
|
|
|
222
215
|
// Spend to 75%
|
|
223
216
|
let newLevel = getNewBudgetAlertLevel(lastLevel, 7.5 / ceiling);
|
|
224
|
-
|
|
217
|
+
assert.deepStrictEqual(newLevel, 75, "budget: alert fires at 75%");
|
|
225
218
|
lastLevel = newLevel!;
|
|
226
219
|
|
|
227
220
|
// Spend to 78% — no alert (between 75 and 80)
|
|
228
|
-
|
|
221
|
+
assert.deepStrictEqual(getNewBudgetAlertLevel(lastLevel, 7.8 / ceiling), null, "budget: no alert at 78%");
|
|
229
222
|
|
|
230
223
|
// Spend to 80% — 80% approach alert
|
|
231
224
|
newLevel = getNewBudgetAlertLevel(lastLevel, 8.0 / ceiling);
|
|
232
|
-
|
|
225
|
+
assert.deepStrictEqual(newLevel, 80, "budget: approach alert fires at 80%");
|
|
233
226
|
lastLevel = newLevel!;
|
|
234
227
|
|
|
235
228
|
// Spend to 85% — no alert (still at 80 level)
|
|
236
|
-
|
|
229
|
+
assert.deepStrictEqual(getNewBudgetAlertLevel(lastLevel, 8.5 / ceiling), null, "budget: no alert at 85%");
|
|
237
230
|
|
|
238
231
|
// Spend to 90%
|
|
239
232
|
newLevel = getNewBudgetAlertLevel(lastLevel, 9.0 / ceiling);
|
|
240
|
-
|
|
233
|
+
assert.deepStrictEqual(newLevel, 90, "budget: alert fires at 90%");
|
|
241
234
|
lastLevel = newLevel!;
|
|
242
235
|
|
|
243
236
|
// Spend to 100%
|
|
244
237
|
newLevel = getNewBudgetAlertLevel(lastLevel, 10.0 / ceiling);
|
|
245
|
-
|
|
238
|
+
assert.deepStrictEqual(newLevel, 100, "budget: alert fires at 100%");
|
|
246
239
|
lastLevel = newLevel!;
|
|
247
240
|
|
|
248
241
|
// Over budget — no re-emission
|
|
249
|
-
|
|
242
|
+
assert.deepStrictEqual(getNewBudgetAlertLevel(lastLevel, 12.0 / ceiling), null, "budget: no re-alert over 100%");
|
|
250
243
|
|
|
251
244
|
// Enforcement at 80% — still "none" (enforcement only at 100%)
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
245
|
+
assert.deepStrictEqual(getBudgetEnforcementAction("pause", 0.80), "none", "budget: no enforcement at 80%");
|
|
246
|
+
assert.deepStrictEqual(getBudgetEnforcementAction("halt", 0.80), "none", "budget: no enforcement at 80%");
|
|
247
|
+
assert.deepStrictEqual(getBudgetEnforcementAction("warn", 0.80), "none", "budget: no enforcement at 80%");
|
|
248
|
+
});
|
|
256
249
|
|
|
257
250
|
// ─── E2E: Budget prediction with multi-milestone cost data ────────────────────
|
|
258
|
-
|
|
259
|
-
console.log("\n=== E2E: Budget prediction across milestones ===");
|
|
260
|
-
|
|
261
|
-
{
|
|
251
|
+
test('E2E: Budget prediction across milestones', () => {
|
|
262
252
|
const units: UnitMetrics[] = [
|
|
263
253
|
makeUnit({ type: "execute-task", id: "M001/S01/T01", cost: 0.10 }),
|
|
264
254
|
makeUnit({ type: "execute-task", id: "M001/S01/T02", cost: 0.15 }),
|
|
@@ -268,30 +258,27 @@ console.log("\n=== E2E: Budget prediction across milestones ===");
|
|
|
268
258
|
];
|
|
269
259
|
|
|
270
260
|
const avgCosts = getAverageCostPerUnitType(units);
|
|
271
|
-
|
|
272
|
-
|
|
261
|
+
assert.ok(avgCosts.has("execute-task"), "prediction: has execute-task average");
|
|
262
|
+
assert.ok(avgCosts.has("plan-slice"), "prediction: has plan-slice average");
|
|
273
263
|
|
|
274
264
|
// Average execute-task cost: (0.10 + 0.15 + 0.20) / 3 = 0.15
|
|
275
265
|
const execAvg = avgCosts.get("execute-task")!;
|
|
276
|
-
|
|
266
|
+
assert.ok(Math.abs(execAvg - 0.15) < 0.001, `prediction: execute-task avg is $0.15 (got ${execAvg})`);
|
|
277
267
|
|
|
278
268
|
// Average plan-slice cost: (0.05 + 0.08) / 2 = 0.065
|
|
279
269
|
const planAvg = avgCosts.get("plan-slice")!;
|
|
280
|
-
|
|
270
|
+
assert.ok(Math.abs(planAvg - 0.065) < 0.001, `prediction: plan-slice avg is $0.065 (got ${planAvg})`);
|
|
281
271
|
|
|
282
272
|
// Predict remaining cost for 3 more execute-tasks and 1 plan-slice
|
|
283
273
|
const remaining = predictRemainingCost(avgCosts, [
|
|
284
274
|
"execute-task", "execute-task", "execute-task", "plan-slice",
|
|
285
275
|
]);
|
|
286
276
|
// Expected: 3 * 0.15 + 1 * 0.065 = 0.515
|
|
287
|
-
|
|
288
|
-
}
|
|
277
|
+
assert.ok(Math.abs(remaining - 0.515) < 0.001, `prediction: remaining cost ~$0.515 (got ${remaining})`);
|
|
278
|
+
});
|
|
289
279
|
|
|
290
280
|
// ─── E2E: Parallel workers + budget alerts combined scenario ──────────────────
|
|
291
|
-
|
|
292
|
-
console.log("\n=== E2E: Combined parallel workers + budget monitoring ===");
|
|
293
|
-
|
|
294
|
-
{
|
|
281
|
+
test('E2E: Combined parallel workers + budget monitoring', () => {
|
|
295
282
|
resetWorkerRegistry();
|
|
296
283
|
|
|
297
284
|
// Simulate a scenario: 3 parallel workers running while budget is at 78%
|
|
@@ -303,34 +290,31 @@ console.log("\n=== E2E: Combined parallel workers + budget monitoring ===");
|
|
|
303
290
|
// Budget is at 78% — no alert yet (between 75 and 80)
|
|
304
291
|
const ceiling = 10.0;
|
|
305
292
|
let lastLevel: ReturnType<typeof getBudgetAlertLevel> = 75; // already got 75% alert
|
|
306
|
-
|
|
307
|
-
|
|
293
|
+
assert.deepStrictEqual(getNewBudgetAlertLevel(lastLevel, 7.8 / ceiling), null, "combined: no alert at 78% with workers running");
|
|
294
|
+
assert.ok(hasActiveWorkers(), "combined: workers running during budget check");
|
|
308
295
|
|
|
309
296
|
// First worker completes, cost rises to 80%
|
|
310
297
|
updateWorker(w1, "completed");
|
|
311
298
|
const level80 = getNewBudgetAlertLevel(lastLevel, 8.0 / ceiling);
|
|
312
|
-
|
|
299
|
+
assert.deepStrictEqual(level80, 80, "combined: 80% approach alert fires after worker completes");
|
|
313
300
|
lastLevel = level80!;
|
|
314
301
|
|
|
315
302
|
// Second worker completes, cost rises to 88%
|
|
316
303
|
updateWorker(w2, "completed");
|
|
317
|
-
|
|
304
|
+
assert.deepStrictEqual(getNewBudgetAlertLevel(lastLevel, 8.8 / ceiling), null, "combined: no alert at 88%");
|
|
318
305
|
|
|
319
306
|
// Third worker completes, cost reaches 90%
|
|
320
307
|
updateWorker(w3, "completed");
|
|
321
308
|
const level90 = getNewBudgetAlertLevel(lastLevel, 9.0 / ceiling);
|
|
322
|
-
|
|
309
|
+
assert.deepStrictEqual(level90, 90, "combined: 90% alert fires after all workers complete");
|
|
323
310
|
|
|
324
|
-
|
|
311
|
+
assert.ok(!hasActiveWorkers(), "combined: no active workers at end");
|
|
325
312
|
|
|
326
313
|
resetWorkerRegistry();
|
|
327
|
-
}
|
|
314
|
+
});
|
|
328
315
|
|
|
329
316
|
// ─── E2E: formatCostProjection with budget ceiling warnings ───────────────────
|
|
330
|
-
|
|
331
|
-
console.log("\n=== E2E: Cost projection ceiling warnings ===");
|
|
332
|
-
|
|
333
|
-
{
|
|
317
|
+
test('E2E: Cost projection ceiling warnings', () => {
|
|
334
318
|
const slices = [
|
|
335
319
|
{ sliceId: "M001/S01", units: 4, tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, cost: 3.0, duration: 10000 },
|
|
336
320
|
{ sliceId: "M001/S02", units: 3, tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, cost: 4.0, duration: 8000 },
|
|
@@ -339,16 +323,15 @@ console.log("\n=== E2E: Cost projection ceiling warnings ===");
|
|
|
339
323
|
|
|
340
324
|
// With ceiling NOT yet reached
|
|
341
325
|
const proj1 = formatCostProjection(slices, 2, 20.0);
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
326
|
+
assert.ok(proj1.length >= 1, "projection: has projection line");
|
|
327
|
+
assert.match(proj1[0], /Projected remaining/, "projection: shows projection");
|
|
328
|
+
assert.ok(proj1.length === 1, "projection: no ceiling warning when under budget");
|
|
345
329
|
|
|
346
330
|
// With ceiling reached (spent 12.0 >= ceiling 10.0)
|
|
347
331
|
const proj2 = formatCostProjection(slices, 2, 10.0);
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
}
|
|
332
|
+
assert.ok(proj2.length >= 2, "projection: has ceiling warning when over budget");
|
|
333
|
+
assert.match(proj2[1], /ceiling/, "projection: ceiling warning text");
|
|
334
|
+
});
|
|
351
335
|
|
|
352
336
|
// ─── Summary ──────────────────────────────────────────────────────────────────
|
|
353
|
-
|
|
354
|
-
report();
|
|
337
|
+
});
|
|
@@ -12,6 +12,8 @@
|
|
|
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';
|
|
15
17
|
import { mkdtempSync, mkdirSync, rmSync, writeFileSync, readFileSync } from 'node:fs';
|
|
16
18
|
import { join } from 'node:path';
|
|
17
19
|
import { tmpdir } from 'node:os';
|
|
@@ -20,16 +22,6 @@ import { deriveState, invalidateStateCache } from '../state.ts';
|
|
|
20
22
|
import { clearPathCache } from '../paths.ts';
|
|
21
23
|
import { parkMilestone, unparkMilestone, discardMilestone } from '../milestone-actions.ts';
|
|
22
24
|
|
|
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
|
-
}
|
|
33
25
|
|
|
34
26
|
function createFixture(): string {
|
|
35
27
|
const b = mkdtempSync(join(tmpdir(), 'gsd-edge-'));
|
|
@@ -61,11 +53,10 @@ function createM(b: string, mid: string, opts?: { roadmap?: boolean; summary?: b
|
|
|
61
53
|
function clear(): void { clearPathCache(); invalidateStateCache(); }
|
|
62
54
|
function cleanup(b: string): void { rmSync(b, { recursive: true, force: true }); }
|
|
63
55
|
|
|
64
|
-
async function main(): Promise<void> {
|
|
65
|
-
|
|
66
56
|
// ─── EDGE 1: Discard breaks depends_on → downstream is BLOCKED ────────
|
|
67
|
-
|
|
68
|
-
|
|
57
|
+
|
|
58
|
+
describe('park-edge-cases', () => {
|
|
59
|
+
test('EDGE 1: Discard breaks depends_on chain', async () => {
|
|
69
60
|
const b = createFixture();
|
|
70
61
|
try {
|
|
71
62
|
createM(b, 'M001', { roadmap: true, summary: true }); // complete
|
|
@@ -78,17 +69,16 @@ async function main(): Promise<void> {
|
|
|
78
69
|
|
|
79
70
|
// M003 depends on M002 which no longer exists.
|
|
80
71
|
// M002 is not in completeMilestoneIds → dep is unmet → M003 stays pending
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
assert(s.blockers.length > 0, 'blockers list is not empty');
|
|
72
|
+
assert.deepStrictEqual(s.registry.find(e => e.id === 'M003')?.status, 'pending', 'M003 stays pending after dep discarded');
|
|
73
|
+
assert.deepStrictEqual(s.phase, 'blocked', 'system is blocked (unmet dep on deleted milestone)');
|
|
74
|
+
assert.ok(s.blockers.length > 0, 'blockers list is not empty');
|
|
84
75
|
} finally {
|
|
85
76
|
cleanup(b);
|
|
86
77
|
}
|
|
87
|
-
|
|
78
|
+
});
|
|
88
79
|
|
|
89
80
|
// ─── EDGE 2: Park blocks depends_on chain ────────────────────────────
|
|
90
|
-
|
|
91
|
-
{
|
|
81
|
+
test('EDGE 2: Park blocks depends_on chain', async () => {
|
|
92
82
|
const b = createFixture();
|
|
93
83
|
try {
|
|
94
84
|
createM(b, 'M001', { roadmap: true, summary: true });
|
|
@@ -98,17 +88,16 @@ async function main(): Promise<void> {
|
|
|
98
88
|
|
|
99
89
|
parkMilestone(b, 'M002', 'testing');
|
|
100
90
|
const s = await deriveState(b);
|
|
101
|
-
|
|
91
|
+
assert.deepStrictEqual(s.registry.find(e => e.id === 'M003')?.status, 'pending', 'M003 pending when M002 parked');
|
|
102
92
|
// System should be blocked since M003 deps unmet and M002 is parked
|
|
103
|
-
assert(s.activeMilestone === null, 'no active milestone (M002 parked, M003 dep-blocked)');
|
|
93
|
+
assert.ok(s.activeMilestone === null, 'no active milestone (M002 parked, M003 dep-blocked)');
|
|
104
94
|
} finally {
|
|
105
95
|
cleanup(b);
|
|
106
96
|
}
|
|
107
|
-
|
|
97
|
+
});
|
|
108
98
|
|
|
109
99
|
// ─── EDGE 3: Discard active, next (no deps) activates ────────────────
|
|
110
|
-
|
|
111
|
-
{
|
|
100
|
+
test('EDGE 3: Discard active → next activates', async () => {
|
|
112
101
|
const b = createFixture();
|
|
113
102
|
try {
|
|
114
103
|
createM(b, 'M001', { roadmap: true });
|
|
@@ -117,16 +106,15 @@ async function main(): Promise<void> {
|
|
|
117
106
|
|
|
118
107
|
discardMilestone(b, 'M001');
|
|
119
108
|
const s = await deriveState(b);
|
|
120
|
-
|
|
121
|
-
assert(s.phase !== 'blocked', 'not blocked');
|
|
109
|
+
assert.deepStrictEqual(s.activeMilestone?.id, 'M002', 'M002 becomes active');
|
|
110
|
+
assert.ok(s.phase !== 'blocked', 'not blocked');
|
|
122
111
|
} finally {
|
|
123
112
|
cleanup(b);
|
|
124
113
|
}
|
|
125
|
-
|
|
114
|
+
});
|
|
126
115
|
|
|
127
116
|
// ─── EDGE 4: Park all + discard all → clean pre-planning ─────────────
|
|
128
|
-
|
|
129
|
-
{
|
|
117
|
+
test('EDGE 4: Park all → discard all → clean state', async () => {
|
|
130
118
|
const b = createFixture();
|
|
131
119
|
try {
|
|
132
120
|
createM(b, 'M001', { roadmap: true });
|
|
@@ -138,30 +126,28 @@ async function main(): Promise<void> {
|
|
|
138
126
|
discardMilestone(b, 'M001');
|
|
139
127
|
discardMilestone(b, 'M002');
|
|
140
128
|
const s = await deriveState(b);
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
assert(s.nextAction.includes('No milestones'), 'nextAction mentions no milestones');
|
|
129
|
+
assert.deepStrictEqual(s.activeMilestone, null, 'no active milestone');
|
|
130
|
+
assert.deepStrictEqual(s.phase, 'pre-planning', 'phase is pre-planning');
|
|
131
|
+
assert.deepStrictEqual(s.registry.length, 0, 'empty registry');
|
|
132
|
+
assert.ok(s.nextAction.includes('No milestones'), 'nextAction mentions no milestones');
|
|
145
133
|
} finally {
|
|
146
134
|
cleanup(b);
|
|
147
135
|
}
|
|
148
|
-
|
|
136
|
+
});
|
|
149
137
|
|
|
150
138
|
// ─── EDGE 5: Discard non-existent → graceful false ───────────────────
|
|
151
|
-
|
|
152
|
-
{
|
|
139
|
+
test('EDGE 5: Discard non-existent', () => {
|
|
153
140
|
const b = createFixture();
|
|
154
141
|
try {
|
|
155
142
|
const result = discardMilestone(b, 'M999');
|
|
156
|
-
assert(!result, 'returns false for non-existent');
|
|
143
|
+
assert.ok(!result, 'returns false for non-existent');
|
|
157
144
|
} finally {
|
|
158
145
|
cleanup(b);
|
|
159
146
|
}
|
|
160
|
-
|
|
147
|
+
});
|
|
161
148
|
|
|
162
149
|
// ─── EDGE 6: Queue order survives discards ───────────────────────────
|
|
163
|
-
|
|
164
|
-
{
|
|
150
|
+
test('EDGE 6: Queue order after discard', async () => {
|
|
165
151
|
const b = createFixture();
|
|
166
152
|
try {
|
|
167
153
|
createM(b, 'M001', { roadmap: true });
|
|
@@ -176,24 +162,23 @@ async function main(): Promise<void> {
|
|
|
176
162
|
|
|
177
163
|
// With custom queue order, M003 should be active first
|
|
178
164
|
let s = await deriveState(b);
|
|
179
|
-
|
|
165
|
+
assert.deepStrictEqual(s.activeMilestone?.id, 'M003', 'M003 active (custom queue order)');
|
|
180
166
|
|
|
181
167
|
// Discard M003 → M001 should be next per queue order
|
|
182
168
|
discardMilestone(b, 'M003');
|
|
183
169
|
s = await deriveState(b);
|
|
184
|
-
|
|
170
|
+
assert.deepStrictEqual(s.activeMilestone?.id, 'M001', 'M001 active after M003 discarded');
|
|
185
171
|
|
|
186
172
|
// Verify queue order file was updated
|
|
187
173
|
const order = JSON.parse(readFileSync(join(b, '.gsd', 'QUEUE-ORDER.json'), 'utf-8'));
|
|
188
|
-
assert(!order.order.includes('M003'), 'M003 removed from QUEUE-ORDER.json');
|
|
174
|
+
assert.ok(!order.order.includes('M003'), 'M003 removed from QUEUE-ORDER.json');
|
|
189
175
|
} finally {
|
|
190
176
|
cleanup(b);
|
|
191
177
|
}
|
|
192
|
-
|
|
178
|
+
});
|
|
193
179
|
|
|
194
180
|
// ─── EDGE 7: Discard milestone that has deps on others ───────────────
|
|
195
|
-
|
|
196
|
-
{
|
|
181
|
+
test('EDGE 7: Discard a milestone that depends on others', async () => {
|
|
197
182
|
const b = createFixture();
|
|
198
183
|
try {
|
|
199
184
|
createM(b, 'M001', { roadmap: true });
|
|
@@ -203,23 +188,22 @@ async function main(): Promise<void> {
|
|
|
203
188
|
|
|
204
189
|
// M002 depends on M001, so M001 is active, M002 is pending
|
|
205
190
|
let s = await deriveState(b);
|
|
206
|
-
|
|
207
|
-
|
|
191
|
+
assert.deepStrictEqual(s.activeMilestone?.id, 'M001', 'M001 is active');
|
|
192
|
+
assert.deepStrictEqual(s.registry.find(e => e.id === 'M002')?.status, 'pending', 'M002 pending (dep on M001)');
|
|
208
193
|
|
|
209
194
|
// Discard M002 (the one WITH deps) — should be fine, M003 becomes pending
|
|
210
195
|
discardMilestone(b, 'M002');
|
|
211
196
|
s = await deriveState(b);
|
|
212
|
-
|
|
213
|
-
assert(!s.registry.some(e => e.id === 'M002'), 'M002 gone from registry');
|
|
214
|
-
|
|
197
|
+
assert.deepStrictEqual(s.activeMilestone?.id, 'M001', 'M001 still active');
|
|
198
|
+
assert.ok(!s.registry.some(e => e.id === 'M002'), 'M002 gone from registry');
|
|
199
|
+
assert.deepStrictEqual(s.registry.find(e => e.id === 'M003')?.status, 'pending', 'M003 is pending (after M001)');
|
|
215
200
|
} finally {
|
|
216
201
|
cleanup(b);
|
|
217
202
|
}
|
|
218
|
-
|
|
203
|
+
});
|
|
219
204
|
|
|
220
205
|
// ─── EDGE 8: Park → Discard → state transitions ─────────────────────
|
|
221
|
-
|
|
222
|
-
{
|
|
206
|
+
test('EDGE 8: Park then discard same milestone', async () => {
|
|
223
207
|
const b = createFixture();
|
|
224
208
|
try {
|
|
225
209
|
createM(b, 'M001', { roadmap: true });
|
|
@@ -228,22 +212,21 @@ async function main(): Promise<void> {
|
|
|
228
212
|
|
|
229
213
|
parkMilestone(b, 'M001', 'temp');
|
|
230
214
|
let s = await deriveState(b);
|
|
231
|
-
|
|
215
|
+
assert.deepStrictEqual(s.activeMilestone?.id, 'M002', 'M002 active while M001 parked');
|
|
232
216
|
|
|
233
217
|
// Now discard the parked milestone
|
|
234
218
|
discardMilestone(b, 'M001');
|
|
235
219
|
s = await deriveState(b);
|
|
236
|
-
|
|
237
|
-
assert(!s.registry.some(e => e.id === 'M001'), 'M001 gone completely');
|
|
238
|
-
|
|
220
|
+
assert.deepStrictEqual(s.activeMilestone?.id, 'M002', 'M002 still active');
|
|
221
|
+
assert.ok(!s.registry.some(e => e.id === 'M001'), 'M001 gone completely');
|
|
222
|
+
assert.deepStrictEqual(s.registry.length, 1, 'only M002 in registry');
|
|
239
223
|
} finally {
|
|
240
224
|
cleanup(b);
|
|
241
225
|
}
|
|
242
|
-
|
|
226
|
+
});
|
|
243
227
|
|
|
244
228
|
// ─── EDGE 9: Complete + parked + pending coexist ─────────────────────
|
|
245
|
-
|
|
246
|
-
{
|
|
229
|
+
test('EDGE 9: Mixed states — complete + parked + active', async () => {
|
|
247
230
|
const b = createFixture();
|
|
248
231
|
try {
|
|
249
232
|
createM(b, 'M001', { roadmap: true, summary: true }); // complete
|
|
@@ -254,23 +237,17 @@ async function main(): Promise<void> {
|
|
|
254
237
|
|
|
255
238
|
parkMilestone(b, 'M002', 'parked');
|
|
256
239
|
const s = await deriveState(b);
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
240
|
+
assert.deepStrictEqual(s.registry.find(e => e.id === 'M001')?.status, 'complete', 'M001 complete');
|
|
241
|
+
assert.deepStrictEqual(s.registry.find(e => e.id === 'M002')?.status, 'parked', 'M002 parked');
|
|
242
|
+
assert.deepStrictEqual(s.registry.find(e => e.id === 'M003')?.status, 'active', 'M003 active');
|
|
243
|
+
assert.deepStrictEqual(s.registry.find(e => e.id === 'M004')?.status, 'pending', 'M004 pending');
|
|
244
|
+
assert.deepStrictEqual(s.activeMilestone?.id, 'M003', 'M003 is the active milestone');
|
|
245
|
+
assert.deepStrictEqual(s.progress?.milestones.done, 1, '1 done');
|
|
246
|
+
assert.deepStrictEqual(s.progress?.milestones.total, 4, '4 total');
|
|
264
247
|
} finally {
|
|
265
248
|
cleanup(b);
|
|
266
249
|
}
|
|
267
|
-
|
|
250
|
+
});
|
|
268
251
|
|
|
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
|
-
}
|
|
252
|
+
});
|
|
275
253
|
|
|
276
|
-
main().catch(e => { console.error(e); process.exit(1); });
|