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
|
@@ -349,6 +349,35 @@ export function ensureGsdSymlink(projectPath) {
|
|
|
349
349
|
if (localGsdNormalized === gsdHomePath) {
|
|
350
350
|
return localGsd;
|
|
351
351
|
}
|
|
352
|
+
// Guard: If projectPath is a plain subdirectory (not a worktree) of a git
|
|
353
|
+
// repo that already has a .gsd at the git root, do not create a duplicate
|
|
354
|
+
// symlink in the subdirectory — that causes `.gsd 2` collision variants on
|
|
355
|
+
// macOS (#2380). Worktrees are excluded because they legitimately need their
|
|
356
|
+
// own .gsd symlink pointing at the shared external state dir.
|
|
357
|
+
if (!inWorktree) {
|
|
358
|
+
try {
|
|
359
|
+
const gitRoot = resolveGitRoot(projectPath);
|
|
360
|
+
const normalizedProject = canonicalizeExistingPath(projectPath);
|
|
361
|
+
const normalizedRoot = canonicalizeExistingPath(gitRoot);
|
|
362
|
+
if (normalizedProject !== normalizedRoot) {
|
|
363
|
+
const rootGsd = join(gitRoot, ".gsd");
|
|
364
|
+
if (existsSync(rootGsd)) {
|
|
365
|
+
try {
|
|
366
|
+
const rootStat = lstatSync(rootGsd);
|
|
367
|
+
if (rootStat.isSymbolicLink() || rootStat.isDirectory()) {
|
|
368
|
+
return rootStat.isSymbolicLink() ? realpathSync(rootGsd) : rootGsd;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
catch {
|
|
372
|
+
// Fall through to normal logic if we can't stat root .gsd
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
catch {
|
|
378
|
+
// If git root detection fails, fall through to normal logic
|
|
379
|
+
}
|
|
380
|
+
}
|
|
352
381
|
// Clean up macOS numbered collision variants (.gsd 2, .gsd 3, etc.) before
|
|
353
382
|
// any existence checks — otherwise they accumulate and confuse state (#2205).
|
|
354
383
|
cleanNumberedGsdVariants(projectPath);
|
|
@@ -36,8 +36,8 @@ export function expandDependencies(deps) {
|
|
|
36
36
|
return result;
|
|
37
37
|
}
|
|
38
38
|
function extractSlicesSection(content) {
|
|
39
|
-
// Match "## Slices", "## Slice Overview", "## Slice Table", etc.
|
|
40
|
-
const headingMatch = /^## Slice(?:s| Overview| Table| Summary| Status)\b.*$/m.exec(content);
|
|
39
|
+
// Match "## Slices", "## Slice Overview", "## Slice Table", "## Slice Roadmap", etc.
|
|
40
|
+
const headingMatch = /^## Slice(?:s| Overview| Table| Summary| Status| Roadmap)\b.*$/m.exec(content);
|
|
41
41
|
if (!headingMatch || headingMatch.index == null)
|
|
42
42
|
return "";
|
|
43
43
|
const start = headingMatch.index + headingMatch[0].length;
|
|
@@ -24,7 +24,6 @@ import { truncateWithEllipsis } from "../shared/format-utils.js";
|
|
|
24
24
|
import { nativeParseJsonlTail } from "./native-parser-bridge.js";
|
|
25
25
|
import { MAX_JSONL_BYTES, parseJSONL } from "./jsonl-utils.js";
|
|
26
26
|
import { nativeWorkingTreeStatus, nativeDiffStat } from "./native-git-bridge.js";
|
|
27
|
-
import { getAutoWorktreePath } from "./auto-worktree.js";
|
|
28
27
|
// ─── JSONL Parsing ────────────────────────────────────────────────────────────
|
|
29
28
|
// MAX_JSONL_BYTES and parseJSONL are imported from ./jsonl-utils.js
|
|
30
29
|
/**
|
|
@@ -235,17 +234,13 @@ export function synthesizeCrashRecovery(basePath, unitType, unitId, sessionFile,
|
|
|
235
234
|
* Deep diagnostic from any JSONL source (activity log or session file).
|
|
236
235
|
* Replaces the old shallow getLastActivityDiagnostic().
|
|
237
236
|
*/
|
|
238
|
-
export function getDeepDiagnostic(basePath) {
|
|
239
|
-
// Try worktree activity logs first if
|
|
237
|
+
export function getDeepDiagnostic(basePath, worktreePath) {
|
|
238
|
+
// Try worktree activity logs first if a worktree path is provided
|
|
240
239
|
let trace = null;
|
|
241
240
|
try {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
if (wtPath) {
|
|
246
|
-
const wtActivityDir = join(gsdRoot(wtPath), "activity");
|
|
247
|
-
trace = readLastActivityLog(wtActivityDir);
|
|
248
|
-
}
|
|
241
|
+
if (worktreePath) {
|
|
242
|
+
const wtActivityDir = join(gsdRoot(worktreePath), "activity");
|
|
243
|
+
trace = readLastActivityLog(wtActivityDir);
|
|
249
244
|
}
|
|
250
245
|
}
|
|
251
246
|
catch { /* non-fatal — fall through to root */ }
|
|
@@ -262,7 +257,7 @@ export function getDeepDiagnostic(basePath) {
|
|
|
262
257
|
* Read the active milestone ID directly from STATE.md without async deriveState().
|
|
263
258
|
* Looks for `**Active Milestone:** M001` pattern.
|
|
264
259
|
*/
|
|
265
|
-
function readActiveMilestoneId(basePath) {
|
|
260
|
+
export function readActiveMilestoneId(basePath) {
|
|
266
261
|
try {
|
|
267
262
|
const statePath = join(gsdRoot(basePath), "STATE.md");
|
|
268
263
|
if (!existsSync(statePath))
|
|
@@ -134,6 +134,49 @@ function ensureExitHandler(_gsdDir) {
|
|
|
134
134
|
}
|
|
135
135
|
});
|
|
136
136
|
}
|
|
137
|
+
// ─── Lock Acquisition Helpers ───────────────────────────────────────────────
|
|
138
|
+
/**
|
|
139
|
+
* Create the onCompromised callback for proper-lockfile.
|
|
140
|
+
*
|
|
141
|
+
* proper-lockfile fires onCompromised when it detects mtime drift (system sleep,
|
|
142
|
+
* event loop stall, etc.). The default handler throws inside setTimeout — an
|
|
143
|
+
* uncaught exception that crashes or corrupts process state.
|
|
144
|
+
*
|
|
145
|
+
* False-positive suppression (#1362): If we're still within the stale window
|
|
146
|
+
* (30 min since acquisition), the mtime mismatch is from an event loop stall
|
|
147
|
+
* during a long LLM call — not a real takeover. Log and continue.
|
|
148
|
+
*
|
|
149
|
+
* PID ownership check (#1578): Past the stale window, check if the lock file
|
|
150
|
+
* still contains our PID before declaring compromise. Retry reads tolerate
|
|
151
|
+
* transient filesystem hiccups (NFS/CIFS latency, APFS snapshots, etc.) (#2324).
|
|
152
|
+
*/
|
|
153
|
+
function createLockCompromisedHandler(lockFilePath) {
|
|
154
|
+
return () => {
|
|
155
|
+
const elapsed = Date.now() - _lockAcquiredAt;
|
|
156
|
+
if (elapsed < 1_800_000) {
|
|
157
|
+
process.stderr.write(`[gsd] Lock heartbeat caught up after ${Math.round(elapsed / 1000)}s — long LLM call, no action needed.\n`);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const existing = readExistingLockDataWithRetry(lockFilePath);
|
|
161
|
+
if (existing && existing.pid === process.pid) {
|
|
162
|
+
process.stderr.write(`[gsd] Lock heartbeat mismatch after ${Math.round(elapsed / 1000)}s — lock file still owned by PID ${process.pid}, treating as false positive.\n`);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
_lockCompromised = true;
|
|
166
|
+
_releaseFunction = null;
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Assign module-level lock state after a successful lock acquisition.
|
|
171
|
+
*/
|
|
172
|
+
function assignLockState(basePath, release, lockFilePath) {
|
|
173
|
+
_releaseFunction = release;
|
|
174
|
+
_lockedPath = basePath;
|
|
175
|
+
_lockPid = process.pid;
|
|
176
|
+
_lockCompromised = false;
|
|
177
|
+
_lockAcquiredAt = Date.now();
|
|
178
|
+
_snapshotLockPath = lockFilePath;
|
|
179
|
+
}
|
|
137
180
|
// ─── Public API ─────────────────────────────────────────────────────────────
|
|
138
181
|
/**
|
|
139
182
|
* Attempt to acquire an exclusive session lock for the given project.
|
|
@@ -188,38 +231,9 @@ export function acquireSessionLock(basePath) {
|
|
|
188
231
|
realpath: false,
|
|
189
232
|
stale: 1_800_000, // 30 minutes — safe for laptop sleep / long event loop stalls
|
|
190
233
|
update: 10_000, // Update lock mtime every 10s to prove liveness
|
|
191
|
-
onCompromised: ()
|
|
192
|
-
// proper-lockfile detected mtime drift (system sleep, event loop stall, etc.).
|
|
193
|
-
// Default handler throws inside setTimeout — an uncaught exception that crashes
|
|
194
|
-
// or corrupts process state.
|
|
195
|
-
//
|
|
196
|
-
// False-positive suppression (#1362): If we're still within the stale window
|
|
197
|
-
// (30 min since acquisition), the mtime mismatch is from an event loop stall
|
|
198
|
-
// during a long LLM call — not a real takeover. Log and continue.
|
|
199
|
-
const elapsed = Date.now() - _lockAcquiredAt;
|
|
200
|
-
if (elapsed < 1_800_000) {
|
|
201
|
-
process.stderr.write(`[gsd] Lock heartbeat caught up after ${Math.round(elapsed / 1000)}s — long LLM call, no action needed.\n`);
|
|
202
|
-
return; // Suppress false positive
|
|
203
|
-
}
|
|
204
|
-
// Past the stale window — check if the lock file still belongs to us before
|
|
205
|
-
// declaring compromise (#1578). If our PID still owns the metadata, this is
|
|
206
|
-
// a false positive from a very long event loop stall (e.g. subagent execution).
|
|
207
|
-
const existing = readExistingLockData(lp);
|
|
208
|
-
if (existing && existing.pid === process.pid) {
|
|
209
|
-
process.stderr.write(`[gsd] Lock heartbeat mismatch after ${Math.round(elapsed / 1000)}s — lock file still owned by PID ${process.pid}, treating as false positive.\n`);
|
|
210
|
-
return; // Our PID still owns the lock file — no real takeover
|
|
211
|
-
}
|
|
212
|
-
// Lock file is gone or owned by another PID — real compromise
|
|
213
|
-
_lockCompromised = true;
|
|
214
|
-
_releaseFunction = null;
|
|
215
|
-
},
|
|
234
|
+
onCompromised: createLockCompromisedHandler(lp),
|
|
216
235
|
});
|
|
217
|
-
|
|
218
|
-
_lockedPath = basePath;
|
|
219
|
-
_lockPid = process.pid;
|
|
220
|
-
_lockCompromised = false;
|
|
221
|
-
_lockAcquiredAt = Date.now();
|
|
222
|
-
_snapshotLockPath = lp; // Snapshot the resolved path for consistent access (#1363)
|
|
236
|
+
assignLockState(basePath, release, lp);
|
|
223
237
|
// Safety net: clean up lock dir on process exit if _releaseFunction
|
|
224
238
|
// wasn't called (e.g., normal exit after clean completion) (#1245).
|
|
225
239
|
ensureExitHandler(gsdDir);
|
|
@@ -245,31 +259,9 @@ export function acquireSessionLock(basePath) {
|
|
|
245
259
|
realpath: false,
|
|
246
260
|
stale: 1_800_000, // 30 minutes — match primary lock settings
|
|
247
261
|
update: 10_000,
|
|
248
|
-
onCompromised: ()
|
|
249
|
-
// Same false-positive suppression as the primary lock (#1512).
|
|
250
|
-
// Without this, the retry path fires _lockCompromised unconditionally
|
|
251
|
-
// on benign mtime drift (laptop sleep, heavy LLM event loop stalls).
|
|
252
|
-
const elapsed = Date.now() - _lockAcquiredAt;
|
|
253
|
-
if (elapsed < 1_800_000) {
|
|
254
|
-
process.stderr.write(`[gsd] Lock heartbeat caught up after ${Math.round(elapsed / 1000)}s — long LLM call, no action needed.\n`);
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
// Check PID ownership before declaring compromise (#1578)
|
|
258
|
-
const existing = readExistingLockData(lp);
|
|
259
|
-
if (existing && existing.pid === process.pid) {
|
|
260
|
-
process.stderr.write(`[gsd] Lock heartbeat mismatch after ${Math.round(elapsed / 1000)}s — lock file still owned by PID ${process.pid}, treating as false positive.\n`);
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
_lockCompromised = true;
|
|
264
|
-
_releaseFunction = null;
|
|
265
|
-
},
|
|
262
|
+
onCompromised: createLockCompromisedHandler(lp),
|
|
266
263
|
});
|
|
267
|
-
|
|
268
|
-
_lockedPath = basePath;
|
|
269
|
-
_lockPid = process.pid;
|
|
270
|
-
_lockCompromised = false;
|
|
271
|
-
_lockAcquiredAt = Date.now();
|
|
272
|
-
_snapshotLockPath = lp; // Snapshot for retry path too (#1363)
|
|
264
|
+
assignLockState(basePath, release, lp);
|
|
273
265
|
// Safety net — uses centralized handler to avoid double-registration
|
|
274
266
|
ensureExitHandler(gsdDir);
|
|
275
267
|
atomicWriteSync(lp, JSON.stringify(lockData, null, 2));
|
|
@@ -348,7 +340,8 @@ export function getSessionLockStatus(basePath) {
|
|
|
348
340
|
// onCompromised fired from benign mtime drift (laptop sleep, event loop stall
|
|
349
341
|
// beyond the stale window). Attempt re-acquisition instead of giving up.
|
|
350
342
|
const lp = lockPath(basePath);
|
|
351
|
-
|
|
343
|
+
// Retry reads to tolerate transient filesystem hiccups (#2324).
|
|
344
|
+
const existing = readExistingLockDataWithRetry(lp);
|
|
352
345
|
if (existing && existing.pid === process.pid) {
|
|
353
346
|
// Lock file still ours — try to re-acquire the OS lock
|
|
354
347
|
try {
|
|
@@ -492,6 +485,24 @@ function readExistingLockData(lp) {
|
|
|
492
485
|
return null;
|
|
493
486
|
}
|
|
494
487
|
}
|
|
488
|
+
export function readExistingLockDataWithRetry(lp, options) {
|
|
489
|
+
const maxAttempts = options?.maxAttempts ?? 3;
|
|
490
|
+
const delayMs = options?.delayMs ?? 200;
|
|
491
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
492
|
+
const data = readExistingLockData(lp);
|
|
493
|
+
if (data !== null)
|
|
494
|
+
return data;
|
|
495
|
+
if (attempt < maxAttempts) {
|
|
496
|
+
// Synchronous busy-wait — onCompromised runs in a sync callback context
|
|
497
|
+
// and the delays are short (200ms default).
|
|
498
|
+
const start = Date.now();
|
|
499
|
+
while (Date.now() - start < delayMs) {
|
|
500
|
+
// busy-wait
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
495
506
|
function isPidAlive(pid) {
|
|
496
507
|
if (!Number.isInteger(pid) || pid <= 0)
|
|
497
508
|
return false;
|
|
@@ -9,7 +9,8 @@ import { nativeBatchParseGsdFiles } from './native-parser-bridge.js';
|
|
|
9
9
|
import { join, resolve } from 'path';
|
|
10
10
|
import { existsSync, readdirSync } from 'node:fs';
|
|
11
11
|
import { debugCount, debugTime } from './debug-logger.js';
|
|
12
|
-
import {
|
|
12
|
+
import { extractVerdict } from './verdict-parser.js';
|
|
13
|
+
import { isDbAvailable, getAllMilestones, getMilestoneSlices, getSliceTasks, getReplanHistory, getSlice, insertMilestone, updateTaskStatus, getPendingSliceGateCount, } from './gsd-db.js';
|
|
13
14
|
/**
|
|
14
15
|
* A "ghost" milestone directory contains only META.json (and no substantive
|
|
15
16
|
* files like CONTEXT, CONTEXT-DRAFT, ROADMAP, or SUMMARY). These appear when
|
|
@@ -42,13 +43,9 @@ export function isMilestoneComplete(roadmap) {
|
|
|
42
43
|
* after remediation slices are executed.
|
|
43
44
|
*/
|
|
44
45
|
export function isValidationTerminal(validationContent) {
|
|
45
|
-
const
|
|
46
|
-
if (!
|
|
46
|
+
const v = extractVerdict(validationContent);
|
|
47
|
+
if (!v)
|
|
47
48
|
return false;
|
|
48
|
-
const verdict = match[1].match(/verdict:\s*(\S+)/);
|
|
49
|
-
if (!verdict)
|
|
50
|
-
return false;
|
|
51
|
-
const v = verdict[1] === 'passed' ? 'pass' : verdict[1];
|
|
52
49
|
// 'pass' and 'needs-attention' are always terminal.
|
|
53
50
|
// 'needs-remediation' is treated as terminal to prevent infinite loops
|
|
54
51
|
// when no remediation slices exist in the roadmap (#832). The validation
|
|
@@ -595,6 +592,21 @@ export async function deriveStateFromDb(basePath) {
|
|
|
595
592
|
};
|
|
596
593
|
}
|
|
597
594
|
}
|
|
595
|
+
// ── Quality gate evaluation check ──────────────────────────────────
|
|
596
|
+
// If slice-scoped gates (Q3/Q4) are still pending, pause before execution
|
|
597
|
+
// so the gate-evaluate dispatch rule can run parallel sub-agents.
|
|
598
|
+
// Slices with zero gate rows (pre-feature or simple) skip straight through.
|
|
599
|
+
const pendingGateCount = getPendingSliceGateCount(activeMilestone.id, activeSlice.id);
|
|
600
|
+
if (pendingGateCount > 0) {
|
|
601
|
+
return {
|
|
602
|
+
activeMilestone, activeSlice, activeTask: null,
|
|
603
|
+
phase: 'evaluating-gates',
|
|
604
|
+
recentDecisions: [], blockers: [],
|
|
605
|
+
nextAction: `Evaluate ${pendingGateCount} quality gate(s) for ${activeSlice.id} before execution.`,
|
|
606
|
+
registry, requirements,
|
|
607
|
+
progress: { milestones: milestoneProgress, slices: sliceProgress, tasks: taskProgress },
|
|
608
|
+
};
|
|
609
|
+
}
|
|
598
610
|
// ── Blocker detection: check completed tasks for blocker_discovered ──
|
|
599
611
|
const completedTasks = tasks.filter(t => isStatusDone(t.status));
|
|
600
612
|
let blockerTaskId = null;
|
|
@@ -1143,6 +1155,21 @@ export async function _deriveStateImpl(basePath) {
|
|
|
1143
1155
|
};
|
|
1144
1156
|
}
|
|
1145
1157
|
const slicePlan = parsePlan(slicePlanContent);
|
|
1158
|
+
// ── Reconcile stale task status for filesystem-based projects (#2514) ──
|
|
1159
|
+
// Heading-style tasks (### T01:) are always parsed as done=false by
|
|
1160
|
+
// parsePlan because the heading syntax has no checkbox. When the agent
|
|
1161
|
+
// writes a SUMMARY file but the plan's heading isn't converted to a
|
|
1162
|
+
// checkbox, the task appears incomplete forever — causing infinite
|
|
1163
|
+
// re-dispatch. Reconcile by checking SUMMARY files on disk.
|
|
1164
|
+
for (const t of slicePlan.tasks) {
|
|
1165
|
+
if (t.done)
|
|
1166
|
+
continue;
|
|
1167
|
+
const summaryPath = resolveTaskFile(basePath, activeMilestone.id, activeSlice.id, t.id, "SUMMARY");
|
|
1168
|
+
if (summaryPath && existsSync(summaryPath)) {
|
|
1169
|
+
t.done = true;
|
|
1170
|
+
process.stderr.write(`gsd-reconcile: task ${activeMilestone.id}/${activeSlice.id}/${t.id} has SUMMARY on disk but plan shows incomplete — marking done (#2514)\n`);
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1146
1173
|
const taskProgress = {
|
|
1147
1174
|
done: slicePlan.tasks.filter(t => t.done).length,
|
|
1148
1175
|
total: slicePlan.tasks.length,
|
|
@@ -49,6 +49,14 @@ completed_at: {{date}}
|
|
|
49
49
|
|
|
50
50
|
- {{requirementId}}: {{fromStatus}} → {{toStatus}} — {{evidence}}
|
|
51
51
|
|
|
52
|
+
## Decision Re-evaluation
|
|
53
|
+
|
|
54
|
+
<!-- Review decisions from this milestone. OMIT if no decisions need re-evaluation. -->
|
|
55
|
+
|
|
56
|
+
| Decision | Original Rationale | Still Valid? | Action |
|
|
57
|
+
|----------|-------------------|-------------|--------|
|
|
58
|
+
| {{decisionId}} | {{originalRationale}} | {{yes/no/partially}} | {{keep/revise/supersede}} |
|
|
59
|
+
|
|
52
60
|
## Forward Intelligence
|
|
53
61
|
|
|
54
62
|
<!-- Write what you wish you'd known at the start of this milestone.
|
|
@@ -8,6 +8,22 @@
|
|
|
8
8
|
- {{mustHave}}
|
|
9
9
|
- {{mustHave}}
|
|
10
10
|
|
|
11
|
+
## Threat Surface
|
|
12
|
+
|
|
13
|
+
<!-- Q3: How can this be exploited? OMIT ENTIRELY for simple slices with no auth, user input, or data exposure. -->
|
|
14
|
+
|
|
15
|
+
- **Abuse**: {{abuseScenarios — parameter tampering, replay, privilege escalation, or N/A}}
|
|
16
|
+
- **Data exposure**: {{sensitiveDataAccessible — PII, tokens, secrets, or none}}
|
|
17
|
+
- **Input trust**: {{untrustedInput — user input reaching DB/API/filesystem, or none}}
|
|
18
|
+
|
|
19
|
+
## Requirement Impact
|
|
20
|
+
|
|
21
|
+
<!-- Q4: What existing promises does this break? OMIT ENTIRELY if no existing requirements are affected. -->
|
|
22
|
+
|
|
23
|
+
- **Requirements touched**: {{requirementIds — e.g. R001, R003, or none}}
|
|
24
|
+
- **Re-verify**: {{whatMustBeRetested — e.g. login flow, API contract, or N/A}}
|
|
25
|
+
- **Decisions revisited**: {{decisionIds — e.g. D002, or none}}
|
|
26
|
+
|
|
11
27
|
## Proof Level
|
|
12
28
|
|
|
13
29
|
<!-- Omit this section entirely for simple slices where the answer is trivially obvious. -->
|
|
@@ -92,6 +92,19 @@ This milestone is complete only when all are true:
|
|
|
92
92
|
- Each "After this" line must be truthful about proof level: if only fixtures or tests prove it, say so; do not imply the user can already perform the live end-to-end behavior unless that has actually been exercised
|
|
93
93
|
-->
|
|
94
94
|
|
|
95
|
+
## Horizontal Checklist
|
|
96
|
+
|
|
97
|
+
<!-- Cross-cutting concerns across all slices. Check each that was considered.
|
|
98
|
+
OMIT ENTIRELY for trivial milestones. -->
|
|
99
|
+
|
|
100
|
+
- [ ] Every active R### re-read against new code — still fully satisfied?
|
|
101
|
+
- [ ] Every D### from prior milestones re-evaluated — still valid at new scope?
|
|
102
|
+
- [ ] Graceful shutdown / cleanup on termination verified
|
|
103
|
+
- [ ] Revenue / billing path impact assessed (or N/A)
|
|
104
|
+
- [ ] Auth boundary documented — what's protected vs public
|
|
105
|
+
- [ ] Shared resource budget confirmed — connection pools, caches, rate limits hold under peak
|
|
106
|
+
- [ ] Reconnection / retry strategy verified for every external dependency
|
|
107
|
+
|
|
95
108
|
## Boundary Map
|
|
96
109
|
|
|
97
110
|
<!-- Be specific. Name concrete outputs: API endpoints, event payloads, shared types/interfaces,
|
|
@@ -57,6 +57,15 @@ completed_at: {{date}}
|
|
|
57
57
|
|
|
58
58
|
- {{requirementIdOr_none}} — {{what changed}}
|
|
59
59
|
|
|
60
|
+
## Operational Readiness
|
|
61
|
+
|
|
62
|
+
<!-- Q8: How will ops know it's healthy/broken? OMIT ENTIRELY for simple slices with no runtime concerns. -->
|
|
63
|
+
|
|
64
|
+
- **Health signal**: {{howToConfirmHealthy — health endpoint, heartbeat log, metric, or N/A}}
|
|
65
|
+
- **Failure signal**: {{howToDetectBroken — error rate spike, alert, log pattern, or N/A}}
|
|
66
|
+
- **Recovery**: {{selfRecoverOrRestart — auto-reconnect, circuit breaker, manual restart, or N/A}}
|
|
67
|
+
- **Monitoring gaps**: {{silentFailureModes — background jobs, cache eviction, memory pressure, or none}}
|
|
68
|
+
|
|
60
69
|
## Deviations
|
|
61
70
|
|
|
62
71
|
<!-- Deviations are unplanned changes to the written plan, not ordinary debugging inside the plan's intended scope. -->
|
|
@@ -17,6 +17,30 @@ skills_used:
|
|
|
17
17
|
|
|
18
18
|
{{description}}
|
|
19
19
|
|
|
20
|
+
## Failure Modes
|
|
21
|
+
|
|
22
|
+
<!-- Q5: What breaks when dependencies fail? OMIT ENTIRELY for tasks with no external dependencies. -->
|
|
23
|
+
|
|
24
|
+
| Dependency | On error | On timeout | On malformed response |
|
|
25
|
+
|------------|----------|-----------|----------------------|
|
|
26
|
+
| {{dependency}} | {{errorStrategy}} | {{timeoutStrategy}} | {{malformedStrategy}} |
|
|
27
|
+
|
|
28
|
+
## Load Profile
|
|
29
|
+
|
|
30
|
+
<!-- Q6: What breaks at 10x load? OMIT ENTIRELY for tasks with no shared resources or scaling concerns. -->
|
|
31
|
+
|
|
32
|
+
- **Shared resources**: {{sharedResources — DB connections, caches, rate limiters, or none}}
|
|
33
|
+
- **Per-operation cost**: {{perOpCost — N API calls, M DB queries, K bytes, or trivial}}
|
|
34
|
+
- **10x breakpoint**: {{whatBreaksFirst — pool exhaustion, rate limit, memory, or N/A}}
|
|
35
|
+
|
|
36
|
+
## Negative Tests
|
|
37
|
+
|
|
38
|
+
<!-- Q7: What negative tests prove robustness? OMIT ENTIRELY for trivial tasks. -->
|
|
39
|
+
|
|
40
|
+
- **Malformed inputs**: {{malformedInputTests — empty string, null, oversized, wrong type}}
|
|
41
|
+
- **Error paths**: {{errorPathTests — network timeout, auth failure, 5xx, invalid JSON}}
|
|
42
|
+
- **Boundary conditions**: {{boundaryTests — empty list, max length, zero, off-by-one}}
|
|
43
|
+
|
|
20
44
|
## Steps
|
|
21
45
|
|
|
22
46
|
1. {{step}}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { clearParseCache } from "../files.js";
|
|
2
|
-
import { transaction, getMilestone, getSlice, insertTask, upsertSlicePlanning, upsertTaskPlanning, } from "../gsd-db.js";
|
|
2
|
+
import { transaction, getMilestone, getSlice, insertTask, upsertSlicePlanning, upsertTaskPlanning, insertGateRow, } from "../gsd-db.js";
|
|
3
3
|
import { invalidateStateCache } from "../state.js";
|
|
4
4
|
import { renderPlanFromDb } from "../markdown-renderer.js";
|
|
5
5
|
import { renderAllProjections } from "../workflow-projections.js";
|
|
@@ -145,6 +145,19 @@ export async function handlePlanSlice(rawParams, basePath) {
|
|
|
145
145
|
fullPlanMd: task.fullPlanMd,
|
|
146
146
|
});
|
|
147
147
|
}
|
|
148
|
+
// Seed quality gate rows inside the transaction — all-or-nothing with
|
|
149
|
+
// the plan data so a crash can't leave orphaned gates without tasks.
|
|
150
|
+
const sliceGates = ["Q3", "Q4"];
|
|
151
|
+
for (const gid of sliceGates) {
|
|
152
|
+
insertGateRow({ milestoneId: params.milestoneId, sliceId: params.sliceId, gateId: gid, scope: "slice" });
|
|
153
|
+
}
|
|
154
|
+
const taskGates = ["Q5", "Q6", "Q7"];
|
|
155
|
+
for (const task of params.tasks) {
|
|
156
|
+
for (const gid of taskGates) {
|
|
157
|
+
insertGateRow({ milestoneId: params.milestoneId, sliceId: params.sliceId, gateId: gid, scope: "task", taskId: task.taskId });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
insertGateRow({ milestoneId: params.milestoneId, sliceId: params.sliceId, gateId: "Q8", scope: "slice" });
|
|
148
161
|
});
|
|
149
162
|
}
|
|
150
163
|
catch (err) {
|
|
@@ -9,6 +9,7 @@ import { transaction, _getAdapter, } from "../gsd-db.js";
|
|
|
9
9
|
import { resolveMilestonePath, clearPathCache } from "../paths.js";
|
|
10
10
|
import { saveFile, clearParseCache } from "../files.js";
|
|
11
11
|
import { invalidateStateCache } from "../state.js";
|
|
12
|
+
import { VALIDATION_VERDICTS, isValidMilestoneVerdict } from "../verdict-parser.js";
|
|
12
13
|
function renderValidationMarkdown(params) {
|
|
13
14
|
let md = `---
|
|
14
15
|
verdict: ${params.verdict}
|
|
@@ -41,9 +42,8 @@ export async function handleValidateMilestone(params, basePath) {
|
|
|
41
42
|
if (!params.milestoneId || typeof params.milestoneId !== "string" || params.milestoneId.trim() === "") {
|
|
42
43
|
return { error: "milestoneId is required and must be a non-empty string" };
|
|
43
44
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return { error: `verdict must be one of: ${validVerdicts.join(", ")}` };
|
|
45
|
+
if (!isValidMilestoneVerdict(params.verdict)) {
|
|
46
|
+
return { error: `verdict must be one of: ${VALIDATION_VERDICTS.join(", ")}` };
|
|
47
47
|
}
|
|
48
48
|
// ── Filesystem render ──────────────────────────────────────────────────
|
|
49
49
|
const validationMd = renderValidationMarkdown(params);
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized verdict extraction, normalization, and schema validation.
|
|
3
|
+
*
|
|
4
|
+
* All verdict-related logic lives here so that normalization rules
|
|
5
|
+
* (e.g. `passed` → `pass`) are applied consistently across the codebase.
|
|
6
|
+
*/
|
|
7
|
+
import { extractUatType } from "./files.js";
|
|
8
|
+
// ── Verdict extraction ──────────────────────────────────────────────────
|
|
9
|
+
/**
|
|
10
|
+
* Extract and normalize the `verdict` value from YAML frontmatter.
|
|
11
|
+
*
|
|
12
|
+
* Normalization:
|
|
13
|
+
* - lowercased
|
|
14
|
+
* - `passed` → `pass`
|
|
15
|
+
*
|
|
16
|
+
* Returns `undefined` when frontmatter is absent or has no `verdict` field.
|
|
17
|
+
*/
|
|
18
|
+
export function extractVerdict(content) {
|
|
19
|
+
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
20
|
+
if (!fmMatch)
|
|
21
|
+
return undefined;
|
|
22
|
+
const verdictMatch = fmMatch[1].match(/verdict:\s*([\w-]+)/i);
|
|
23
|
+
if (!verdictMatch)
|
|
24
|
+
return undefined;
|
|
25
|
+
let v = verdictMatch[1].toLowerCase();
|
|
26
|
+
if (v === "passed")
|
|
27
|
+
v = "pass";
|
|
28
|
+
return v;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Returns `true` when the content's frontmatter contains a `verdict` field.
|
|
32
|
+
*/
|
|
33
|
+
export function hasVerdict(content) {
|
|
34
|
+
return /verdict:\s*[\w-]+/i.test(content);
|
|
35
|
+
}
|
|
36
|
+
// ── UAT verdict schema ──────────────────────────────────────────────────
|
|
37
|
+
/**
|
|
38
|
+
* Base verdicts that are always acceptable for UAT results.
|
|
39
|
+
*/
|
|
40
|
+
export const UAT_ACCEPTABLE_VERDICTS = ["pass", "passed"];
|
|
41
|
+
/**
|
|
42
|
+
* UAT types whose results may legitimately produce a `partial` verdict
|
|
43
|
+
* when all automatable checks pass but human-only checks remain.
|
|
44
|
+
*/
|
|
45
|
+
const PARTIAL_ELIGIBLE_UAT_TYPES = [
|
|
46
|
+
"mixed",
|
|
47
|
+
"human-experience",
|
|
48
|
+
"live-runtime",
|
|
49
|
+
];
|
|
50
|
+
/**
|
|
51
|
+
* Check whether a verdict is acceptable for a given UAT type.
|
|
52
|
+
*
|
|
53
|
+
* `pass` / `passed` are always acceptable. `partial` is acceptable only for
|
|
54
|
+
* UAT types that include non-automatable human checks.
|
|
55
|
+
*/
|
|
56
|
+
export function isAcceptableUatVerdict(verdict, uatType) {
|
|
57
|
+
if (UAT_ACCEPTABLE_VERDICTS.includes(verdict))
|
|
58
|
+
return true;
|
|
59
|
+
if (verdict === "partial" && uatType && PARTIAL_ELIGIBLE_UAT_TYPES.includes(uatType)) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
// ── Milestone validation verdict schema ─────────────────────────────────
|
|
65
|
+
/**
|
|
66
|
+
* Valid verdicts for the `validate-milestone` tool.
|
|
67
|
+
*/
|
|
68
|
+
export const VALIDATION_VERDICTS = ["pass", "needs-attention", "needs-remediation"];
|
|
69
|
+
/**
|
|
70
|
+
* Check whether a string is a valid milestone validation verdict.
|
|
71
|
+
*/
|
|
72
|
+
export function isValidMilestoneVerdict(verdict) {
|
|
73
|
+
return VALIDATION_VERDICTS.includes(verdict);
|
|
74
|
+
}
|
|
75
|
+
// ── UAT type helper ─────────────────────────────────────────────────────
|
|
76
|
+
/**
|
|
77
|
+
* Extract the UAT type from content, defaulting to `"artifact-driven"`.
|
|
78
|
+
*
|
|
79
|
+
* The `"artifact-driven"` fallback is the original default used throughout
|
|
80
|
+
* the codebase when a UAT file lacks an explicit `## UAT Type` section.
|
|
81
|
+
*/
|
|
82
|
+
export function getUatType(content) {
|
|
83
|
+
return extractUatType(content) ?? "artifact-driven";
|
|
84
|
+
}
|
|
@@ -548,7 +548,7 @@ async function handleMerge(basePath, name, ctx, pi, targetBranch) {
|
|
|
548
548
|
// --- Deterministic merge path (preferred) ---
|
|
549
549
|
// Try a direct squash-merge first. Only fall back to LLM on conflict.
|
|
550
550
|
const commitType = inferCommitType(name);
|
|
551
|
-
const commitMessage = `${commitType}
|
|
551
|
+
const commitMessage = `${commitType}: merge worktree ${name}\n\nGSD-Worktree: ${name}`;
|
|
552
552
|
// Reconcile worktree DB into main DB before squash merge
|
|
553
553
|
const wtDbPath = join(worktreePath(basePath, name), ".gsd", "gsd.db");
|
|
554
554
|
const mainDbPath = join(basePath, ".gsd", "gsd.db");
|
|
@@ -72,6 +72,16 @@ export class WorktreeResolver {
|
|
|
72
72
|
*/
|
|
73
73
|
enterMilestone(milestoneId, ctx) {
|
|
74
74
|
this.validateMilestoneId(milestoneId);
|
|
75
|
+
// If worktree creation failed earlier this session, skip all future attempts
|
|
76
|
+
if (this.s.isolationDegraded) {
|
|
77
|
+
debugLog("WorktreeResolver", {
|
|
78
|
+
action: "enterMilestone",
|
|
79
|
+
milestoneId,
|
|
80
|
+
skipped: true,
|
|
81
|
+
reason: "isolation-degraded",
|
|
82
|
+
});
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
75
85
|
if (!this.deps.shouldUseWorktreeIsolation()) {
|
|
76
86
|
debugLog("WorktreeResolver", {
|
|
77
87
|
action: "enterMilestone",
|
|
@@ -136,6 +146,9 @@ export class WorktreeResolver {
|
|
|
136
146
|
data: { milestoneId, error: msg, fallback: "project-root" },
|
|
137
147
|
});
|
|
138
148
|
ctx.notify(`Auto-worktree creation for ${milestoneId} failed: ${msg}. Continuing in project root.`, "warning");
|
|
149
|
+
// Degrade isolation for the rest of this session so mergeAndExit
|
|
150
|
+
// doesn't try to merge a nonexistent worktree branch (#2483)
|
|
151
|
+
this.s.isolationDegraded = true;
|
|
139
152
|
// Do NOT update s.basePath — stay in project root
|
|
140
153
|
}
|
|
141
154
|
}
|
|
@@ -210,6 +223,17 @@ export class WorktreeResolver {
|
|
|
210
223
|
*/
|
|
211
224
|
mergeAndExit(milestoneId, ctx) {
|
|
212
225
|
this.validateMilestoneId(milestoneId);
|
|
226
|
+
// If worktree creation failed earlier, skip merge — work is on current branch (#2483)
|
|
227
|
+
if (this.s.isolationDegraded) {
|
|
228
|
+
debugLog("WorktreeResolver", {
|
|
229
|
+
action: "mergeAndExit",
|
|
230
|
+
milestoneId,
|
|
231
|
+
skipped: true,
|
|
232
|
+
reason: "isolation-degraded",
|
|
233
|
+
});
|
|
234
|
+
ctx.notify(`Skipping worktree merge for ${milestoneId} — isolation was degraded (worktree creation failed earlier). Work is on the current branch.`, "info");
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
213
237
|
const mode = this.deps.getIsolationMode();
|
|
214
238
|
debugLog("WorktreeResolver", {
|
|
215
239
|
action: "mergeAndExit",
|
|
@@ -220,8 +220,9 @@ export function getSliceBranchName(milestoneId, sliceId, worktreeName) {
|
|
|
220
220
|
}
|
|
221
221
|
return `gsd/${milestoneId}/${sliceId}`;
|
|
222
222
|
}
|
|
223
|
-
/**
|
|
224
|
-
export
|
|
223
|
+
/** Re-export for backward compatibility — canonical definition in branch-patterns.ts */
|
|
224
|
+
export { SLICE_BRANCH_RE } from "./branch-patterns.js";
|
|
225
|
+
import { SLICE_BRANCH_RE } from "./branch-patterns.js";
|
|
225
226
|
/**
|
|
226
227
|
* Parse a slice branch name into its components.
|
|
227
228
|
* Handles both `gsd/M001/S01` and `gsd/myworktree/M001/S01`.
|