gsd-pi 2.48.0 → 2.49.0-dev.9e177e9
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/dist/headless-ui.js +12 -2
- package/dist/headless.js +29 -13
- package/dist/resources/extensions/gsd/auto/infra-errors.js +1 -0
- package/dist/resources/extensions/gsd/auto/phases.js +11 -11
- package/dist/resources/extensions/gsd/auto/resolve.js +2 -2
- package/dist/resources/extensions/gsd/auto/run-unit.js +2 -2
- package/dist/resources/extensions/gsd/auto/session.js +4 -0
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +8 -10
- package/dist/resources/extensions/gsd/auto-dashboard.js +6 -3
- package/dist/resources/extensions/gsd/auto-dispatch.js +34 -7
- package/dist/resources/extensions/gsd/auto-post-unit.js +34 -27
- package/dist/resources/extensions/gsd/auto-prompts.js +102 -21
- package/dist/resources/extensions/gsd/auto-recovery.js +62 -184
- package/dist/resources/extensions/gsd/auto-start.js +4 -31
- package/dist/resources/extensions/gsd/auto-timers.js +2 -2
- package/dist/resources/extensions/gsd/auto-verification.js +4 -7
- package/dist/resources/extensions/gsd/auto-worktree.js +262 -115
- package/dist/resources/extensions/gsd/auto.js +7 -5
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +89 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +8 -1
- package/dist/resources/extensions/gsd/branch-patterns.js +13 -0
- package/dist/resources/extensions/gsd/commands/handlers/auto.js +43 -3
- package/dist/resources/extensions/gsd/doctor-checks.js +5 -1234
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +168 -0
- package/dist/resources/extensions/gsd/doctor-environment.js +28 -7
- package/dist/resources/extensions/gsd/doctor-git-checks.js +405 -0
- package/dist/resources/extensions/gsd/doctor-global-checks.js +74 -0
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +600 -0
- package/dist/resources/extensions/gsd/doctor.js +9 -1
- package/dist/resources/extensions/gsd/extension-manifest.json +1 -1
- package/dist/resources/extensions/gsd/git-service.js +20 -20
- package/dist/resources/extensions/gsd/gsd-db.js +124 -1
- package/dist/resources/extensions/gsd/guided-flow-queue.js +10 -11
- package/dist/resources/extensions/gsd/markdown-renderer.js +33 -5
- package/dist/resources/extensions/gsd/preferences-types.js +2 -1
- package/dist/resources/extensions/gsd/preferences-validation.js +39 -0
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +27 -8
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +9 -8
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +223 -56
- package/dist/resources/extensions/gsd/prompts/execute-task.md +16 -13
- package/dist/resources/extensions/gsd/prompts/forensics.md +12 -5
- package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +32 -0
- package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +8 -3
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +3 -0
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/run-uat.md +4 -4
- package/dist/resources/extensions/gsd/repo-identity.js +29 -0
- package/dist/resources/extensions/gsd/roadmap-slices.js +2 -2
- package/dist/resources/extensions/gsd/session-forensics.js +6 -11
- package/dist/resources/extensions/gsd/session-lock.js +67 -56
- package/dist/resources/extensions/gsd/state.js +34 -7
- package/dist/resources/extensions/gsd/templates/milestone-summary.md +8 -0
- package/dist/resources/extensions/gsd/templates/plan.md +16 -0
- package/dist/resources/extensions/gsd/templates/roadmap.md +13 -0
- package/dist/resources/extensions/gsd/templates/slice-summary.md +9 -0
- package/dist/resources/extensions/gsd/templates/task-plan.md +24 -0
- package/dist/resources/extensions/gsd/tools/plan-slice.js +14 -1
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +3 -3
- package/dist/resources/extensions/gsd/verdict-parser.js +84 -0
- package/dist/resources/extensions/gsd/worktree-command.js +1 -1
- package/dist/resources/extensions/gsd/worktree-resolver.js +24 -0
- package/dist/resources/extensions/gsd/worktree.js +3 -2
- package/dist/resources/extensions/remote-questions/config.js +3 -5
- package/dist/resources/extensions/search-the-web/native-search.js +8 -3
- package/dist/resources/extensions/search-the-web/tool-search.js +19 -2
- package/dist/resources/skills/github-workflows/references/gh/SKILL.md +22 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
- package/dist/web/standalone/.next/build-manifest.json +4 -4
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- 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 +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
- package/dist/web/standalone/.next/server/chunks/229.js +2 -2
- package/dist/web/standalone/.next/server/chunks/471.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/4024.7c75ac378de0f2b5.js +9 -0
- package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-6654a8cca61a3d1c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-0a4cd455ec4197d2.js → webpack-2473ce2c3879fff4.js} +1 -1
- 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/worktree-cli.js +1 -1
- package/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +4 -1
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.ts +4 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js +39 -10
- package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
- package/packages/pi-ai/src/providers/openai-codex-responses.ts +39 -8
- package/packages/pi-coding-agent/dist/core/blob-store.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/blob-store.js +8 -3
- package/packages/pi-coding-agent/dist/core/blob-store.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.js +9 -2
- package/packages/pi-coding-agent/dist/core/discovery-cache.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -32
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/jsonl.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/jsonl.js +5 -0
- package/packages/pi-coding-agent/dist/modes/rpc/jsonl.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +0 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/blob-store.ts +6 -3
- package/packages/pi-coding-agent/src/core/discovery-cache.ts +9 -2
- package/packages/pi-coding-agent/src/core/retry-handler.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +7 -32
- package/packages/pi-coding-agent/src/modes/rpc/jsonl.ts +6 -0
- package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +0 -2
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/extensions/github-sync/tests/commit-linking.test.ts +8 -4
- package/src/resources/extensions/gsd/auto/infra-errors.ts +1 -0
- package/src/resources/extensions/gsd/auto/phases.ts +10 -11
- package/src/resources/extensions/gsd/auto/resolve.ts +3 -3
- package/src/resources/extensions/gsd/auto/run-unit.ts +2 -2
- package/src/resources/extensions/gsd/auto/session.ts +5 -0
- package/src/resources/extensions/gsd/auto/types.ts +13 -0
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +19 -21
- package/src/resources/extensions/gsd/auto-dashboard.ts +5 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +40 -5
- package/src/resources/extensions/gsd/auto-loop.ts +1 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +36 -31
- package/src/resources/extensions/gsd/auto-prompts.ts +113 -19
- package/src/resources/extensions/gsd/auto-recovery.ts +65 -199
- package/src/resources/extensions/gsd/auto-start.ts +7 -27
- package/src/resources/extensions/gsd/auto-timers.ts +2 -2
- package/src/resources/extensions/gsd/auto-verification.ts +4 -7
- package/src/resources/extensions/gsd/auto-worktree.ts +309 -110
- package/src/resources/extensions/gsd/auto.ts +11 -10
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +93 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +8 -0
- package/src/resources/extensions/gsd/branch-patterns.ts +16 -0
- package/src/resources/extensions/gsd/commands/handlers/auto.ts +46 -3
- package/src/resources/extensions/gsd/doctor-checks.ts +5 -1291
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +182 -0
- package/src/resources/extensions/gsd/doctor-environment.ts +30 -7
- package/src/resources/extensions/gsd/doctor-git-checks.ts +415 -0
- package/src/resources/extensions/gsd/doctor-global-checks.ts +84 -0
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +626 -0
- package/src/resources/extensions/gsd/doctor.ts +9 -1
- package/src/resources/extensions/gsd/extension-manifest.json +1 -1
- package/src/resources/extensions/gsd/git-service.ts +19 -26
- package/src/resources/extensions/gsd/gsd-db.ts +150 -2
- package/src/resources/extensions/gsd/guided-flow-queue.ts +11 -12
- package/src/resources/extensions/gsd/markdown-renderer.ts +37 -4
- package/src/resources/extensions/gsd/preferences-types.ts +5 -1
- package/src/resources/extensions/gsd/preferences-validation.ts +37 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +27 -8
- package/src/resources/extensions/gsd/prompts/complete-slice.md +9 -8
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +223 -56
- package/src/resources/extensions/gsd/prompts/execute-task.md +16 -13
- package/src/resources/extensions/gsd/prompts/forensics.md +12 -5
- package/src/resources/extensions/gsd/prompts/gate-evaluate.md +32 -0
- package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +8 -3
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +3 -0
- package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +4 -4
- package/src/resources/extensions/gsd/repo-identity.ts +28 -0
- package/src/resources/extensions/gsd/roadmap-slices.ts +2 -2
- package/src/resources/extensions/gsd/session-forensics.ts +6 -11
- package/src/resources/extensions/gsd/session-lock.ts +92 -64
- package/src/resources/extensions/gsd/state.ts +38 -5
- package/src/resources/extensions/gsd/templates/milestone-summary.md +8 -0
- package/src/resources/extensions/gsd/templates/plan.md +16 -0
- package/src/resources/extensions/gsd/templates/roadmap.md +13 -0
- package/src/resources/extensions/gsd/templates/slice-summary.md +9 -0
- package/src/resources/extensions/gsd/templates/task-plan.md +24 -0
- package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +1 -81
- package/src/resources/extensions/gsd/tests/auto-stash-merge.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +14 -12
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +9 -12
- package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +115 -1
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +65 -1
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +189 -0
- package/src/resources/extensions/gsd/tests/gate-storage.test.ts +156 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +68 -9
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/infra-error.test.ts +12 -2
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/parallel-merge.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/quality-gates.test.ts +347 -0
- package/src/resources/extensions/gsd/tests/queue-completed-milestone-perf.test.ts +155 -0
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +87 -15
- package/src/resources/extensions/gsd/tests/session-lock-transient-read.test.ts +223 -0
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +44 -4
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +0 -16
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +67 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +204 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +16 -0
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +3 -3
- package/src/resources/extensions/gsd/types.ts +30 -0
- package/src/resources/extensions/gsd/verdict-parser.ts +95 -0
- package/src/resources/extensions/gsd/verification-gate.ts +0 -2
- package/src/resources/extensions/gsd/worktree-command.ts +1 -1
- package/src/resources/extensions/gsd/worktree-resolver.ts +31 -0
- package/src/resources/extensions/gsd/worktree.ts +3 -2
- package/src/resources/extensions/remote-questions/config.ts +3 -5
- package/src/resources/extensions/search-the-web/native-search.ts +8 -3
- package/src/resources/extensions/search-the-web/tool-search.ts +22 -2
- package/src/resources/skills/github-workflows/references/gh/SKILL.md +22 -1
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +0 -191
- package/dist/resources/extensions/gsd/resource-version.js +0 -97
- package/dist/web/standalone/.next/static/chunks/4024.11ca5c01938e5948.js +0 -9
- package/dist/web/standalone/.next/static/chunks/app/page-12dd5ece0df4badc.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/src/resources/extensions/gsd/auto-worktree-sync.ts +0 -234
- package/src/resources/extensions/gsd/resource-version.ts +0 -101
- /package/dist/web/standalone/.next/static/{zGWUKFTfjCQerNgsPpAbF → vNN0h0emdEi8l_npi8poE}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{zGWUKFTfjCQerNgsPpAbF → vNN0h0emdEi8l_npi8poE}/_ssgManifest.js +0 -0
|
@@ -171,7 +171,7 @@ test('(k) run-uat prompt template', () => {
|
|
|
171
171
|
const milestoneId = 'M001';
|
|
172
172
|
const sliceId = 'S01';
|
|
173
173
|
const uatPath = '.gsd/milestones/M001/slices/S01/S01-UAT.md';
|
|
174
|
-
const uatResultPath = '.gsd/milestones/M001/slices/S01/S01-UAT
|
|
174
|
+
const uatResultPath = '.gsd/milestones/M001/slices/S01/S01-UAT.md';
|
|
175
175
|
const uatType = 'live-runtime';
|
|
176
176
|
const inlinedContext = '<!-- no context -->';
|
|
177
177
|
let promptResult: string | undefined;
|
|
@@ -234,7 +234,7 @@ test('(k2) run-uat prompt references gsd_summary_save, not direct write', () =>
|
|
|
234
234
|
milestoneId: 'M001',
|
|
235
235
|
sliceId: 'S01',
|
|
236
236
|
uatPath: '.gsd/milestones/M001/slices/S01/S01-UAT.md',
|
|
237
|
-
uatResultPath: '.gsd/milestones/M001/slices/S01/S01-UAT
|
|
237
|
+
uatResultPath: '.gsd/milestones/M001/slices/S01/S01-UAT.md',
|
|
238
238
|
uatType: 'artifact-driven',
|
|
239
239
|
inlinedContext: '<!-- no context -->',
|
|
240
240
|
});
|
|
@@ -265,14 +265,13 @@ test('(l) dispatch preconditions via resolveSliceFile', () => {
|
|
|
265
265
|
'resolveSliceFile(..., "UAT") returns non-null when UAT file exists (dispatch trigger state)',
|
|
266
266
|
);
|
|
267
267
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
'
|
|
268
|
+
// UAT spec without a verdict line means UAT has not been run yet
|
|
269
|
+
const rawContent = readFileSync(uatFilePath!, 'utf-8');
|
|
270
|
+
assert.ok(
|
|
271
|
+
!/verdict:\s*[\w-]+/i.test(rawContent),
|
|
272
|
+
'UAT file without verdict indicates UAT has not been run (dispatch trigger state)',
|
|
273
273
|
);
|
|
274
274
|
|
|
275
|
-
const rawContent = readFileSync(uatFilePath!, 'utf-8');
|
|
276
275
|
assert.deepStrictEqual(
|
|
277
276
|
extractUatType(rawContent),
|
|
278
277
|
'artifact-driven',
|
|
@@ -286,13 +285,18 @@ test('(l) dispatch preconditions via resolveSliceFile', () => {
|
|
|
286
285
|
test('test block at line 307', () => {
|
|
287
286
|
const base = createFixtureBase();
|
|
288
287
|
try {
|
|
289
|
-
|
|
290
|
-
writeSliceFile(base, 'M001', 'S01', 'UAT
|
|
288
|
+
// Write UAT file with a verdict — simulates completed UAT
|
|
289
|
+
writeSliceFile(base, 'M001', 'S01', 'UAT', '# UAT Result\n\nverdict: PASS\n');
|
|
291
290
|
|
|
292
|
-
const
|
|
291
|
+
const uatFilePath = resolveSliceFile(base, 'M001', 'S01', 'UAT');
|
|
293
292
|
assert.ok(
|
|
294
|
-
|
|
295
|
-
'resolveSliceFile(..., "UAT
|
|
293
|
+
uatFilePath !== null,
|
|
294
|
+
'resolveSliceFile(..., "UAT") returns non-null when UAT file exists',
|
|
295
|
+
);
|
|
296
|
+
const content = readFileSync(uatFilePath!, 'utf-8');
|
|
297
|
+
assert.ok(
|
|
298
|
+
/verdict:\s*[\w-]+/i.test(content),
|
|
299
|
+
'UAT file with verdict indicates UAT has been completed (idempotent skip state)',
|
|
296
300
|
);
|
|
297
301
|
} finally {
|
|
298
302
|
cleanup(base);
|
|
@@ -343,6 +347,74 @@ test('(m) non-artifact UAT skip', async () => {
|
|
|
343
347
|
}
|
|
344
348
|
});
|
|
345
349
|
|
|
350
|
+
test('(o) verdict gate: PARTIAL is acceptable for mixed/human-experience/live-runtime UAT types', () => {
|
|
351
|
+
// This test verifies the contract that extractUatType correctly identifies
|
|
352
|
+
// the modes where PARTIAL should not block progression.
|
|
353
|
+
// The verdict gate in auto-dispatch.ts uses this to build acceptableVerdicts.
|
|
354
|
+
const mixedType = extractUatType(makeUatContent('mixed'));
|
|
355
|
+
const humanExpType = extractUatType(makeUatContent('human-experience'));
|
|
356
|
+
const liveRuntimeType = extractUatType(makeUatContent('live-runtime'));
|
|
357
|
+
const artifactType = extractUatType(makeUatContent('artifact-driven'));
|
|
358
|
+
const browserType = extractUatType(makeUatContent('browser-executable'));
|
|
359
|
+
const runtimeExecType = extractUatType(makeUatContent('runtime-executable'));
|
|
360
|
+
|
|
361
|
+
// These modes should allow PARTIAL (non-fully-automatable)
|
|
362
|
+
const partialAcceptableModes = ['mixed', 'human-experience', 'live-runtime'];
|
|
363
|
+
assert.ok(
|
|
364
|
+
partialAcceptableModes.includes(mixedType!),
|
|
365
|
+
`mixed → "${mixedType}" is in partialAcceptableModes`,
|
|
366
|
+
);
|
|
367
|
+
assert.ok(
|
|
368
|
+
partialAcceptableModes.includes(humanExpType!),
|
|
369
|
+
`human-experience → "${humanExpType}" is in partialAcceptableModes`,
|
|
370
|
+
);
|
|
371
|
+
assert.ok(
|
|
372
|
+
partialAcceptableModes.includes(liveRuntimeType!),
|
|
373
|
+
`live-runtime → "${liveRuntimeType}" is in partialAcceptableModes`,
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
// These modes should NOT allow PARTIAL (fully automatable)
|
|
377
|
+
assert.ok(
|
|
378
|
+
!partialAcceptableModes.includes(artifactType!),
|
|
379
|
+
`artifact-driven → "${artifactType}" is NOT in partialAcceptableModes`,
|
|
380
|
+
);
|
|
381
|
+
assert.ok(
|
|
382
|
+
!partialAcceptableModes.includes(browserType!),
|
|
383
|
+
`browser-executable → "${browserType}" is NOT in partialAcceptableModes`,
|
|
384
|
+
);
|
|
385
|
+
assert.ok(
|
|
386
|
+
!partialAcceptableModes.includes(runtimeExecType!),
|
|
387
|
+
`runtime-executable → "${runtimeExecType}" is NOT in partialAcceptableModes`,
|
|
388
|
+
);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
test('(p) run-uat prompt allows PASS when human-only checks remain as NEEDS-HUMAN', () => {
|
|
392
|
+
const promptResult = loadPromptFromWorktree('run-uat', {
|
|
393
|
+
workingDirectory: '/tmp/test-project',
|
|
394
|
+
milestoneId: 'M001',
|
|
395
|
+
sliceId: 'S01',
|
|
396
|
+
uatPath: '.gsd/milestones/M001/slices/S01/S01-UAT.md',
|
|
397
|
+
uatResultPath: '.gsd/milestones/M001/slices/S01/S01-UAT.md',
|
|
398
|
+
uatType: 'mixed',
|
|
399
|
+
inlinedContext: '<!-- no context -->',
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
// PASS verdict should be usable when automatable checks pass (even with NEEDS-HUMAN remaining)
|
|
403
|
+
assert.ok(
|
|
404
|
+
/PASS.*automatable checks passed/i.test(promptResult),
|
|
405
|
+
'prompt defines PASS as valid when all automatable checks passed',
|
|
406
|
+
);
|
|
407
|
+
assert.ok(
|
|
408
|
+
/PARTIAL.*automatable checks.*(skipped|inconclusive)/i.test(promptResult),
|
|
409
|
+
'prompt reserves PARTIAL for when automatable checks themselves are inconclusive',
|
|
410
|
+
);
|
|
411
|
+
// human-experience mode should NOT force PARTIAL when automatable checks pass
|
|
412
|
+
assert.ok(
|
|
413
|
+
!promptResult.includes('use an overall verdict of `PARTIAL`'),
|
|
414
|
+
'prompt does not force PARTIAL verdict for human-experience mode',
|
|
415
|
+
);
|
|
416
|
+
});
|
|
417
|
+
|
|
346
418
|
test('(n) stale replay guard', async () => {
|
|
347
419
|
const base = createFixtureBase();
|
|
348
420
|
try {
|
|
@@ -364,7 +436,7 @@ test('(n) stale replay guard', async () => {
|
|
|
364
436
|
);
|
|
365
437
|
|
|
366
438
|
writeSliceFile(base, 'M001', 'S01', 'UAT', makeUatContent('artifact-driven'));
|
|
367
|
-
writeSliceFile(base, 'M001', 'S01', 'UAT
|
|
439
|
+
writeSliceFile(base, 'M001', 'S01', 'UAT', '---\nverdict: FAIL\n---\n');
|
|
368
440
|
|
|
369
441
|
const state = {
|
|
370
442
|
activeMilestone: { id: 'M001', title: 'Test roadmap' },
|
|
@@ -381,7 +453,7 @@ test('(n) stale replay guard', async () => {
|
|
|
381
453
|
assert.deepStrictEqual(
|
|
382
454
|
result,
|
|
383
455
|
null,
|
|
384
|
-
'existing UAT
|
|
456
|
+
'existing UAT with FAIL verdict does not re-dispatch; verdict gate owns blocking',
|
|
385
457
|
);
|
|
386
458
|
} finally {
|
|
387
459
|
cleanup(base);
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* session-lock-transient-read.test.ts — Tests for transient lock file unreadability (#2324).
|
|
3
|
+
*
|
|
4
|
+
* Regression coverage for:
|
|
5
|
+
* #2324 onCompromised declares lock lost when the lock file is temporarily
|
|
6
|
+
* unreadable (NFS/CIFS latency, macOS APFS snapshot, concurrent process
|
|
7
|
+
* briefly holding the file).
|
|
8
|
+
*
|
|
9
|
+
* Tests:
|
|
10
|
+
* - readExistingLockDataWithRetry retries on transient read failure
|
|
11
|
+
* - readExistingLockDataWithRetry returns data when file becomes readable after retries
|
|
12
|
+
* - readExistingLockDataWithRetry returns null only when ALL retries exhausted
|
|
13
|
+
* - onCompromised does not declare compromise when lock file is transiently unreadable
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, renameSync, unlinkSync, chmodSync } from 'node:fs';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
import { tmpdir } from 'node:os';
|
|
19
|
+
import { execSync, spawn } from 'node:child_process';
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
acquireSessionLock,
|
|
23
|
+
getSessionLockStatus,
|
|
24
|
+
releaseSessionLock,
|
|
25
|
+
readExistingLockDataWithRetry,
|
|
26
|
+
type SessionLockData,
|
|
27
|
+
} from '../session-lock.ts';
|
|
28
|
+
import { gsdRoot } from '../paths.ts';
|
|
29
|
+
import { createTestContext } from './test-helpers.ts';
|
|
30
|
+
|
|
31
|
+
const { assertEq, assertTrue, report } = createTestContext();
|
|
32
|
+
|
|
33
|
+
async function main(): Promise<void> {
|
|
34
|
+
|
|
35
|
+
// ─── 1. readExistingLockDataWithRetry succeeds on first read when file is fine ─
|
|
36
|
+
console.log('\n=== 1. readExistingLockDataWithRetry reads file normally ===');
|
|
37
|
+
{
|
|
38
|
+
const base = mkdtempSync(join(tmpdir(), 'gsd-transient-'));
|
|
39
|
+
mkdirSync(join(base, '.gsd'), { recursive: true });
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const lockFile = join(gsdRoot(base), 'auto.lock');
|
|
43
|
+
const lockData: SessionLockData = {
|
|
44
|
+
pid: process.pid,
|
|
45
|
+
startedAt: new Date().toISOString(),
|
|
46
|
+
unitType: 'execute-task',
|
|
47
|
+
unitId: 'M001/S01/T01',
|
|
48
|
+
unitStartedAt: new Date().toISOString(),
|
|
49
|
+
sessionFile: 'test-session.json',
|
|
50
|
+
};
|
|
51
|
+
writeFileSync(lockFile, JSON.stringify(lockData, null, 2));
|
|
52
|
+
|
|
53
|
+
const result = readExistingLockDataWithRetry(lockFile);
|
|
54
|
+
assertTrue(result !== null, 'data returned for readable file');
|
|
55
|
+
assertEq(result!.pid, process.pid, 'correct PID read');
|
|
56
|
+
assertEq(result!.sessionFile, 'test-session.json', 'correct sessionFile read');
|
|
57
|
+
} finally {
|
|
58
|
+
rmSync(base, { recursive: true, force: true });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ─── 2. readExistingLockDataWithRetry returns null for truly missing file ──
|
|
63
|
+
console.log('\n=== 2. readExistingLockDataWithRetry returns null for missing file ===');
|
|
64
|
+
{
|
|
65
|
+
const base = mkdtempSync(join(tmpdir(), 'gsd-transient-'));
|
|
66
|
+
mkdirSync(join(base, '.gsd'), { recursive: true });
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const lockFile = join(gsdRoot(base), 'auto.lock');
|
|
70
|
+
// File doesn't exist
|
|
71
|
+
const result = readExistingLockDataWithRetry(lockFile, { maxAttempts: 2, delayMs: 10 });
|
|
72
|
+
assertEq(result, null, 'null for truly missing file after retries');
|
|
73
|
+
} finally {
|
|
74
|
+
rmSync(base, { recursive: true, force: true });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ─── 3. readExistingLockDataWithRetry recovers after transient rename ──────
|
|
79
|
+
console.log('\n=== 3. readExistingLockDataWithRetry recovers after transient unavailability ===');
|
|
80
|
+
{
|
|
81
|
+
const base = mkdtempSync(join(tmpdir(), 'gsd-transient-'));
|
|
82
|
+
mkdirSync(join(base, '.gsd'), { recursive: true });
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const lockFile = join(gsdRoot(base), 'auto.lock');
|
|
86
|
+
const tmpFile = lockFile + '.hidden';
|
|
87
|
+
const lockData: SessionLockData = {
|
|
88
|
+
pid: process.pid,
|
|
89
|
+
startedAt: new Date().toISOString(),
|
|
90
|
+
unitType: 'execute-task',
|
|
91
|
+
unitId: 'M001/S01/T01',
|
|
92
|
+
unitStartedAt: new Date().toISOString(),
|
|
93
|
+
sessionFile: 'recovery-session.json',
|
|
94
|
+
};
|
|
95
|
+
writeFileSync(lockFile, JSON.stringify(lockData, null, 2));
|
|
96
|
+
|
|
97
|
+
// Simulate transient unavailability: move file away, spawn a child process
|
|
98
|
+
// to restore it after 100ms. The child runs outside our event loop so it
|
|
99
|
+
// fires even during busy-wait retries.
|
|
100
|
+
renameSync(lockFile, tmpFile);
|
|
101
|
+
spawn('bash', ['-c', `sleep 0.1 && mv "${tmpFile}" "${lockFile}"`], { stdio: 'ignore', detached: true }).unref();
|
|
102
|
+
|
|
103
|
+
// With retries (3 attempts, 200ms delay), it should recover on 2nd or 3rd attempt
|
|
104
|
+
const result = readExistingLockDataWithRetry(lockFile, { maxAttempts: 3, delayMs: 200 });
|
|
105
|
+
assertTrue(result !== null, 'data recovered after transient unavailability');
|
|
106
|
+
if (result) {
|
|
107
|
+
assertEq(result.pid, process.pid, 'correct PID after recovery');
|
|
108
|
+
assertEq(result.sessionFile, 'recovery-session.json', 'correct sessionFile after recovery');
|
|
109
|
+
}
|
|
110
|
+
} finally {
|
|
111
|
+
rmSync(base, { recursive: true, force: true });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ─── 4. readExistingLockDataWithRetry recovers from transient permission error ─
|
|
116
|
+
console.log('\n=== 4. readExistingLockDataWithRetry recovers from transient permission error ===');
|
|
117
|
+
{
|
|
118
|
+
const base = mkdtempSync(join(tmpdir(), 'gsd-transient-'));
|
|
119
|
+
mkdirSync(join(base, '.gsd'), { recursive: true });
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
const lockFile = join(gsdRoot(base), 'auto.lock');
|
|
123
|
+
const lockData: SessionLockData = {
|
|
124
|
+
pid: process.pid,
|
|
125
|
+
startedAt: new Date().toISOString(),
|
|
126
|
+
unitType: 'execute-task',
|
|
127
|
+
unitId: 'M001/S01/T01',
|
|
128
|
+
unitStartedAt: new Date().toISOString(),
|
|
129
|
+
sessionFile: 'perm-session.json',
|
|
130
|
+
};
|
|
131
|
+
writeFileSync(lockFile, JSON.stringify(lockData, null, 2));
|
|
132
|
+
|
|
133
|
+
// Remove read permission to simulate NFS/CIFS latency, then spawn a child
|
|
134
|
+
// to restore permissions after 100ms (runs outside our event loop).
|
|
135
|
+
chmodSync(lockFile, 0o000);
|
|
136
|
+
spawn('bash', ['-c', `sleep 0.1 && chmod 644 "${lockFile}"`], { stdio: 'ignore', detached: true }).unref();
|
|
137
|
+
|
|
138
|
+
const result = readExistingLockDataWithRetry(lockFile, { maxAttempts: 3, delayMs: 200 });
|
|
139
|
+
assertTrue(result !== null, 'data recovered after transient permission error');
|
|
140
|
+
if (result) {
|
|
141
|
+
assertEq(result.pid, process.pid, 'correct PID after permission recovery');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Ensure permissions restored for cleanup
|
|
145
|
+
try { chmodSync(lockFile, 0o644); } catch { /* best-effort */ }
|
|
146
|
+
} finally {
|
|
147
|
+
rmSync(base, { recursive: true, force: true });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ─── 5. getSessionLockStatus does not false-positive on transient read failure ─
|
|
152
|
+
console.log('\n=== 5. getSessionLockStatus tolerates transient lock file unavailability ===');
|
|
153
|
+
{
|
|
154
|
+
const base = mkdtempSync(join(tmpdir(), 'gsd-transient-'));
|
|
155
|
+
mkdirSync(join(base, '.gsd'), { recursive: true });
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
const result = acquireSessionLock(base);
|
|
159
|
+
assertTrue(result.acquired, 'lock acquired');
|
|
160
|
+
|
|
161
|
+
// Validate works initially
|
|
162
|
+
const status1 = getSessionLockStatus(base);
|
|
163
|
+
assertTrue(status1.valid, 'lock valid before transient failure');
|
|
164
|
+
|
|
165
|
+
// Temporarily hide the lock file
|
|
166
|
+
const lockFile = join(gsdRoot(base), 'auto.lock');
|
|
167
|
+
const tmpFile = lockFile + '.hidden';
|
|
168
|
+
renameSync(lockFile, tmpFile);
|
|
169
|
+
|
|
170
|
+
// Schedule restoration
|
|
171
|
+
setTimeout(() => {
|
|
172
|
+
try { renameSync(tmpFile, lockFile); } catch { /* best-effort */ }
|
|
173
|
+
}, 30);
|
|
174
|
+
|
|
175
|
+
// Small delay to ensure restoration runs, then check — with the OS lock
|
|
176
|
+
// still held, getSessionLockStatus should return valid=true even if the
|
|
177
|
+
// lock file was briefly missing (it checks _releaseFunction first).
|
|
178
|
+
await new Promise(r => setTimeout(r, 60));
|
|
179
|
+
const status2 = getSessionLockStatus(base);
|
|
180
|
+
assertTrue(status2.valid, 'lock still valid after transient file disappearance (OS lock held)');
|
|
181
|
+
|
|
182
|
+
// Restore if not yet restored
|
|
183
|
+
try { renameSync(tmpFile, lockFile); } catch { /* already restored */ }
|
|
184
|
+
|
|
185
|
+
releaseSessionLock(base);
|
|
186
|
+
} finally {
|
|
187
|
+
rmSync(base, { recursive: true, force: true });
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// ─── 6. Retry defaults: 3 attempts with 200ms delay ────────────────────────
|
|
192
|
+
console.log('\n=== 6. Default retry params: function works with defaults ===');
|
|
193
|
+
{
|
|
194
|
+
const base = mkdtempSync(join(tmpdir(), 'gsd-transient-'));
|
|
195
|
+
mkdirSync(join(base, '.gsd'), { recursive: true });
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
const lockFile = join(gsdRoot(base), 'auto.lock');
|
|
199
|
+
const lockData: SessionLockData = {
|
|
200
|
+
pid: process.pid,
|
|
201
|
+
startedAt: new Date().toISOString(),
|
|
202
|
+
unitType: 'execute-task',
|
|
203
|
+
unitId: 'M001/S01/T01',
|
|
204
|
+
unitStartedAt: new Date().toISOString(),
|
|
205
|
+
sessionFile: 'status-session.json',
|
|
206
|
+
};
|
|
207
|
+
writeFileSync(lockFile, JSON.stringify(lockData, null, 2));
|
|
208
|
+
|
|
209
|
+
// Call with no options — uses defaults (3 attempts, 200ms)
|
|
210
|
+
const result = readExistingLockDataWithRetry(lockFile);
|
|
211
|
+
assertTrue(result !== null, 'default params work for readable file');
|
|
212
|
+
} finally {
|
|
213
|
+
rmSync(base, { recursive: true, force: true });
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
report();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
main().catch((error) => {
|
|
221
|
+
console.error(error);
|
|
222
|
+
process.exit(1);
|
|
223
|
+
});
|
|
@@ -75,7 +75,7 @@ test("buildSkillActivationBlock activates skills via prefer_skills when context
|
|
|
75
75
|
prefer_skills: ["react"],
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
-
assert.match(result, /Call Skill\('react'\)/);
|
|
78
|
+
assert.match(result, /Call Skill\(\{ skill: 'react' \}\)/);
|
|
79
79
|
assert.doesNotMatch(result, /swiftui/);
|
|
80
80
|
} finally {
|
|
81
81
|
cleanup(base);
|
|
@@ -92,7 +92,7 @@ test("buildSkillActivationBlock includes always_use_skills from preferences usin
|
|
|
92
92
|
always_use_skills: ["swift-testing"],
|
|
93
93
|
});
|
|
94
94
|
|
|
95
|
-
assert.equal(result, "<skill_activation>Call Skill('swift-testing').</skill_activation>");
|
|
95
|
+
assert.equal(result, "<skill_activation>Call Skill({ skill: 'swift-testing' }).</skill_activation>");
|
|
96
96
|
} finally {
|
|
97
97
|
cleanup(base);
|
|
98
98
|
}
|
|
@@ -120,8 +120,8 @@ test("buildSkillActivationBlock includes skill_rules matches and task-plan skill
|
|
|
120
120
|
skill_rules: [{ when: "prisma database schema", use: ["prisma"] }],
|
|
121
121
|
});
|
|
122
122
|
|
|
123
|
-
assert.match(result, /Call Skill\('accessibility'\)/);
|
|
124
|
-
assert.match(result, /Call Skill\('prisma'\)/);
|
|
123
|
+
assert.match(result, /Call Skill\(\{ skill: 'accessibility' \}\)/);
|
|
124
|
+
assert.match(result, /Call Skill\(\{ skill: 'prisma' \}\)/);
|
|
125
125
|
} finally {
|
|
126
126
|
cleanup(base);
|
|
127
127
|
}
|
|
@@ -191,3 +191,43 @@ test("buildSkillActivationBlock does not activate skills from extraContext or ta
|
|
|
191
191
|
cleanup(base);
|
|
192
192
|
}
|
|
193
193
|
});
|
|
194
|
+
|
|
195
|
+
test("buildSkillActivationBlock rejects skill names with special characters", () => {
|
|
196
|
+
const base = makeTempBase();
|
|
197
|
+
try {
|
|
198
|
+
// Skill names with quotes, braces, or other non-alphanumeric characters are
|
|
199
|
+
// rejected by the SAFE_SKILL_NAME guard to prevent prompt injection.
|
|
200
|
+
writeSkill(base, "my-skill's", "Skill with apostrophe in name.");
|
|
201
|
+
loadOnlyTestSkills(base);
|
|
202
|
+
|
|
203
|
+
const result = buildBlock(base, {}, {
|
|
204
|
+
always_use_skills: ["my-skill's"],
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Unsafe skill name is filtered out — empty result
|
|
208
|
+
assert.equal(result, "");
|
|
209
|
+
} finally {
|
|
210
|
+
cleanup(base);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("buildSkillActivationBlock allows valid skill names and rejects invalid ones", () => {
|
|
215
|
+
const base = makeTempBase();
|
|
216
|
+
try {
|
|
217
|
+
writeSkill(base, "react", "React skill.");
|
|
218
|
+
writeSkill(base, "bad'name", "Injection attempt.");
|
|
219
|
+
writeSkill(base, "good-skill-2", "Another valid skill.");
|
|
220
|
+
loadOnlyTestSkills(base);
|
|
221
|
+
|
|
222
|
+
const result = buildBlock(base, {}, {
|
|
223
|
+
always_use_skills: ["react", "bad'name", "good-skill-2"],
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
assert.match(result, /skill_activation/);
|
|
227
|
+
assert.match(result, /Call Skill\(\{ skill: 'react' \}\)/);
|
|
228
|
+
assert.match(result, /Call Skill\(\{ skill: 'good-skill-2' \}\)/);
|
|
229
|
+
assert.doesNotMatch(result, /bad'name/);
|
|
230
|
+
} finally {
|
|
231
|
+
cleanup(base);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
@@ -44,7 +44,7 @@ console.log('\n── Tool naming: registration count ──');
|
|
|
44
44
|
const pi = makeMockPi();
|
|
45
45
|
registerDbTools(pi);
|
|
46
46
|
|
|
47
|
-
assert.deepStrictEqual(pi.tools.length,
|
|
47
|
+
assert.deepStrictEqual(pi.tools.length, 27, 'Should register exactly 27 tools (13 canonical + 13 aliases + 1 gate tool)');
|
|
48
48
|
|
|
49
49
|
// ─── Both names exist for each pair ──────────────────────────────────────────
|
|
50
50
|
|
|
@@ -6,7 +6,8 @@ import { tmpdir } from "node:os";
|
|
|
6
6
|
import { randomUUID } from "node:crypto";
|
|
7
7
|
|
|
8
8
|
import { deriveState, isValidationTerminal } from "../state.ts";
|
|
9
|
-
import { resolveExpectedArtifactPath,
|
|
9
|
+
import { resolveExpectedArtifactPath, diagnoseExpectedArtifact } from "../auto-artifact-paths.ts";
|
|
10
|
+
import { verifyExpectedArtifact, buildLoopRemediationSteps } from "../auto-recovery.ts";
|
|
10
11
|
import { resolveDispatch, type DispatchContext } from "../auto-dispatch.ts";
|
|
11
12
|
import type { GSDState } from "../types.ts";
|
|
12
13
|
import { clearPathCache } from "../paths.ts";
|
|
@@ -226,8 +226,6 @@ describe("verification-gate: execution", () => {
|
|
|
226
226
|
|
|
227
227
|
test("all commands pass → gate passes", () => {
|
|
228
228
|
const result = runVerificationGate({
|
|
229
|
-
basePath: tmp,
|
|
230
|
-
unitId: "T01",
|
|
231
229
|
cwd: tmp,
|
|
232
230
|
preferenceCommands: ["echo hello", "echo world"],
|
|
233
231
|
});
|
|
@@ -243,8 +241,6 @@ describe("verification-gate: execution", () => {
|
|
|
243
241
|
|
|
244
242
|
test("one command fails → gate fails with exit code + stderr", () => {
|
|
245
243
|
const result = runVerificationGate({
|
|
246
|
-
basePath: tmp,
|
|
247
|
-
unitId: "T01",
|
|
248
244
|
cwd: tmp,
|
|
249
245
|
preferenceCommands: ["echo ok", "sh -c 'echo err >&2; exit 1'"],
|
|
250
246
|
});
|
|
@@ -257,8 +253,6 @@ describe("verification-gate: execution", () => {
|
|
|
257
253
|
|
|
258
254
|
test("no commands discovered → gate passes with 0 checks", () => {
|
|
259
255
|
const result = runVerificationGate({
|
|
260
|
-
basePath: tmp,
|
|
261
|
-
unitId: "T01",
|
|
262
256
|
cwd: tmp,
|
|
263
257
|
});
|
|
264
258
|
assert.equal(result.passed, true);
|
|
@@ -268,8 +262,6 @@ describe("verification-gate: execution", () => {
|
|
|
268
262
|
|
|
269
263
|
test("command not found → exit code 127", () => {
|
|
270
264
|
const result = runVerificationGate({
|
|
271
|
-
basePath: tmp,
|
|
272
|
-
unitId: "T01",
|
|
273
265
|
cwd: tmp,
|
|
274
266
|
preferenceCommands: ["__nonexistent_command_xyz_42__"],
|
|
275
267
|
});
|
|
@@ -289,8 +281,6 @@ describe("verification-gate: execution", () => {
|
|
|
289
281
|
const script = [
|
|
290
282
|
`import { runVerificationGate } from ${JSON.stringify(pathToFileURL(gatePath).href)};`,
|
|
291
283
|
`runVerificationGate({`,
|
|
292
|
-
` basePath: ${JSON.stringify(tmp)},`,
|
|
293
|
-
` unitId: "T-DEP",`,
|
|
294
284
|
` cwd: ${JSON.stringify(tmp)},`,
|
|
295
285
|
` preferenceCommands: ["echo dep0190-check"],`,
|
|
296
286
|
`});`,
|
|
@@ -317,8 +307,6 @@ describe("verification-gate: execution", () => {
|
|
|
317
307
|
|
|
318
308
|
test("each check has durationMs", () => {
|
|
319
309
|
const result = runVerificationGate({
|
|
320
|
-
basePath: tmp,
|
|
321
|
-
unitId: "T01",
|
|
322
310
|
cwd: tmp,
|
|
323
311
|
preferenceCommands: ["echo fast"],
|
|
324
312
|
});
|
|
@@ -330,8 +318,6 @@ describe("verification-gate: execution", () => {
|
|
|
330
318
|
test("one command fails — remaining commands still run (non-short-circuit)", () => {
|
|
331
319
|
// First fails, second and third should still execute
|
|
332
320
|
const result = runVerificationGate({
|
|
333
|
-
basePath: tmp,
|
|
334
|
-
unitId: "T02",
|
|
335
321
|
cwd: tmp,
|
|
336
322
|
preferenceCommands: [
|
|
337
323
|
"sh -c 'exit 1'",
|
|
@@ -351,8 +337,6 @@ describe("verification-gate: execution", () => {
|
|
|
351
337
|
test("gate execution uses cwd for spawnSync", () => {
|
|
352
338
|
// pwd should report the temp dir
|
|
353
339
|
const result = runVerificationGate({
|
|
354
|
-
basePath: tmp,
|
|
355
|
-
unitId: "T02",
|
|
356
340
|
cwd: tmp,
|
|
357
341
|
preferenceCommands: ["pwd"],
|
|
358
342
|
});
|
|
@@ -846,3 +846,70 @@ test("GitService is rebuilt with originalBasePath after exitMilestone", () => {
|
|
|
846
846
|
|
|
847
847
|
assert.equal(gitServiceBasePath, "/project"); // project root, not worktree
|
|
848
848
|
});
|
|
849
|
+
|
|
850
|
+
// ─── Isolation Degradation Tests (#2483) ──────────────────────────────────
|
|
851
|
+
|
|
852
|
+
test("enterMilestone sets isolationDegraded when worktree creation throws (#2483)", () => {
|
|
853
|
+
const s = makeSession();
|
|
854
|
+
const deps = makeDeps({
|
|
855
|
+
getAutoWorktreePath: () => null,
|
|
856
|
+
createAutoWorktree: () => {
|
|
857
|
+
throw new Error("empty repo");
|
|
858
|
+
},
|
|
859
|
+
});
|
|
860
|
+
const ctx = makeNotifyCtx();
|
|
861
|
+
const resolver = new WorktreeResolver(s, deps);
|
|
862
|
+
|
|
863
|
+
resolver.enterMilestone("M001", ctx);
|
|
864
|
+
|
|
865
|
+
assert.equal(s.isolationDegraded, true);
|
|
866
|
+
assert.equal(s.basePath, "/project"); // unchanged — error recovery
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
test("enterMilestone is no-op when isolationDegraded is true (#2483)", () => {
|
|
870
|
+
const s = makeSession();
|
|
871
|
+
s.isolationDegraded = true;
|
|
872
|
+
const deps = makeDeps();
|
|
873
|
+
const ctx = makeNotifyCtx();
|
|
874
|
+
const resolver = new WorktreeResolver(s, deps);
|
|
875
|
+
|
|
876
|
+
resolver.enterMilestone("M001", ctx);
|
|
877
|
+
|
|
878
|
+
assert.equal(s.basePath, "/project"); // unchanged
|
|
879
|
+
assert.equal(findCalls(deps.calls, "createAutoWorktree").length, 0);
|
|
880
|
+
assert.equal(findCalls(deps.calls, "enterAutoWorktree").length, 0);
|
|
881
|
+
assert.equal(findCalls(deps.calls, "shouldUseWorktreeIsolation").length, 0);
|
|
882
|
+
});
|
|
883
|
+
|
|
884
|
+
test("mergeAndExit is no-op when isolationDegraded is true (#2483)", () => {
|
|
885
|
+
const s = makeSession({
|
|
886
|
+
basePath: "/project",
|
|
887
|
+
originalBasePath: "/project",
|
|
888
|
+
});
|
|
889
|
+
s.isolationDegraded = true;
|
|
890
|
+
const deps = makeDeps({
|
|
891
|
+
getIsolationMode: () => "worktree",
|
|
892
|
+
});
|
|
893
|
+
const ctx = makeNotifyCtx();
|
|
894
|
+
const resolver = new WorktreeResolver(s, deps);
|
|
895
|
+
|
|
896
|
+
resolver.mergeAndExit("M001", ctx);
|
|
897
|
+
|
|
898
|
+
assert.equal(findCalls(deps.calls, "mergeMilestoneToMain").length, 0);
|
|
899
|
+
assert.equal(findCalls(deps.calls, "teardownAutoWorktree").length, 0);
|
|
900
|
+
assert.equal(findCalls(deps.calls, "getIsolationMode").length, 0);
|
|
901
|
+
assert.ok(
|
|
902
|
+
ctx.messages.some(
|
|
903
|
+
(m) => m.level === "info" && m.msg.includes("isolation was degraded"),
|
|
904
|
+
),
|
|
905
|
+
);
|
|
906
|
+
});
|
|
907
|
+
|
|
908
|
+
test("isolationDegraded is reset by session.reset() (#2483)", () => {
|
|
909
|
+
const s = new AutoSession();
|
|
910
|
+
s.isolationDegraded = true;
|
|
911
|
+
|
|
912
|
+
s.reset();
|
|
913
|
+
|
|
914
|
+
assert.equal(s.isolationDegraded, false);
|
|
915
|
+
});
|
|
@@ -27,7 +27,7 @@ import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, readFileSync
|
|
|
27
27
|
import { join } from 'node:path';
|
|
28
28
|
import { tmpdir } from 'node:os';
|
|
29
29
|
|
|
30
|
-
import { syncProjectRootToWorktree } from '../auto-worktree
|
|
30
|
+
import { syncProjectRootToWorktree } from '../auto-worktree.ts';
|
|
31
31
|
import { syncGsdStateToWorktree, syncWorktreeStateBack } from '../auto-worktree.ts';
|
|
32
32
|
import { describe, test } from 'node:test';
|
|
33
33
|
import assert from 'node:assert/strict';
|