gsd-pi 2.63.0 → 2.64.0-dev.9c14bd0
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 +46 -134
- package/dist/cli.js +48 -6
- package/dist/headless-query.js +11 -1
- package/dist/help-text.js +4 -1
- package/dist/onboarding.js +15 -8
- package/dist/resource-loader.js +18 -3
- package/dist/resources/extensions/cmux/index.js +21 -12
- package/dist/resources/extensions/gsd/auto/detect-stuck.js +27 -0
- package/dist/resources/extensions/gsd/auto/finalize-timeout.js +40 -0
- package/dist/resources/extensions/gsd/auto/loop.js +4 -0
- package/dist/resources/extensions/gsd/auto/phases.js +157 -22
- package/dist/resources/extensions/gsd/auto/session.js +12 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +14 -8
- package/dist/resources/extensions/gsd/auto-model-selection.js +32 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +222 -11
- package/dist/resources/extensions/gsd/auto-prompts.js +25 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +15 -7
- package/dist/resources/extensions/gsd/auto-start.js +10 -21
- package/dist/resources/extensions/gsd/auto-timers.js +2 -1
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +17 -0
- package/dist/resources/extensions/gsd/auto-verification.js +138 -1
- package/dist/resources/extensions/gsd/auto-worktree.js +13 -7
- package/dist/resources/extensions/gsd/auto.js +24 -2
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +147 -75
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +13 -0
- package/dist/resources/extensions/gsd/bootstrap/query-tools.js +85 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +3 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +32 -1
- package/dist/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.js +54 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +30 -2
- package/dist/resources/extensions/gsd/commands-handlers.js +9 -4
- package/dist/resources/extensions/gsd/constants.js +42 -0
- package/dist/resources/extensions/gsd/db-writer.js +72 -4
- package/dist/resources/extensions/gsd/forensics.js +20 -4
- package/dist/resources/extensions/gsd/gsd-db.js +64 -17
- package/dist/resources/extensions/gsd/guided-flow.js +19 -0
- package/dist/resources/extensions/gsd/metrics.js +27 -1
- package/dist/resources/extensions/gsd/native-git-bridge.js +5 -3
- package/dist/resources/extensions/gsd/post-execution-checks.js +407 -0
- package/dist/resources/extensions/gsd/pre-execution-checks.js +464 -0
- package/dist/resources/extensions/gsd/preferences-types.js +6 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +33 -0
- package/dist/resources/extensions/gsd/preferences.js +11 -2
- package/dist/resources/extensions/gsd/prompt-loader.js +7 -0
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -0
- package/dist/resources/extensions/gsd/prompts/doctor-heal.md +1 -0
- package/dist/resources/extensions/gsd/prompts/forensics.md +2 -0
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +2 -0
- package/dist/resources/extensions/gsd/prompts/system.md +4 -7
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
- package/dist/resources/extensions/gsd/roadmap-mutations.js +1 -1
- package/dist/resources/extensions/gsd/roadmap-slices.js +9 -5
- package/dist/resources/extensions/gsd/safety/content-validator.js +73 -0
- package/dist/resources/extensions/gsd/safety/destructive-guard.js +34 -0
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +109 -0
- package/dist/resources/extensions/gsd/safety/evidence-cross-ref.js +83 -0
- package/dist/resources/extensions/gsd/safety/file-change-validator.js +71 -0
- package/dist/resources/extensions/gsd/safety/git-checkpoint.js +91 -0
- package/dist/resources/extensions/gsd/safety/safety-harness.js +64 -0
- package/dist/resources/extensions/gsd/slice-parallel-conflict.js +67 -0
- package/dist/resources/extensions/gsd/slice-parallel-eligibility.js +51 -0
- package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +378 -0
- package/dist/resources/extensions/gsd/state.js +74 -14
- package/dist/resources/extensions/gsd/status-guards.js +11 -0
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +17 -12
- package/dist/resources/extensions/gsd/tools/complete-slice.js +40 -26
- package/dist/resources/extensions/gsd/tools/complete-task.js +12 -12
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +33 -25
- package/dist/resources/extensions/gsd/tools/plan-slice.js +5 -8
- package/dist/resources/extensions/gsd/verification-evidence.js +18 -0
- package/dist/resources/extensions/gsd/workflow-projections.js +21 -5
- package/dist/resources/extensions/gsd/worktree-manager.js +82 -29
- package/dist/resources/extensions/gsd/worktree-resolver.js +4 -3
- package/dist/resources/extensions/mcp-client/auth.js +101 -0
- package/dist/resources/extensions/mcp-client/index.js +10 -1
- package/dist/resources/extensions/ollama/index.js +28 -22
- package/dist/resources/extensions/ollama/model-capabilities.js +37 -34
- package/dist/resources/extensions/ollama/ndjson-stream.js +54 -0
- package/dist/resources/extensions/ollama/ollama-chat-provider.js +380 -0
- package/dist/resources/extensions/ollama/ollama-client.js +23 -32
- package/dist/resources/extensions/ollama/ollama-discovery.js +2 -7
- package/dist/resources/extensions/ollama/ollama-tool.js +62 -0
- package/dist/resources/extensions/ollama/thinking-parser.js +104 -0
- package/dist/update-cmd.js +4 -2
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
- 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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +1 -1
- 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.js.nft.json +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.js.nft.json +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.js.nft.json +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/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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 +2 -2
- 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.js.nft.json +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.js.nft.json +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.js.nft.json +1 -1
- 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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +1 -1
- 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.js.nft.json +1 -1
- 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.js.nft.json +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.js.nft.json +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 +15 -15
- package/dist/web/standalone/.next/server/chunks/6897.js +12 -0
- package/dist/web/standalone/.next/server/chunks/7471.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-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-0c485498795110d6.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
- package/dist/web/standalone/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/dist/welcome-screen.js +1 -1
- package/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.d.ts +8 -0
- package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +50 -0
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.test.ts +221 -5
- package/packages/pi-agent-core/src/agent-loop.ts +53 -0
- package/packages/pi-ai/dist/types.d.ts +16 -1
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/src/types.ts +18 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +50 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +41 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +7 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +31 -4
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.test.js +28 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.js +46 -0
- package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -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/model-registry.d.ts +1 -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 +12 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +3 -3
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +23 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js +80 -56
- package/packages/pi-coding-agent/dist/core/resource-loader.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 +9 -0
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +53 -0
- package/packages/pi-coding-agent/src/core/auth-storage.ts +66 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.test.ts +39 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +34 -4
- package/packages/pi-coding-agent/src/core/extensions/provider-registration.test.ts +81 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +14 -0
- package/packages/pi-coding-agent/src/core/model-resolver.ts +3 -3
- package/packages/pi-coding-agent/src/core/resource-loader.ts +89 -56
- package/packages/pi-coding-agent/src/core/sdk.ts +10 -0
- package/pkg/package.json +1 -1
- package/src/resources/extensions/cmux/index.ts +18 -12
- package/src/resources/extensions/gsd/auto/detect-stuck.ts +27 -0
- package/src/resources/extensions/gsd/auto/finalize-timeout.ts +46 -0
- package/src/resources/extensions/gsd/auto/loop.ts +5 -0
- package/src/resources/extensions/gsd/auto/phases.ts +194 -33
- package/src/resources/extensions/gsd/auto/session.ts +14 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +16 -7
- package/src/resources/extensions/gsd/auto-model-selection.ts +36 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +263 -12
- package/src/resources/extensions/gsd/auto-prompts.ts +21 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +9 -8
- package/src/resources/extensions/gsd/auto-start.ts +11 -20
- package/src/resources/extensions/gsd/auto-timers.ts +2 -1
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +19 -0
- package/src/resources/extensions/gsd/auto-verification.ts +190 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +14 -6
- package/src/resources/extensions/gsd/auto.ts +26 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +160 -88
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +15 -0
- package/src/resources/extensions/gsd/bootstrap/query-tools.ts +98 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +36 -1
- package/src/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.ts +57 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +31 -2
- package/src/resources/extensions/gsd/commands-handlers.ts +10 -4
- package/src/resources/extensions/gsd/constants.ts +44 -0
- package/src/resources/extensions/gsd/db-writer.ts +78 -4
- package/src/resources/extensions/gsd/forensics.ts +21 -5
- package/src/resources/extensions/gsd/gsd-db.ts +64 -17
- package/src/resources/extensions/gsd/guided-flow.ts +22 -0
- package/src/resources/extensions/gsd/metrics.ts +28 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +5 -3
- package/src/resources/extensions/gsd/post-execution-checks.ts +539 -0
- package/src/resources/extensions/gsd/pre-execution-checks.ts +573 -0
- package/src/resources/extensions/gsd/preferences-types.ts +44 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +33 -0
- package/src/resources/extensions/gsd/preferences.ts +13 -2
- package/src/resources/extensions/gsd/prompt-loader.ts +8 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -0
- package/src/resources/extensions/gsd/prompts/doctor-heal.md +1 -0
- package/src/resources/extensions/gsd/prompts/forensics.md +2 -0
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +2 -0
- package/src/resources/extensions/gsd/prompts/system.md +4 -7
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
- package/src/resources/extensions/gsd/roadmap-mutations.ts +1 -1
- package/src/resources/extensions/gsd/roadmap-slices.ts +10 -5
- package/src/resources/extensions/gsd/safety/content-validator.ts +98 -0
- package/src/resources/extensions/gsd/safety/destructive-guard.ts +49 -0
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +151 -0
- package/src/resources/extensions/gsd/safety/evidence-cross-ref.ts +120 -0
- package/src/resources/extensions/gsd/safety/file-change-validator.ts +108 -0
- package/src/resources/extensions/gsd/safety/git-checkpoint.ts +106 -0
- package/src/resources/extensions/gsd/safety/safety-harness.ts +105 -0
- package/src/resources/extensions/gsd/slice-parallel-conflict.ts +86 -0
- package/src/resources/extensions/gsd/slice-parallel-eligibility.ts +73 -0
- package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +477 -0
- package/src/resources/extensions/gsd/state.ts +67 -12
- package/src/resources/extensions/gsd/status-guards.ts +13 -0
- package/src/resources/extensions/gsd/tests/artifact-corruption-2630.test.ts +288 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +34 -13
- package/src/resources/extensions/gsd/tests/auto-start-time-persistence.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/cmux.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/complete-slice-string-coercion.test.ts +211 -0
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/db-access-guardrails.test.ts +109 -0
- package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +13 -9
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +134 -0
- package/src/resources/extensions/gsd/tests/deferred-slice-dispatch.test.ts +203 -0
- package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +130 -0
- package/src/resources/extensions/gsd/tests/doctor-fix-flag.test.ts +92 -0
- package/src/resources/extensions/gsd/tests/enhanced-verification-integration.test.ts +526 -0
- package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +116 -0
- package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/git-checkpoint.test.ts +94 -0
- package/src/resources/extensions/gsd/tests/insert-slice-no-wipe.test.ts +88 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +27 -7
- package/src/resources/extensions/gsd/tests/integration/idle-recovery.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/metrics.test.ts +116 -1
- package/src/resources/extensions/gsd/tests/milestone-status-tool.test.ts +201 -0
- package/src/resources/extensions/gsd/tests/plan-milestone-title.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +82 -18
- package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +312 -0
- package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +813 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +999 -0
- package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +266 -0
- package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +457 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/shared-wal.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/slice-parallel-conflict.test.ts +92 -0
- package/src/resources/extensions/gsd/tests/slice-parallel-eligibility.test.ts +95 -0
- package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +83 -0
- package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +349 -0
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +35 -2
- package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +73 -0
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +148 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +34 -20
- package/src/resources/extensions/gsd/tools/complete-slice.ts +41 -26
- package/src/resources/extensions/gsd/tools/complete-task.ts +12 -12
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +55 -30
- package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -8
- package/src/resources/extensions/gsd/types.ts +44 -22
- package/src/resources/extensions/gsd/verification-evidence.ts +68 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +2 -1
- package/src/resources/extensions/gsd/workflow-projections.ts +23 -5
- package/src/resources/extensions/gsd/worktree-manager.ts +76 -28
- package/src/resources/extensions/gsd/worktree-resolver.ts +4 -3
- package/src/resources/extensions/mcp-client/auth.ts +149 -0
- package/src/resources/extensions/mcp-client/index.ts +16 -1
- package/src/resources/extensions/ollama/index.ts +26 -25
- package/src/resources/extensions/ollama/model-capabilities.ts +41 -34
- package/src/resources/extensions/ollama/ndjson-stream.ts +63 -0
- package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +20 -0
- package/src/resources/extensions/ollama/ollama-chat-provider.ts +459 -0
- package/src/resources/extensions/ollama/ollama-client.ts +30 -30
- package/src/resources/extensions/ollama/ollama-discovery.ts +5 -8
- package/src/resources/extensions/ollama/ollama-tool.ts +69 -0
- package/src/resources/extensions/ollama/tests/ollama-chat-provider-stream.test.ts +82 -0
- package/src/resources/extensions/ollama/tests/ollama-discovery.test.ts +0 -27
- package/src/resources/extensions/ollama/thinking-parser.ts +116 -0
- package/src/resources/extensions/ollama/types.ts +23 -0
- package/dist/web/standalone/.next/server/chunks/2229.js +0 -12
- package/dist/web/standalone/.next/static/chunks/app/page-62be3b5fa91e4c8f.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
- /package/dist/web/standalone/.next/static/{5FLUBNdqolRyyehCyChPd → SoxM61WC_ia7R2gk4VMpJ}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{5FLUBNdqolRyyehCyChPd → SoxM61WC_ia7R2gk4VMpJ}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// GSD2 — Tests for gsd_milestone_status read-only query tool
|
|
2
|
+
|
|
3
|
+
import test from "node:test";
|
|
4
|
+
import assert from "node:assert/strict";
|
|
5
|
+
import { mkdirSync, rmSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { tmpdir } from "node:os";
|
|
8
|
+
import { randomUUID } from "node:crypto";
|
|
9
|
+
|
|
10
|
+
import { registerQueryTools } from "../bootstrap/query-tools.ts";
|
|
11
|
+
import {
|
|
12
|
+
openDatabase,
|
|
13
|
+
closeDatabase,
|
|
14
|
+
_getAdapter,
|
|
15
|
+
} from "../gsd-db.ts";
|
|
16
|
+
|
|
17
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
function makeMockPi() {
|
|
20
|
+
const tools: any[] = [];
|
|
21
|
+
return {
|
|
22
|
+
registerTool: (tool: any) => tools.push(tool),
|
|
23
|
+
tools,
|
|
24
|
+
} as any;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function makeTmpBase(): string {
|
|
28
|
+
const base = join(tmpdir(), `gsd-query-tool-test-${randomUUID()}`);
|
|
29
|
+
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
30
|
+
return base;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function cleanup(base: string): void {
|
|
34
|
+
try { rmSync(base, { recursive: true, force: true }); } catch { /* swallow */ }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function openTestDb(base: string): void {
|
|
38
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function executeToolInDir(tool: any, params: Record<string, unknown>, dir: string) {
|
|
42
|
+
const originalCwd = process.cwd();
|
|
43
|
+
try {
|
|
44
|
+
process.chdir(dir);
|
|
45
|
+
return await tool.execute("test-call-id", params, undefined, undefined, undefined);
|
|
46
|
+
} finally {
|
|
47
|
+
process.chdir(originalCwd);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ─── Seed helpers ─────────────────────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
function seedMilestone(milestoneId: string, title: string, status = "active"): void {
|
|
54
|
+
const db = _getAdapter();
|
|
55
|
+
if (!db) throw new Error("DB not open");
|
|
56
|
+
db.prepare(
|
|
57
|
+
"INSERT OR REPLACE INTO milestones (id, title, status, created_at) VALUES (?, ?, ?, ?)",
|
|
58
|
+
).run(milestoneId, title, status, new Date().toISOString());
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function seedSlice(milestoneId: string, sliceId: string, status: string): void {
|
|
62
|
+
const db = _getAdapter();
|
|
63
|
+
if (!db) throw new Error("DB not open");
|
|
64
|
+
db.prepare(
|
|
65
|
+
"INSERT OR REPLACE INTO slices (milestone_id, id, title, status, created_at) VALUES (?, ?, ?, ?, ?)",
|
|
66
|
+
).run(milestoneId, sliceId, `Slice ${sliceId}`, status, new Date().toISOString());
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function seedTask(milestoneId: string, sliceId: string, taskId: string, status: string): void {
|
|
70
|
+
const db = _getAdapter();
|
|
71
|
+
if (!db) throw new Error("DB not open");
|
|
72
|
+
db.prepare(
|
|
73
|
+
"INSERT OR REPLACE INTO tasks (milestone_id, slice_id, id, title, status) VALUES (?, ?, ?, ?, ?)",
|
|
74
|
+
).run(milestoneId, sliceId, taskId, `Task ${taskId}`, status);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ─── Registration ─────────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
test("registerQueryTools registers gsd_milestone_status tool", () => {
|
|
80
|
+
const pi = makeMockPi();
|
|
81
|
+
registerQueryTools(pi);
|
|
82
|
+
assert.equal(pi.tools.length, 1, "Should register exactly one tool");
|
|
83
|
+
assert.equal(pi.tools[0].name, "gsd_milestone_status");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("gsd_milestone_status has promptGuidelines mentioning prohibited alternatives", () => {
|
|
87
|
+
const pi = makeMockPi();
|
|
88
|
+
registerQueryTools(pi);
|
|
89
|
+
const tool = pi.tools[0];
|
|
90
|
+
assert.ok(Array.isArray(tool.promptGuidelines), "promptGuidelines must be an array");
|
|
91
|
+
assert.ok(tool.promptGuidelines.length >= 1, "Must have at least one guideline");
|
|
92
|
+
const joined = tool.promptGuidelines.join(" ");
|
|
93
|
+
assert.match(joined, /sqlite3|better-sqlite3/, "Guidelines must mention prohibited alternatives");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// ─── Happy path: milestone with slices and tasks ──────────────────────────────
|
|
97
|
+
|
|
98
|
+
test("gsd_milestone_status returns milestone metadata and slice statuses", async () => {
|
|
99
|
+
const base = makeTmpBase();
|
|
100
|
+
try {
|
|
101
|
+
openTestDb(base);
|
|
102
|
+
seedMilestone("M001", "Test Milestone");
|
|
103
|
+
seedSlice("M001", "S01", "complete");
|
|
104
|
+
seedSlice("M001", "S02", "active");
|
|
105
|
+
seedTask("M001", "S01", "T01", "done");
|
|
106
|
+
seedTask("M001", "S01", "T02", "done");
|
|
107
|
+
seedTask("M001", "S02", "T01", "pending");
|
|
108
|
+
|
|
109
|
+
const pi = makeMockPi();
|
|
110
|
+
registerQueryTools(pi);
|
|
111
|
+
const tool = pi.tools[0];
|
|
112
|
+
|
|
113
|
+
const result = await executeToolInDir(tool, { milestoneId: "M001" }, base);
|
|
114
|
+
const parsed = JSON.parse(result.content[0].text);
|
|
115
|
+
|
|
116
|
+
assert.equal(parsed.milestoneId, "M001");
|
|
117
|
+
assert.equal(parsed.title, "Test Milestone");
|
|
118
|
+
assert.equal(parsed.status, "active");
|
|
119
|
+
assert.equal(parsed.sliceCount, 2);
|
|
120
|
+
assert.equal(parsed.slices.length, 2);
|
|
121
|
+
|
|
122
|
+
const s01 = parsed.slices.find((s: any) => s.id === "S01");
|
|
123
|
+
assert.ok(s01, "S01 should be in slices");
|
|
124
|
+
assert.equal(s01.status, "complete");
|
|
125
|
+
assert.equal(s01.taskCounts.total, 2);
|
|
126
|
+
assert.equal(s01.taskCounts.done, 2);
|
|
127
|
+
|
|
128
|
+
const s02 = parsed.slices.find((s: any) => s.id === "S02");
|
|
129
|
+
assert.ok(s02, "S02 should be in slices");
|
|
130
|
+
assert.equal(s02.status, "active");
|
|
131
|
+
assert.equal(s02.taskCounts.pending, 1);
|
|
132
|
+
} finally {
|
|
133
|
+
closeDatabase();
|
|
134
|
+
cleanup(base);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// ─── Milestone with no slices ─────────────────────────────────────────────────
|
|
139
|
+
|
|
140
|
+
test("gsd_milestone_status returns empty slices array for milestone with no slices", async () => {
|
|
141
|
+
const base = makeTmpBase();
|
|
142
|
+
try {
|
|
143
|
+
openTestDb(base);
|
|
144
|
+
seedMilestone("M002", "Empty Milestone");
|
|
145
|
+
|
|
146
|
+
const pi = makeMockPi();
|
|
147
|
+
registerQueryTools(pi);
|
|
148
|
+
const tool = pi.tools[0];
|
|
149
|
+
|
|
150
|
+
const result = await executeToolInDir(tool, { milestoneId: "M002" }, base);
|
|
151
|
+
const parsed = JSON.parse(result.content[0].text);
|
|
152
|
+
|
|
153
|
+
assert.equal(parsed.milestoneId, "M002");
|
|
154
|
+
assert.equal(parsed.sliceCount, 0);
|
|
155
|
+
assert.deepEqual(parsed.slices, []);
|
|
156
|
+
} finally {
|
|
157
|
+
closeDatabase();
|
|
158
|
+
cleanup(base);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// ─── Missing milestone ────────────────────────────────────────────────────────
|
|
163
|
+
|
|
164
|
+
test("gsd_milestone_status returns not-found for missing milestone", async () => {
|
|
165
|
+
const base = makeTmpBase();
|
|
166
|
+
try {
|
|
167
|
+
openTestDb(base);
|
|
168
|
+
|
|
169
|
+
const pi = makeMockPi();
|
|
170
|
+
registerQueryTools(pi);
|
|
171
|
+
const tool = pi.tools[0];
|
|
172
|
+
|
|
173
|
+
const result = await executeToolInDir(tool, { milestoneId: "M999" }, base);
|
|
174
|
+
assert.match(result.content[0].text, /M999.*not found/i);
|
|
175
|
+
assert.equal(result.details.found, false);
|
|
176
|
+
} finally {
|
|
177
|
+
closeDatabase();
|
|
178
|
+
cleanup(base);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// ─── DB unavailable ───────────────────────────────────────────────────────────
|
|
183
|
+
|
|
184
|
+
test("gsd_milestone_status handles missing DB gracefully", async () => {
|
|
185
|
+
// Create a directory without .gsd/ to ensure ensureDbOpen has nothing to open
|
|
186
|
+
const base = join(tmpdir(), `gsd-no-db-${randomUUID()}`);
|
|
187
|
+
mkdirSync(base, { recursive: true });
|
|
188
|
+
closeDatabase(); // ensure no prior DB is open
|
|
189
|
+
try {
|
|
190
|
+
const pi = makeMockPi();
|
|
191
|
+
registerQueryTools(pi);
|
|
192
|
+
const tool = pi.tools[0];
|
|
193
|
+
|
|
194
|
+
const result = await executeToolInDir(tool, { milestoneId: "M001" }, base);
|
|
195
|
+
assert.match(result.content[0].text, /GSD database is not available/);
|
|
196
|
+
assert.equal(result.details.error, "db_unavailable");
|
|
197
|
+
} finally {
|
|
198
|
+
closeDatabase();
|
|
199
|
+
cleanup(base);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
@@ -38,8 +38,9 @@ test("upsertMilestonePlanning updates title when DB row pre-exists with empty ti
|
|
|
38
38
|
|
|
39
39
|
// Step 3: upsertMilestonePlanning should update the title
|
|
40
40
|
upsertMilestonePlanning("M099", {
|
|
41
|
+
title: "My Important Milestone",
|
|
41
42
|
vision: "Test vision",
|
|
42
|
-
}
|
|
43
|
+
});
|
|
43
44
|
const afterUpsert = getMilestone("M099");
|
|
44
45
|
assert.ok(afterUpsert);
|
|
45
46
|
assert.equal(
|
|
@@ -4,7 +4,7 @@ import { mkdtempSync, mkdirSync, rmSync, readFileSync, existsSync, writeFileSync
|
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { tmpdir } from 'node:os';
|
|
6
6
|
|
|
7
|
-
import { openDatabase, closeDatabase, getMilestone, getMilestoneSlices, updateSliceStatus } from '../gsd-db.ts';
|
|
7
|
+
import { openDatabase, closeDatabase, getMilestone, getMilestoneSlices, getSlice, updateSliceStatus, deleteSlice, insertMilestone } from '../gsd-db.ts';
|
|
8
8
|
import { handlePlanMilestone } from '../tools/plan-milestone.ts';
|
|
9
9
|
import { parseRoadmap } from '../parsers-legacy.ts';
|
|
10
10
|
|
|
@@ -198,33 +198,97 @@ test('handlePlanMilestone reruns idempotently and updates existing planning stat
|
|
|
198
198
|
}
|
|
199
199
|
});
|
|
200
200
|
|
|
201
|
-
|
|
202
|
-
test('handlePlanMilestone refuses to re-plan a milestone with completed slices (#2960)', async () => {
|
|
201
|
+
test('handlePlanMilestone preserves completed slice status on re-plan (#2558)', async () => {
|
|
203
202
|
const base = makeTmpBase();
|
|
204
203
|
const dbPath = join(base, '.gsd', 'gsd.db');
|
|
205
204
|
openDatabase(dbPath);
|
|
206
205
|
|
|
207
206
|
try {
|
|
208
|
-
//
|
|
207
|
+
// Initial plan — both slices start as "pending"
|
|
209
208
|
const first = await handlePlanMilestone(validParams(), base);
|
|
210
|
-
assert.ok(!('error' in first), `
|
|
209
|
+
assert.ok(!('error' in first), `unexpected error: ${'error' in first ? first.error : ''}`);
|
|
211
210
|
|
|
212
|
-
// Mark S01 as complete
|
|
213
|
-
updateSliceStatus('M001', 'S01', 'complete');
|
|
211
|
+
// Mark S01 as complete (simulates work done in a worktree)
|
|
212
|
+
updateSliceStatus('M001', 'S01', 'complete', new Date().toISOString());
|
|
214
213
|
|
|
215
|
-
|
|
216
|
-
|
|
214
|
+
const s01Before = getSlice('M001', 'S01');
|
|
215
|
+
assert.equal(s01Before?.status, 'complete', 'S01 should be complete before re-plan');
|
|
216
|
+
|
|
217
|
+
// Re-plan the same milestone — S01 must stay "complete", S02 stays "pending"
|
|
218
|
+
const second = await handlePlanMilestone(validParams(), base);
|
|
219
|
+
assert.ok(!('error' in second), `unexpected error: ${'error' in second ? second.error : ''}`);
|
|
220
|
+
|
|
221
|
+
const s01After = getSlice('M001', 'S01');
|
|
222
|
+
assert.equal(s01After?.status, 'complete', 'S01 status must be preserved as complete after re-plan');
|
|
223
|
+
|
|
224
|
+
const s02After = getSlice('M001', 'S02');
|
|
225
|
+
assert.equal(s02After?.status, 'pending', 'S02 should remain pending');
|
|
226
|
+
} finally {
|
|
227
|
+
cleanup(base);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test('plan-milestone re-plan preserves completed status and updates slice fields (#2558)', async () => {
|
|
232
|
+
const base = makeTmpBase();
|
|
233
|
+
const dbPath = join(base, '.gsd', 'gsd.db');
|
|
234
|
+
openDatabase(dbPath);
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
// Initial plan — both slices start as "pending"
|
|
238
|
+
const first = await handlePlanMilestone(validParams(), base);
|
|
239
|
+
assert.ok(!('error' in first), `unexpected error: ${'error' in first ? first.error : ''}`);
|
|
240
|
+
|
|
241
|
+
// Mark S01 as complete (simulates work done in worktree, then reconciled)
|
|
242
|
+
updateSliceStatus('M001', 'S01', 'complete', new Date().toISOString());
|
|
243
|
+
assert.equal(getSlice('M001', 'S01')?.status, 'complete');
|
|
244
|
+
|
|
245
|
+
// Re-plan with updated title for S01.
|
|
246
|
+
// The handler must:
|
|
247
|
+
// 1. NOT downgrade S01 from "complete" to "pending"
|
|
248
|
+
// 2. Update S01's non-status fields (title, risk, depends, demo)
|
|
249
|
+
// 3. Keep S02 as "pending"
|
|
250
|
+
const updatedParams = {
|
|
217
251
|
...validParams(),
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
252
|
+
slices: [
|
|
253
|
+
{ ...validParams().slices[0], title: 'Updated S01 title', risk: 'high' },
|
|
254
|
+
validParams().slices[1],
|
|
255
|
+
],
|
|
256
|
+
};
|
|
257
|
+
const second = await handlePlanMilestone(updatedParams, base);
|
|
258
|
+
assert.ok(!('error' in second), `unexpected error: ${'error' in second ? second.error : ''}`);
|
|
223
259
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
assert.equal(
|
|
260
|
+
const s01After = getSlice('M001', 'S01');
|
|
261
|
+
assert.equal(s01After?.status, 'complete', 'completed slice status must survive re-plan');
|
|
262
|
+
assert.equal(s01After?.title, 'Updated S01 title', 'title should update on re-plan');
|
|
263
|
+
assert.equal(s01After?.risk, 'high', 'risk should update on re-plan');
|
|
264
|
+
|
|
265
|
+
const s02After = getSlice('M001', 'S02');
|
|
266
|
+
assert.equal(s02After?.status, 'pending', 'pending slice stays pending');
|
|
267
|
+
} finally {
|
|
268
|
+
cleanup(base);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test('handlePlanMilestone promotes pre-existing queued milestone to active (#3022)', async () => {
|
|
273
|
+
const base = makeTmpBase();
|
|
274
|
+
const dbPath = join(base, '.gsd', 'gsd.db');
|
|
275
|
+
openDatabase(dbPath);
|
|
276
|
+
|
|
277
|
+
try {
|
|
278
|
+
// Simulate ensureMilestoneDbRow: pre-create row with status "queued"
|
|
279
|
+
// (this is what gsd_milestone_generate_id does)
|
|
280
|
+
insertMilestone({ id: 'M001', status: 'queued' });
|
|
281
|
+
|
|
282
|
+
const before = getMilestone('M001');
|
|
283
|
+
assert.equal(before?.status, 'queued', 'pre-condition: milestone should start as queued');
|
|
284
|
+
|
|
285
|
+
// Now plan the milestone — status should be promoted to "active"
|
|
286
|
+
const result = await handlePlanMilestone(validParams(), base);
|
|
287
|
+
assert.ok(!('error' in result), `unexpected error: ${'error' in result ? result.error : ''}`);
|
|
288
|
+
|
|
289
|
+
const after = getMilestone('M001');
|
|
290
|
+
assert.equal(after?.status, 'active', 'milestone status should be promoted from queued to active');
|
|
291
|
+
assert.equal(after?.title, 'DB-backed planning', 'milestone title should be set');
|
|
228
292
|
} finally {
|
|
229
293
|
cleanup(base);
|
|
230
294
|
}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* post-exec-retry-bypass.test.ts — Tests for post-execution blocking failure retry bypass.
|
|
3
|
+
*
|
|
4
|
+
* Verifies that when post-execution checks fail (postExecBlockingFailure is true),
|
|
5
|
+
* the retry system is bypassed and auto-mode pauses immediately. Post-execution
|
|
6
|
+
* failures are cross-task consistency issues — retrying the same task won't fix them.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, test, mock, beforeEach, afterEach } from "node:test";
|
|
10
|
+
import assert from "node:assert/strict";
|
|
11
|
+
import { tmpdir } from "node:os";
|
|
12
|
+
import { mkdirSync, writeFileSync, rmSync, existsSync } from "node:fs";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
|
|
15
|
+
import { runPostUnitVerification, type VerificationContext } from "../auto-verification.ts";
|
|
16
|
+
import { AutoSession } from "../auto/session.ts";
|
|
17
|
+
import { openDatabase, closeDatabase, insertMilestone, insertSlice, insertTask } from "../gsd-db.ts";
|
|
18
|
+
import { invalidateAllCaches } from "../cache.ts";
|
|
19
|
+
import { _clearGsdRootCache } from "../paths.ts";
|
|
20
|
+
|
|
21
|
+
// ─── Test Fixtures ───────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
let tempDir: string;
|
|
24
|
+
let dbPath: string;
|
|
25
|
+
let originalCwd: string;
|
|
26
|
+
|
|
27
|
+
function makeMockCtx() {
|
|
28
|
+
return {
|
|
29
|
+
ui: {
|
|
30
|
+
notify: mock.fn(),
|
|
31
|
+
setStatus: () => {},
|
|
32
|
+
setWidget: () => {},
|
|
33
|
+
setFooter: () => {},
|
|
34
|
+
},
|
|
35
|
+
model: { id: "test-model" },
|
|
36
|
+
} as any;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function makeMockPi() {
|
|
40
|
+
return {
|
|
41
|
+
sendMessage: mock.fn(),
|
|
42
|
+
setModel: mock.fn(async () => true),
|
|
43
|
+
} as any;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function makeMockSession(basePath: string, currentUnit?: { type: string; id: string }): AutoSession {
|
|
47
|
+
const s = new AutoSession();
|
|
48
|
+
s.basePath = basePath;
|
|
49
|
+
s.active = true;
|
|
50
|
+
// verificationRetryCount is readonly but initialized as an empty Map in AutoSession
|
|
51
|
+
s.pendingVerificationRetry = null;
|
|
52
|
+
if (currentUnit) {
|
|
53
|
+
s.currentUnit = {
|
|
54
|
+
type: currentUnit.type,
|
|
55
|
+
id: currentUnit.id,
|
|
56
|
+
startedAt: Date.now(),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return s;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function setupTestEnvironment(): void {
|
|
63
|
+
originalCwd = process.cwd();
|
|
64
|
+
tempDir = join(tmpdir(), `post-exec-retry-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
65
|
+
mkdirSync(tempDir, { recursive: true });
|
|
66
|
+
|
|
67
|
+
const gsdDir = join(tempDir, ".gsd");
|
|
68
|
+
mkdirSync(gsdDir, { recursive: true });
|
|
69
|
+
|
|
70
|
+
const milestonesDir = join(gsdDir, "milestones", "M001", "slices", "S01", "tasks");
|
|
71
|
+
mkdirSync(milestonesDir, { recursive: true });
|
|
72
|
+
|
|
73
|
+
process.chdir(tempDir);
|
|
74
|
+
_clearGsdRootCache();
|
|
75
|
+
|
|
76
|
+
dbPath = join(gsdDir, "gsd.db");
|
|
77
|
+
openDatabase(dbPath);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function cleanupTestEnvironment(): void {
|
|
81
|
+
try {
|
|
82
|
+
process.chdir(originalCwd);
|
|
83
|
+
} catch {
|
|
84
|
+
// Ignore
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
closeDatabase();
|
|
88
|
+
} catch {
|
|
89
|
+
// Ignore
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
93
|
+
} catch {
|
|
94
|
+
// Ignore
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function writePreferences(prefs: Record<string, unknown>): void {
|
|
99
|
+
const yamlLines = Object.entries(prefs).map(([k, v]) => `${k}: ${JSON.stringify(v)}`);
|
|
100
|
+
const prefsContent = `---
|
|
101
|
+
${yamlLines.join("\n")}
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
# GSD Preferences
|
|
105
|
+
`;
|
|
106
|
+
writeFileSync(join(tempDir, ".gsd", "PREFERENCES.md"), prefsContent);
|
|
107
|
+
invalidateAllCaches();
|
|
108
|
+
_clearGsdRootCache();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Create a task in DB that will pass basic verification but allows us to test the flow.
|
|
113
|
+
*/
|
|
114
|
+
function createBasicTask(): void {
|
|
115
|
+
insertMilestone({ id: "M001" });
|
|
116
|
+
insertSlice({
|
|
117
|
+
id: "S01",
|
|
118
|
+
milestoneId: "M001",
|
|
119
|
+
title: "Test Slice",
|
|
120
|
+
risk: "low",
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Create a simple task
|
|
124
|
+
insertTask({
|
|
125
|
+
id: "T01",
|
|
126
|
+
sliceId: "S01",
|
|
127
|
+
milestoneId: "M001",
|
|
128
|
+
title: "Basic task",
|
|
129
|
+
status: "pending",
|
|
130
|
+
planning: {
|
|
131
|
+
description: "A basic task for testing",
|
|
132
|
+
estimate: "1h",
|
|
133
|
+
files: [],
|
|
134
|
+
verify: "echo pass", // Simple verification that always passes
|
|
135
|
+
inputs: [],
|
|
136
|
+
expectedOutput: ["output.ts"],
|
|
137
|
+
observabilityImpact: "",
|
|
138
|
+
},
|
|
139
|
+
sequence: 0,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ─── Tests ───────────────────────────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
describe("Post-execution blocking failure retry bypass", () => {
|
|
146
|
+
beforeEach(() => {
|
|
147
|
+
setupTestEnvironment();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
afterEach(() => {
|
|
151
|
+
cleanupTestEnvironment();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("skips verification when unit type is not execute-task", async () => {
|
|
155
|
+
createBasicTask();
|
|
156
|
+
writePreferences({
|
|
157
|
+
enhanced_verification: true,
|
|
158
|
+
enhanced_verification_post: true,
|
|
159
|
+
verification_auto_fix: true,
|
|
160
|
+
verification_max_retries: 3,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const ctx = makeMockCtx();
|
|
164
|
+
const pi = makeMockPi();
|
|
165
|
+
const pauseAutoMock = mock.fn(async () => {});
|
|
166
|
+
const s = makeMockSession(tempDir, { type: "plan-slice", id: "M001/S01" });
|
|
167
|
+
|
|
168
|
+
const vctx: VerificationContext = { s, ctx, pi };
|
|
169
|
+
const result = await runPostUnitVerification(vctx, pauseAutoMock);
|
|
170
|
+
|
|
171
|
+
// Non-execute-task units should return "continue" immediately
|
|
172
|
+
assert.equal(result, "continue");
|
|
173
|
+
assert.equal(pauseAutoMock.mock.callCount(), 0);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("returns continue when verification passes", async () => {
|
|
177
|
+
createBasicTask();
|
|
178
|
+
writePreferences({
|
|
179
|
+
enhanced_verification: true,
|
|
180
|
+
enhanced_verification_post: true,
|
|
181
|
+
verification_auto_fix: true,
|
|
182
|
+
verification_max_retries: 3,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const ctx = makeMockCtx();
|
|
186
|
+
const pi = makeMockPi();
|
|
187
|
+
const pauseAutoMock = mock.fn(async () => {});
|
|
188
|
+
const s = makeMockSession(tempDir, { type: "execute-task", id: "M001/S01/T01" });
|
|
189
|
+
|
|
190
|
+
const vctx: VerificationContext = { s, ctx, pi };
|
|
191
|
+
const result = await runPostUnitVerification(vctx, pauseAutoMock);
|
|
192
|
+
|
|
193
|
+
// When verification passes, should return "continue" and not call pauseAuto
|
|
194
|
+
assert.equal(result, "continue");
|
|
195
|
+
assert.equal(pauseAutoMock.mock.callCount(), 0);
|
|
196
|
+
|
|
197
|
+
// Retry state should be cleared
|
|
198
|
+
assert.equal(s.pendingVerificationRetry, null);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test("verification retry count is cleared on success", async () => {
|
|
202
|
+
createBasicTask();
|
|
203
|
+
writePreferences({
|
|
204
|
+
enhanced_verification: true,
|
|
205
|
+
enhanced_verification_post: true,
|
|
206
|
+
verification_auto_fix: true,
|
|
207
|
+
verification_max_retries: 3,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const ctx = makeMockCtx();
|
|
211
|
+
const pi = makeMockPi();
|
|
212
|
+
const pauseAutoMock = mock.fn(async () => {});
|
|
213
|
+
const s = makeMockSession(tempDir, { type: "execute-task", id: "M001/S01/T01" });
|
|
214
|
+
|
|
215
|
+
// Pre-set some retry state
|
|
216
|
+
s.verificationRetryCount.set("M001/S01/T01", 2);
|
|
217
|
+
|
|
218
|
+
const vctx: VerificationContext = { s, ctx, pi };
|
|
219
|
+
const result = await runPostUnitVerification(vctx, pauseAutoMock);
|
|
220
|
+
|
|
221
|
+
// On success, retry count should be cleared
|
|
222
|
+
assert.equal(result, "continue");
|
|
223
|
+
assert.equal(s.verificationRetryCount.has("M001/S01/T01"), false);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
test("post-exec failure notification mentions cross-task consistency", async () => {
|
|
227
|
+
// This test verifies that the notification for post-exec failures includes
|
|
228
|
+
// the appropriate message about cross-task consistency issues.
|
|
229
|
+
// The actual post-exec failure would require specific file/output state
|
|
230
|
+
// that's harder to set up in a unit test, but we can verify the code path exists.
|
|
231
|
+
|
|
232
|
+
createBasicTask();
|
|
233
|
+
writePreferences({
|
|
234
|
+
enhanced_verification: true,
|
|
235
|
+
enhanced_verification_post: true,
|
|
236
|
+
verification_auto_fix: true,
|
|
237
|
+
verification_max_retries: 3,
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
const ctx = makeMockCtx();
|
|
241
|
+
const pi = makeMockPi();
|
|
242
|
+
const pauseAutoMock = mock.fn(async () => {});
|
|
243
|
+
const s = makeMockSession(tempDir, { type: "execute-task", id: "M001/S01/T01" });
|
|
244
|
+
|
|
245
|
+
const vctx: VerificationContext = { s, ctx, pi };
|
|
246
|
+
const result = await runPostUnitVerification(vctx, pauseAutoMock);
|
|
247
|
+
|
|
248
|
+
// The verification should pass with our simple "echo pass" task
|
|
249
|
+
// This test mainly confirms the wiring is correct
|
|
250
|
+
assert.equal(result, "continue");
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
describe("Post-execution retry behavior", () => {
|
|
255
|
+
beforeEach(() => {
|
|
256
|
+
setupTestEnvironment();
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
afterEach(() => {
|
|
260
|
+
cleanupTestEnvironment();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test("when autofix is disabled, failure pauses immediately without retry", async () => {
|
|
264
|
+
// Create a task with a verify command that will fail
|
|
265
|
+
insertMilestone({ id: "M001" });
|
|
266
|
+
insertSlice({
|
|
267
|
+
id: "S01",
|
|
268
|
+
milestoneId: "M001",
|
|
269
|
+
title: "Test Slice",
|
|
270
|
+
risk: "low",
|
|
271
|
+
});
|
|
272
|
+
insertTask({
|
|
273
|
+
id: "T01",
|
|
274
|
+
sliceId: "S01",
|
|
275
|
+
milestoneId: "M001",
|
|
276
|
+
title: "Failing task",
|
|
277
|
+
status: "pending",
|
|
278
|
+
planning: {
|
|
279
|
+
description: "Task with failing verification",
|
|
280
|
+
estimate: "1h",
|
|
281
|
+
files: [],
|
|
282
|
+
verify: "exit 1", // This will fail
|
|
283
|
+
inputs: [],
|
|
284
|
+
expectedOutput: [],
|
|
285
|
+
observabilityImpact: "",
|
|
286
|
+
},
|
|
287
|
+
sequence: 0,
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
writePreferences({
|
|
291
|
+
enhanced_verification: true,
|
|
292
|
+
enhanced_verification_post: true,
|
|
293
|
+
verification_auto_fix: false, // Autofix disabled
|
|
294
|
+
verification_max_retries: 3,
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
const ctx = makeMockCtx();
|
|
298
|
+
const pi = makeMockPi();
|
|
299
|
+
const pauseAutoMock = mock.fn(async () => {});
|
|
300
|
+
const s = makeMockSession(tempDir, { type: "execute-task", id: "M001/S01/T01" });
|
|
301
|
+
|
|
302
|
+
const vctx: VerificationContext = { s, ctx, pi };
|
|
303
|
+
const result = await runPostUnitVerification(vctx, pauseAutoMock);
|
|
304
|
+
|
|
305
|
+
// When autofix is disabled and verification fails, should pause
|
|
306
|
+
assert.equal(result, "pause");
|
|
307
|
+
assert.equal(pauseAutoMock.mock.callCount(), 1);
|
|
308
|
+
|
|
309
|
+
// Should NOT set up a retry
|
|
310
|
+
assert.equal(s.pendingVerificationRetry, null);
|
|
311
|
+
});
|
|
312
|
+
});
|