gsd-pi 2.45.0 → 2.46.0-dev.cc9d310
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/help-text.js +1 -1
- package/dist/loader.js +34 -0
- package/dist/resources/extensions/gsd/auto/phases.js +27 -42
- package/dist/resources/extensions/gsd/auto/run-unit.js +6 -3
- package/dist/resources/extensions/gsd/auto/session.js +0 -11
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +112 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +25 -96
- package/dist/resources/extensions/gsd/auto-start.js +2 -3
- package/dist/resources/extensions/gsd/auto-worktree.js +5 -4
- package/dist/resources/extensions/gsd/auto.js +12 -57
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +15 -12
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +18 -0
- package/dist/resources/extensions/gsd/commands/context.js +0 -4
- package/dist/resources/extensions/gsd/commands/handlers/parallel.js +1 -1
- package/dist/resources/extensions/gsd/crash-recovery.js +2 -4
- package/dist/resources/extensions/gsd/dashboard-overlay.js +0 -44
- package/dist/resources/extensions/gsd/db-writer.js +9 -9
- package/dist/resources/extensions/gsd/doctor-checks.js +167 -2
- package/dist/resources/extensions/gsd/doctor.js +5 -3
- package/dist/resources/extensions/gsd/gsd-db.js +16 -3
- package/dist/resources/extensions/gsd/guided-flow.js +1 -2
- package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +5 -18
- package/dist/resources/extensions/gsd/preferences-types.js +2 -2
- package/dist/resources/extensions/gsd/preferences.js +8 -4
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +21 -8
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +10 -23
- package/dist/resources/extensions/gsd/prompts/discuss.md +2 -2
- package/dist/resources/extensions/gsd/prompts/execute-task.md +5 -15
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -2
- package/dist/resources/extensions/gsd/prompts/queue.md +2 -2
- package/dist/resources/extensions/gsd/prompts/quick-task.md +2 -0
- package/dist/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-slice.md +3 -3
- package/dist/resources/extensions/gsd/prompts/rethink.md +7 -2
- package/dist/resources/extensions/gsd/prompts/system.md +1 -1
- package/dist/resources/extensions/gsd/session-lock.js +1 -3
- package/dist/resources/extensions/gsd/state.js +7 -0
- package/dist/resources/extensions/gsd/sync-lock.js +89 -0
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +61 -11
- package/dist/resources/extensions/gsd/tools/complete-slice.js +56 -11
- package/dist/resources/extensions/gsd/tools/complete-task.js +50 -2
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +37 -1
- package/dist/resources/extensions/gsd/tools/plan-slice.js +30 -1
- package/dist/resources/extensions/gsd/tools/plan-task.js +27 -1
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +32 -2
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +86 -0
- package/dist/resources/extensions/gsd/tools/reopen-task.js +90 -0
- package/dist/resources/extensions/gsd/tools/replan-slice.js +32 -2
- package/dist/resources/extensions/gsd/unit-ownership.js +85 -0
- package/dist/resources/extensions/gsd/workflow-events.js +102 -0
- package/dist/resources/extensions/gsd/workflow-logger.js +193 -0
- package/dist/resources/extensions/gsd/workflow-manifest.js +244 -0
- package/dist/resources/extensions/gsd/workflow-migration.js +280 -0
- package/dist/resources/extensions/gsd/workflow-projections.js +373 -0
- package/dist/resources/extensions/gsd/workflow-reconcile.js +411 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +4 -3
- package/dist/resources/extensions/gsd/worktree-resolver.js +37 -0
- package/dist/resources/extensions/gsd/write-intercept.js +84 -0
- package/dist/resources/extensions/voice/index.js +11 -16
- package/dist/resources/extensions/voice/linux-ready.js +67 -0
- 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 +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
- package/dist/web/standalone/.next/server/chunks/229.js +1 -1
- package/dist/web/standalone/.next/server/chunks/471.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-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/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/package.json +2 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +2 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts +4 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +10 -5
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js +185 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +239 -10
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +2 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +20 -2
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-commands.test.js +206 -195
- package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +2 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -1
- package/packages/pi-coding-agent/src/core/lifecycle-hooks.test.ts +227 -0
- package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +11 -5
- package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +297 -11
- package/packages/pi-coding-agent/src/core/model-registry.ts +30 -3
- package/packages/pi-coding-agent/src/core/package-commands.test.ts +227 -205
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -19
- package/src/resources/extensions/gsd/auto/phases.ts +24 -44
- package/src/resources/extensions/gsd/auto/run-unit.ts +6 -3
- package/src/resources/extensions/gsd/auto/session.ts +0 -18
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +131 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +0 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +25 -106
- package/src/resources/extensions/gsd/auto-start.ts +1 -3
- package/src/resources/extensions/gsd/auto-worktree.ts +8 -5
- package/src/resources/extensions/gsd/auto.ts +7 -83
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +15 -12
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +22 -0
- package/src/resources/extensions/gsd/commands/context.ts +0 -5
- package/src/resources/extensions/gsd/commands/handlers/parallel.ts +1 -1
- package/src/resources/extensions/gsd/crash-recovery.ts +1 -5
- package/src/resources/extensions/gsd/dashboard-overlay.ts +0 -50
- package/src/resources/extensions/gsd/db-writer.ts +9 -17
- package/src/resources/extensions/gsd/doctor-checks.ts +180 -2
- package/src/resources/extensions/gsd/doctor-types.ts +7 -1
- package/src/resources/extensions/gsd/doctor.ts +6 -3
- package/src/resources/extensions/gsd/gsd-db.ts +16 -3
- package/src/resources/extensions/gsd/guided-flow.ts +1 -2
- package/src/resources/extensions/gsd/journal.ts +6 -1
- package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +5 -21
- package/src/resources/extensions/gsd/preferences-types.ts +2 -2
- package/src/resources/extensions/gsd/preferences.ts +7 -3
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +21 -8
- package/src/resources/extensions/gsd/prompts/complete-slice.md +10 -23
- package/src/resources/extensions/gsd/prompts/discuss.md +2 -2
- package/src/resources/extensions/gsd/prompts/execute-task.md +5 -15
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -2
- package/src/resources/extensions/gsd/prompts/queue.md +2 -2
- package/src/resources/extensions/gsd/prompts/quick-task.md +2 -0
- package/src/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-slice.md +3 -3
- package/src/resources/extensions/gsd/prompts/rethink.md +7 -2
- package/src/resources/extensions/gsd/prompts/system.md +1 -1
- package/src/resources/extensions/gsd/session-lock.ts +0 -4
- package/src/resources/extensions/gsd/state.ts +8 -0
- package/src/resources/extensions/gsd/sync-lock.ts +94 -0
- package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +5 -13
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +6 -10
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +96 -0
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +264 -228
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +317 -250
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +2 -8
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +0 -3
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/integration-proof.test.ts +15 -24
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -3
- 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/merge-conflict-stops-loop.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +8 -9
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +42 -3
- package/src/resources/extensions/gsd/tests/parallel-budget-atomicity.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +0 -7
- package/src/resources/extensions/gsd/tests/parallel-merge.test.ts +7 -8
- package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +20 -24
- package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +0 -2
- package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +9 -6
- package/src/resources/extensions/gsd/tests/post-mutation-hook.test.ts +171 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +7 -9
- package/src/resources/extensions/gsd/tests/projection-regression.test.ts +174 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +15 -14
- package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +155 -0
- package/src/resources/extensions/gsd/tests/reopen-task.test.ts +165 -0
- package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +1 -4
- package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +2 -3
- package/src/resources/extensions/gsd/tests/sync-lock.test.ts +122 -0
- package/src/resources/extensions/gsd/tests/unit-ownership.test.ts +175 -0
- package/src/resources/extensions/gsd/tests/workflow-events.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +275 -0
- package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +171 -0
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +220 -0
- package/src/resources/extensions/gsd/tests/write-intercept.test.ts +76 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +74 -11
- package/src/resources/extensions/gsd/tools/complete-slice.ts +68 -11
- package/src/resources/extensions/gsd/tools/complete-task.ts +63 -1
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +45 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +38 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +35 -1
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +39 -1
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +125 -0
- package/src/resources/extensions/gsd/tools/reopen-task.ts +129 -0
- package/src/resources/extensions/gsd/tools/replan-slice.ts +38 -1
- package/src/resources/extensions/gsd/types.ts +8 -0
- package/src/resources/extensions/gsd/unit-ownership.ts +104 -0
- package/src/resources/extensions/gsd/workflow-events.ts +154 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +243 -0
- package/src/resources/extensions/gsd/workflow-manifest.ts +334 -0
- package/src/resources/extensions/gsd/workflow-migration.ts +345 -0
- package/src/resources/extensions/gsd/workflow-projections.ts +425 -0
- package/src/resources/extensions/gsd/workflow-reconcile.ts +503 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +4 -9
- package/src/resources/extensions/gsd/worktree-resolver.ts +37 -0
- package/src/resources/extensions/gsd/write-intercept.ts +90 -0
- package/src/resources/extensions/voice/index.ts +11 -21
- package/src/resources/extensions/voice/linux-ready.ts +87 -0
- package/src/resources/extensions/voice/tests/linux-ready.test.ts +124 -0
- 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/dist/web/standalone/.next/static/{wUzEX1U3CmFcMry2SUDJn → ZIDqryyYDroh_8AnaAOSG}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{wUzEX1U3CmFcMry2SUDJn → ZIDqryyYDroh_8AnaAOSG}/_ssgManifest.js +0 -0
|
@@ -48,7 +48,7 @@ Then:
|
|
|
48
48
|
3. Create the roadmap: decompose into demoable vertical slices — as many as the work genuinely needs, no more. A simple feature might be 1 slice. Don't decompose for decomposition's sake.
|
|
49
49
|
4. Order by risk (high-risk first)
|
|
50
50
|
5. Call `gsd_plan_milestone` to persist the milestone planning fields and slice rows in the DB-backed planning path. Do **not** write `{{outputPath}}`, `ROADMAP.md`, or other planning artifacts manually — the planning tool owns roadmap rendering and persistence.
|
|
51
|
-
6. If planning produced structural decisions (e.g. slice ordering rationale, technology choices, scope exclusions),
|
|
51
|
+
6. If planning produced structural decisions (e.g. slice ordering rationale, technology choices, scope exclusions), call `gsd_decision_save` for each decision — the tool auto-assigns IDs and regenerates `.gsd/DECISIONS.md` automatically.
|
|
52
52
|
|
|
53
53
|
## Requirement Mapping Rules
|
|
54
54
|
|
|
@@ -72,9 +72,11 @@ Then:
|
|
|
72
72
|
- **Key links planned:** For every pair of artifacts that must connect, there is an explicit step that wires them.
|
|
73
73
|
- **Scope sanity:** Target 2–5 steps and 3–8 files per task. 10+ steps or 12+ files — must split. Each task must be completable in a single fresh context window.
|
|
74
74
|
- **Feature completeness:** Every task produces real, user-facing progress — not just internal scaffolding.
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
10. If planning produced structural decisions, append them to `.gsd/DECISIONS.md`
|
|
76
|
+
11. {{commitInstruction}}
|
|
77
77
|
|
|
78
78
|
The slice directory and tasks/ subdirectory already exist. Do NOT mkdir. All work stays in your working directory: `{{workingDirectory}}`.
|
|
79
79
|
|
|
80
|
+
**You MUST write the file `{{outputPath}}` before finishing.**
|
|
81
|
+
|
|
80
82
|
When done, say: "Slice {{sliceId}} planned."
|
|
@@ -8,7 +8,7 @@ Before asking "What do you want to add?", check the existing milestones context
|
|
|
8
8
|
|
|
9
9
|
1. Tell the user which milestones have draft contexts and briefly summarize what each draft contains (read the draft file).
|
|
10
10
|
2. Use `ask_user_questions` to ask per-draft milestone:
|
|
11
|
-
- **"Discuss now"** — Treat this draft as the primary topic. Read the draft content, use it as seed material, and conduct a focused discussion following the standard discussion flow (reflection → investigation → questioning → depth verification → requirements → roadmap). After the discussion, write the full
|
|
11
|
+
- **"Discuss now"** — Treat this draft as the primary topic. Read the draft content, use it as seed material, and conduct a focused discussion following the standard discussion flow (reflection → investigation → questioning → depth verification → requirements → roadmap). After the discussion, call `gsd_summary_save` with the milestone ID and `artifact_type: "CONTEXT"` to write the full context — then delete the `CONTEXT-DRAFT.md` file. The milestone is then ready for auto-planning.
|
|
12
12
|
- **"Leave for later"** — Keep the draft as-is. The user will discuss it in a future session. Auto-mode will continue to pause when it reaches this milestone.
|
|
13
13
|
3. Handle all draft discussions before proceeding to new queue work.
|
|
14
14
|
4. If no drafts exist in the context, skip this section entirely and proceed to "What do you want to add?"
|
|
@@ -108,7 +108,7 @@ The user confirms or corrects before you write. One depth verification per miles
|
|
|
108
108
|
Once the user is satisfied, in a single pass for **each** new milestone:
|
|
109
109
|
|
|
110
110
|
1. Call `gsd_milestone_generate_id` to get the milestone ID — never invent milestone IDs manually. Then `mkdir -p .gsd/milestones/<ID>/slices`.
|
|
111
|
-
2.
|
|
111
|
+
2. Call `gsd_summary_save` with `milestone_id: <ID>`, `artifact_type: "CONTEXT"`, and the full context markdown as `content` — the tool computes the file path and persists to both DB and disk. Capture intent, scope, risks, constraints, integration points, and relevant requirements in the content. Mark the status as "Queued — pending auto-mode execution." **If this milestone depends on other milestones, include YAML frontmatter with `depends_on` in the content:**
|
|
112
112
|
```yaml
|
|
113
113
|
---
|
|
114
114
|
depends_on: [M001, M002]
|
|
@@ -21,7 +21,9 @@ You are executing a GSD quick task — a lightweight, focused unit of work outsi
|
|
|
21
21
|
- Use conventional commit messages (feat:, fix:, refactor:, etc.)
|
|
22
22
|
- Stage only relevant files — never commit secrets or runtime files.
|
|
23
23
|
- Commit logical units separately if the task involves distinct changes.
|
|
24
|
+
- Quick tasks run outside the auto-mode lifecycle — there is no system auto-commit, so commit directly here.
|
|
24
25
|
7. Write a brief summary to `{{summaryPath}}`:
|
|
26
|
+
- Quick tasks operate outside the milestone/slice/task DB structure, so `gsd_summary_save` (which requires a `milestone_id`) cannot be used here. Write the file directly.
|
|
25
27
|
|
|
26
28
|
```markdown
|
|
27
29
|
# Quick Task: {{description}}
|
|
@@ -26,7 +26,7 @@ You are executing **multiple tasks in parallel** for this slice. The task graph
|
|
|
26
26
|
2. **Wait for all subagents** to complete.
|
|
27
27
|
3. **Verify each dispatched task's outputs** — check that expected files were created/modified, that verification commands pass where applicable, and that each task wrote its own `T##-SUMMARY.md`.
|
|
28
28
|
4. **Do not rewrite successful task summaries or duplicate completion tool calls.** Treat a subagent-written summary as authoritative for that task.
|
|
29
|
-
5. **If a failed task produced no summary,
|
|
29
|
+
5. **If a failed task produced no summary, call `gsd_summary_save`** with `milestone_id: {{milestoneId}}`, `slice_id: {{sliceId}}`, the failed task's `task_id`, and `artifact_type: "SUMMARY"` — include `blocker_discovered: true` and clear failure details in the `content`. Do NOT call `gsd_task_complete` for the failed task — leave it uncompleted so replan/retry has an authoritative record.
|
|
30
30
|
6. **Preserve successful sibling tasks exactly as they landed.** Do not roll back good work because another parallel task failed.
|
|
31
31
|
7. **Do NOT create a batch commit.** The surrounding unit lifecycle owns commits; this parent batch agent should not invent a second commit layer.
|
|
32
32
|
8. **Report the batch outcome** — which tasks succeeded, which failed, and any output collisions or dependency surprises.
|
|
@@ -48,10 +48,10 @@ Research what this slice needs. Narrate key findings and surprises as you go —
|
|
|
48
48
|
4. Use `resolve_library` / `get_library_docs` for unfamiliar libraries — skip this for libraries already used in the codebase
|
|
49
49
|
5. **Web search budget:** You have a limited budget of web searches (max ~15 per session). Use them strategically — prefer `resolve_library` / `get_library_docs` for library documentation. Do NOT repeat the same or similar queries. If a search didn't find what you need, rephrase once or move on. Target 3-5 total web searches for a typical research unit.
|
|
50
50
|
6. Use the **Research** output template from the inlined context above — include only sections that have real content. The template is already inlined above; do NOT attempt to read any template file from disk (there is no `templates/SLICE-RESEARCH.md` — the correct template is already present in this prompt).
|
|
51
|
-
7.
|
|
51
|
+
7. Call `gsd_summary_save` with `milestone_id: {{milestoneId}}`, `slice_id: {{sliceId}}`, `artifact_type: "RESEARCH"`, and the full research markdown as `content` — the tool computes the file path and persists to both DB and disk.
|
|
52
52
|
|
|
53
|
-
The slice directory already exists at `{{slicePath}}/`. Do NOT mkdir
|
|
53
|
+
The slice directory already exists at `{{slicePath}}/`. Do NOT mkdir.
|
|
54
54
|
|
|
55
|
-
**You MUST
|
|
55
|
+
**You MUST call `gsd_summary_save` with the research content before finishing.**
|
|
56
56
|
|
|
57
57
|
When done, say: "Slice {{sliceId}} researched."
|
|
@@ -16,6 +16,11 @@ You are a project reorganization assistant for a GSD (Get Shit Done) project. Th
|
|
|
16
16
|
|
|
17
17
|
## Supported Operations
|
|
18
18
|
|
|
19
|
+
<!-- NOTE: Park, unpark, reorder, discard, and dependency-update operations are intentionally
|
|
20
|
+
file-based. No gsd_* tool API exists for these milestone-lifecycle mutations yet.
|
|
21
|
+
The single-writer DB tools (gsd_plan_milestone, gsd_complete_milestone, etc.) own
|
|
22
|
+
create and complete; queue management is file-driven until tool support is added. -->
|
|
23
|
+
|
|
19
24
|
### Reorder milestones
|
|
20
25
|
Change execution order of pending/active milestones. Write `.gsd/QUEUE-ORDER.json`:
|
|
21
26
|
```json
|
|
@@ -44,7 +49,7 @@ Remove the `{ID}-PARKED.md` file from the milestone directory to reactivate it.
|
|
|
44
49
|
**Permanently** delete a milestone directory and prune it from QUEUE-ORDER.json. **Always confirm with the user before discarding.** Warn explicitly if the milestone has completed work.
|
|
45
50
|
|
|
46
51
|
### Add a new milestone
|
|
47
|
-
Use the `gsd_milestone_generate_id` tool to get the next ID, then
|
|
52
|
+
Use the `gsd_milestone_generate_id` tool to get the next ID, then call `gsd_summary_save` with `milestone_id: {ID}`, `artifact_type: "CONTEXT"`, and the scope/goals/success criteria as `content` — the tool writes the context file to disk and persists to DB. Update QUEUE-ORDER.json to place it at the desired position.
|
|
48
53
|
|
|
49
54
|
### Update dependencies
|
|
50
55
|
Edit `depends_on` in the YAML frontmatter of a milestone's `{ID}-CONTEXT.md` file. For example:
|
|
@@ -75,4 +80,4 @@ If a proposed order would violate constraints, explain the issue and suggest alt
|
|
|
75
80
|
- Do NOT park completed milestones — it would corrupt dependency satisfaction
|
|
76
81
|
- Park is preferred over discard when a milestone has any completed work
|
|
77
82
|
- Always persist queue order changes to `.gsd/QUEUE-ORDER.json`
|
|
78
|
-
- After changes, run `git add .gsd/ && git commit -m "docs: rethink milestone
|
|
83
|
+
- After changes, run `git add .gsd/ && git commit -m "docs(gsd): rethink milestone plan"` to persist (rethink runs interactively outside auto-mode, so no system auto-commit)
|
|
@@ -112,7 +112,7 @@ In all modes, slices commit sequentially on the active branch; there are no per-
|
|
|
112
112
|
- **Milestones** are major project phases (M001, M002, ...)
|
|
113
113
|
- **Slices** are demoable vertical increments (S01, S02, ...) ordered by risk. After each slice completes, the roadmap is reassessed before the next slice begins.
|
|
114
114
|
- **Tasks** are single-context-window units of work (T01, T02, ...)
|
|
115
|
-
- Checkboxes in roadmap and plan files track completion (`[ ]` → `[x]`)
|
|
115
|
+
- Checkboxes in roadmap and plan files track completion (`[ ]` → `[x]`) — toggled automatically by gsd_* tools, never edited manually
|
|
116
116
|
- Summaries compress prior work - read them instead of re-reading all task details
|
|
117
117
|
- `STATE.md` is a system-managed status file — rebuilt automatically after each unit completes
|
|
118
118
|
|
|
@@ -169,7 +169,6 @@ export function acquireSessionLock(basePath) {
|
|
|
169
169
|
unitType: "starting",
|
|
170
170
|
unitId: "bootstrap",
|
|
171
171
|
unitStartedAt: new Date().toISOString(),
|
|
172
|
-
completedUnits: 0,
|
|
173
172
|
};
|
|
174
173
|
let lockfile;
|
|
175
174
|
try {
|
|
@@ -314,7 +313,7 @@ function acquireFallbackLock(basePath, lp, lockData) {
|
|
|
314
313
|
* Update the lock file metadata (called on each unit dispatch).
|
|
315
314
|
* Does NOT re-acquire the OS lock — just updates the JSON content.
|
|
316
315
|
*/
|
|
317
|
-
export function updateSessionLock(basePath, unitType, unitId,
|
|
316
|
+
export function updateSessionLock(basePath, unitType, unitId, sessionFile) {
|
|
318
317
|
if (_lockedPath !== basePath && _lockedPath !== null)
|
|
319
318
|
return;
|
|
320
319
|
const lp = lockPath(basePath);
|
|
@@ -325,7 +324,6 @@ export function updateSessionLock(basePath, unitType, unitId, completedUnits, se
|
|
|
325
324
|
unitType,
|
|
326
325
|
unitId,
|
|
327
326
|
unitStartedAt: new Date().toISOString(),
|
|
328
|
-
completedUnits,
|
|
329
327
|
sessionFile,
|
|
330
328
|
};
|
|
331
329
|
atomicWriteSync(lp, JSON.stringify(data, null, 2));
|
|
@@ -57,6 +57,10 @@ export function isValidationTerminal(validationContent) {
|
|
|
57
57
|
}
|
|
58
58
|
const CACHE_TTL_MS = 100;
|
|
59
59
|
let _stateCache = null;
|
|
60
|
+
// ── Telemetry counters for derive-path observability ────────────────────────
|
|
61
|
+
let _telemetry = { dbDeriveCount: 0, markdownDeriveCount: 0 };
|
|
62
|
+
export function getDeriveTelemetry() { return { ..._telemetry }; }
|
|
63
|
+
export function resetDeriveTelemetry() { _telemetry = { dbDeriveCount: 0, markdownDeriveCount: 0 }; }
|
|
60
64
|
/**
|
|
61
65
|
* Invalidate the deriveState() cache. Call this whenever planning files on disk
|
|
62
66
|
* may have changed (unit completion, merges, file writes).
|
|
@@ -141,14 +145,17 @@ export async function deriveState(basePath) {
|
|
|
141
145
|
const stopDbTimer = debugTime("derive-state-db");
|
|
142
146
|
result = await deriveStateFromDb(basePath);
|
|
143
147
|
stopDbTimer({ phase: result.phase, milestone: result.activeMilestone?.id });
|
|
148
|
+
_telemetry.dbDeriveCount++;
|
|
144
149
|
}
|
|
145
150
|
else {
|
|
146
151
|
// DB open but empty hierarchy tables — pre-migration project, use filesystem
|
|
147
152
|
result = await _deriveStateImpl(basePath);
|
|
153
|
+
_telemetry.markdownDeriveCount++;
|
|
148
154
|
}
|
|
149
155
|
}
|
|
150
156
|
else {
|
|
151
157
|
result = await _deriveStateImpl(basePath);
|
|
158
|
+
_telemetry.markdownDeriveCount++;
|
|
152
159
|
}
|
|
153
160
|
stopTimer({ phase: result.phase, milestone: result.activeMilestone?.id });
|
|
154
161
|
debugCount("deriveStateCalls");
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// GSD Extension — Advisory Sync Lock
|
|
2
|
+
// Prevents concurrent worktree syncs from colliding via a simple file lock.
|
|
3
|
+
// Stale locks (mtime > 60s) are auto-overridden. Lock acquisition waits up
|
|
4
|
+
// to 5 seconds then skips non-fatally.
|
|
5
|
+
import { existsSync, statSync, unlinkSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { atomicWriteSync } from "./atomic-write.js";
|
|
8
|
+
const STALE_THRESHOLD_MS = 60_000; // 60 seconds
|
|
9
|
+
const DEFAULT_TIMEOUT_MS = 5_000; // 5 seconds
|
|
10
|
+
const SPIN_INTERVAL_MS = 100; // 100ms polling interval
|
|
11
|
+
// SharedArrayBuffer for synchronous sleep via Atomics.wait
|
|
12
|
+
const SLEEP_BUFFER = new SharedArrayBuffer(4);
|
|
13
|
+
const SLEEP_VIEW = new Int32Array(SLEEP_BUFFER);
|
|
14
|
+
function lockFilePath(basePath) {
|
|
15
|
+
return join(basePath, ".gsd", "sync.lock");
|
|
16
|
+
}
|
|
17
|
+
function sleepSync(ms) {
|
|
18
|
+
Atomics.wait(SLEEP_VIEW, 0, 0, ms);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Acquire an advisory sync lock for the given basePath.
|
|
22
|
+
* Returns { acquired: true } on success, { acquired: false } after timeout.
|
|
23
|
+
*
|
|
24
|
+
* - Creates lock file at {basePath}/.gsd/sync.lock with JSON { pid, acquired_at }
|
|
25
|
+
* - If lock exists and mtime > 60s (stale), overrides it
|
|
26
|
+
* - If lock exists and not stale, spins up to timeoutMs before giving up
|
|
27
|
+
*/
|
|
28
|
+
export function acquireSyncLock(basePath, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
29
|
+
const lp = lockFilePath(basePath);
|
|
30
|
+
const deadline = Date.now() + timeoutMs;
|
|
31
|
+
while (true) {
|
|
32
|
+
// Check if lock file exists
|
|
33
|
+
if (existsSync(lp)) {
|
|
34
|
+
// Check staleness
|
|
35
|
+
try {
|
|
36
|
+
const stat = statSync(lp);
|
|
37
|
+
const age = Date.now() - stat.mtimeMs;
|
|
38
|
+
if (age > STALE_THRESHOLD_MS) {
|
|
39
|
+
// Stale lock — override it
|
|
40
|
+
try {
|
|
41
|
+
unlinkSync(lp);
|
|
42
|
+
}
|
|
43
|
+
catch { /* race: already removed */ }
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// Lock is held and not stale — wait or give up
|
|
47
|
+
if (Date.now() >= deadline) {
|
|
48
|
+
return { acquired: false };
|
|
49
|
+
}
|
|
50
|
+
sleepSync(SPIN_INTERVAL_MS);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// stat failed (file removed between exists check and stat) — try to acquire
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Lock file does not exist (or was just removed) — try to write it
|
|
59
|
+
try {
|
|
60
|
+
const lockData = {
|
|
61
|
+
pid: process.pid,
|
|
62
|
+
acquired_at: new Date().toISOString(),
|
|
63
|
+
};
|
|
64
|
+
atomicWriteSync(lp, JSON.stringify(lockData, null, 2));
|
|
65
|
+
return { acquired: true };
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// Write failed (race condition with another process) — retry or give up
|
|
69
|
+
if (Date.now() >= deadline) {
|
|
70
|
+
return { acquired: false };
|
|
71
|
+
}
|
|
72
|
+
sleepSync(SPIN_INTERVAL_MS);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Release the advisory sync lock. No-op if lock file does not exist.
|
|
78
|
+
*/
|
|
79
|
+
export function releaseSyncLock(basePath) {
|
|
80
|
+
const lp = lockFilePath(basePath);
|
|
81
|
+
try {
|
|
82
|
+
if (existsSync(lp)) {
|
|
83
|
+
unlinkSync(lp);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// Non-fatal — lock may have been released by another process
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -7,10 +7,13 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { join } from "node:path";
|
|
9
9
|
import { mkdirSync } from "node:fs";
|
|
10
|
-
import { transaction, getMilestoneSlices, _getAdapter, } from "../gsd-db.js";
|
|
10
|
+
import { transaction, getMilestone, getMilestoneSlices, getSliceTasks, _getAdapter, } from "../gsd-db.js";
|
|
11
11
|
import { resolveMilestonePath, clearPathCache } from "../paths.js";
|
|
12
12
|
import { saveFile, clearParseCache } from "../files.js";
|
|
13
13
|
import { invalidateStateCache } from "../state.js";
|
|
14
|
+
import { renderAllProjections } from "../workflow-projections.js";
|
|
15
|
+
import { writeManifest } from "../workflow-manifest.js";
|
|
16
|
+
import { appendEvent } from "../workflow-events.js";
|
|
14
17
|
function renderMilestoneSummaryMarkdown(params) {
|
|
15
18
|
const now = new Date().toISOString();
|
|
16
19
|
const keyDecisionsYaml = params.keyDecisions.length > 0
|
|
@@ -72,25 +75,56 @@ export async function handleCompleteMilestone(params, basePath) {
|
|
|
72
75
|
if (!params.title || typeof params.title !== "string" || params.title.trim() === "") {
|
|
73
76
|
return { error: "title is required and must be a non-empty string" };
|
|
74
77
|
}
|
|
75
|
-
// ── Verify
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return { error: `no slices found for milestone ${params.milestoneId}` };
|
|
78
|
+
// ── Verify that verification passed ─────────────────────────────────────
|
|
79
|
+
if (params.verificationPassed !== true) {
|
|
80
|
+
return { error: "verification did not pass — milestone completion blocked. verificationPassed must be explicitly set to true after all verification steps succeed" };
|
|
79
81
|
}
|
|
80
|
-
|
|
81
|
-
if (incompleteSlices.length > 0) {
|
|
82
|
-
const incompleteIds = incompleteSlices.map(s => `${s.id} (status: ${s.status})`).join(", ");
|
|
83
|
-
return { error: `incomplete slices: ${incompleteIds}` };
|
|
84
|
-
}
|
|
85
|
-
// ── DB writes inside a transaction ──────────────────────────────────────
|
|
82
|
+
// ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
|
|
86
83
|
const completedAt = new Date().toISOString();
|
|
84
|
+
let guardError = null;
|
|
87
85
|
transaction(() => {
|
|
86
|
+
// State machine preconditions (inside txn for atomicity)
|
|
87
|
+
const milestone = getMilestone(params.milestoneId);
|
|
88
|
+
if (!milestone) {
|
|
89
|
+
guardError = `milestone not found: ${params.milestoneId}`;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (milestone.status === "complete" || milestone.status === "done") {
|
|
93
|
+
guardError = `milestone ${params.milestoneId} is already complete`;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// Verify all slices are complete
|
|
97
|
+
const slices = getMilestoneSlices(params.milestoneId);
|
|
98
|
+
if (slices.length === 0) {
|
|
99
|
+
guardError = `no slices found for milestone ${params.milestoneId}`;
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const incompleteSlices = slices.filter(s => s.status !== "complete" && s.status !== "done");
|
|
103
|
+
if (incompleteSlices.length > 0) {
|
|
104
|
+
const incompleteIds = incompleteSlices.map(s => `${s.id} (status: ${s.status})`).join(", ");
|
|
105
|
+
guardError = `incomplete slices: ${incompleteIds}`;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
// Deep check: verify all tasks in all slices are complete
|
|
109
|
+
for (const slice of slices) {
|
|
110
|
+
const tasks = getSliceTasks(params.milestoneId, slice.id);
|
|
111
|
+
const incompleteTasks = tasks.filter(t => t.status !== "complete" && t.status !== "done");
|
|
112
|
+
if (incompleteTasks.length > 0) {
|
|
113
|
+
const ids = incompleteTasks.map(t => `${t.id} (status: ${t.status})`).join(", ");
|
|
114
|
+
guardError = `slice ${slice.id} has incomplete tasks: ${ids}`;
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// All guards passed — perform write
|
|
88
119
|
const adapter = _getAdapter();
|
|
89
120
|
adapter.prepare(`UPDATE milestones SET status = 'complete', completed_at = :completed_at WHERE id = :mid`).run({
|
|
90
121
|
":completed_at": completedAt,
|
|
91
122
|
":mid": params.milestoneId,
|
|
92
123
|
});
|
|
93
124
|
});
|
|
125
|
+
if (guardError) {
|
|
126
|
+
return { error: guardError };
|
|
127
|
+
}
|
|
94
128
|
// ── Filesystem operations (outside transaction) ─────────────────────────
|
|
95
129
|
const summaryMd = renderMilestoneSummaryMarkdown(params);
|
|
96
130
|
let summaryPath;
|
|
@@ -121,6 +155,22 @@ export async function handleCompleteMilestone(params, basePath) {
|
|
|
121
155
|
invalidateStateCache();
|
|
122
156
|
clearPathCache();
|
|
123
157
|
clearParseCache();
|
|
158
|
+
// ── Post-mutation hook: projections, manifest, event log ───────────────
|
|
159
|
+
try {
|
|
160
|
+
await renderAllProjections(basePath, params.milestoneId);
|
|
161
|
+
writeManifest(basePath);
|
|
162
|
+
appendEvent(basePath, {
|
|
163
|
+
cmd: "complete-milestone",
|
|
164
|
+
params: { milestoneId: params.milestoneId },
|
|
165
|
+
ts: new Date().toISOString(),
|
|
166
|
+
actor: "agent",
|
|
167
|
+
actor_name: params.actorName,
|
|
168
|
+
trigger_reason: params.triggerReason,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
catch (hookErr) {
|
|
172
|
+
process.stderr.write(`gsd: complete-milestone post-mutation hook warning: ${hookErr.message}\n`);
|
|
173
|
+
}
|
|
124
174
|
return {
|
|
125
175
|
milestoneId: params.milestoneId,
|
|
126
176
|
summaryPath,
|
|
@@ -8,11 +8,15 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
import { mkdirSync } from "node:fs";
|
|
11
|
-
import { transaction, insertMilestone, insertSlice, getSliceTasks, updateSliceStatus, _getAdapter, } from "../gsd-db.js";
|
|
11
|
+
import { transaction, insertMilestone, insertSlice, getSlice, getSliceTasks, getMilestone, updateSliceStatus, _getAdapter, } from "../gsd-db.js";
|
|
12
12
|
import { resolveSlicePath, clearPathCache } from "../paths.js";
|
|
13
|
+
import { checkOwnership, sliceUnitKey } from "../unit-ownership.js";
|
|
13
14
|
import { saveFile, clearParseCache } from "../files.js";
|
|
14
15
|
import { invalidateStateCache } from "../state.js";
|
|
15
16
|
import { renderRoadmapCheckboxes } from "../markdown-renderer.js";
|
|
17
|
+
import { renderAllProjections } from "../workflow-projections.js";
|
|
18
|
+
import { writeManifest } from "../workflow-manifest.js";
|
|
19
|
+
import { appendEvent } from "../workflow-events.js";
|
|
16
20
|
/**
|
|
17
21
|
* Render slice summary markdown matching the template format.
|
|
18
22
|
* YAML frontmatter uses snake_case keys for parseSummary() compatibility.
|
|
@@ -162,23 +166,48 @@ export async function handleCompleteSlice(params, basePath) {
|
|
|
162
166
|
if (!params.milestoneId || typeof params.milestoneId !== "string" || params.milestoneId.trim() === "") {
|
|
163
167
|
return { error: "milestoneId is required and must be a non-empty string" };
|
|
164
168
|
}
|
|
165
|
-
// ──
|
|
166
|
-
const
|
|
167
|
-
if (
|
|
168
|
-
return { error:
|
|
169
|
+
// ── Ownership check (opt-in: only enforced when claim file exists) ──────
|
|
170
|
+
const ownershipErr = checkOwnership(basePath, sliceUnitKey(params.milestoneId, params.sliceId), params.actorName);
|
|
171
|
+
if (ownershipErr) {
|
|
172
|
+
return { error: ownershipErr };
|
|
169
173
|
}
|
|
170
|
-
|
|
171
|
-
if (incompleteTasks.length > 0) {
|
|
172
|
-
const incompleteIds = incompleteTasks.map(t => `${t.id} (status: ${t.status})`).join(", ");
|
|
173
|
-
return { error: `incomplete tasks: ${incompleteIds}` };
|
|
174
|
-
}
|
|
175
|
-
// ── DB writes inside a transaction ──────────────────────────────────────
|
|
174
|
+
// ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
|
|
176
175
|
const completedAt = new Date().toISOString();
|
|
176
|
+
let guardError = null;
|
|
177
177
|
transaction(() => {
|
|
178
|
+
// State machine preconditions (inside txn for atomicity).
|
|
179
|
+
// Milestone/slice not existing is OK — insertMilestone/insertSlice below will auto-create.
|
|
180
|
+
// Only block if they exist and are closed.
|
|
181
|
+
const milestone = getMilestone(params.milestoneId);
|
|
182
|
+
if (milestone && (milestone.status === "complete" || milestone.status === "done")) {
|
|
183
|
+
guardError = `cannot complete slice in a closed milestone: ${params.milestoneId} (status: ${milestone.status})`;
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const slice = getSlice(params.milestoneId, params.sliceId);
|
|
187
|
+
if (slice && (slice.status === "complete" || slice.status === "done")) {
|
|
188
|
+
guardError = `slice ${params.sliceId} is already complete — use gsd_slice_reopen first if you need to redo it`;
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
// Verify all tasks are complete
|
|
192
|
+
const tasks = getSliceTasks(params.milestoneId, params.sliceId);
|
|
193
|
+
if (tasks.length === 0) {
|
|
194
|
+
guardError = `no tasks found for slice ${params.sliceId} in milestone ${params.milestoneId}`;
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const incompleteTasks = tasks.filter(t => t.status !== "complete" && t.status !== "done");
|
|
198
|
+
if (incompleteTasks.length > 0) {
|
|
199
|
+
const incompleteIds = incompleteTasks.map(t => `${t.id} (status: ${t.status})`).join(", ");
|
|
200
|
+
guardError = `incomplete tasks: ${incompleteIds}`;
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
// All guards passed — perform writes
|
|
178
204
|
insertMilestone({ id: params.milestoneId });
|
|
179
205
|
insertSlice({ id: params.sliceId, milestoneId: params.milestoneId });
|
|
180
206
|
updateSliceStatus(params.milestoneId, params.sliceId, "complete", completedAt);
|
|
181
207
|
});
|
|
208
|
+
if (guardError) {
|
|
209
|
+
return { error: guardError };
|
|
210
|
+
}
|
|
182
211
|
// ── Filesystem operations (outside transaction) ─────────────────────────
|
|
183
212
|
// If disk render fails, roll back the DB status so deriveState() and
|
|
184
213
|
// verifyExpectedArtifact() stay consistent (both say "not done").
|
|
@@ -235,6 +264,22 @@ export async function handleCompleteSlice(params, basePath) {
|
|
|
235
264
|
invalidateStateCache();
|
|
236
265
|
clearPathCache();
|
|
237
266
|
clearParseCache();
|
|
267
|
+
// ── Post-mutation hook: projections, manifest, event log ───────────────
|
|
268
|
+
try {
|
|
269
|
+
await renderAllProjections(basePath, params.milestoneId);
|
|
270
|
+
writeManifest(basePath);
|
|
271
|
+
appendEvent(basePath, {
|
|
272
|
+
cmd: "complete-slice",
|
|
273
|
+
params: { milestoneId: params.milestoneId, sliceId: params.sliceId },
|
|
274
|
+
ts: new Date().toISOString(),
|
|
275
|
+
actor: "agent",
|
|
276
|
+
actor_name: params.actorName,
|
|
277
|
+
trigger_reason: params.triggerReason,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
catch (hookErr) {
|
|
281
|
+
process.stderr.write(`gsd: complete-slice post-mutation hook warning: ${hookErr.message}\n`);
|
|
282
|
+
}
|
|
238
283
|
return {
|
|
239
284
|
sliceId: params.sliceId,
|
|
240
285
|
milestoneId: params.milestoneId,
|
|
@@ -8,11 +8,15 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
import { mkdirSync } from "node:fs";
|
|
11
|
-
import { transaction, insertMilestone, insertSlice, insertTask, insertVerificationEvidence, _getAdapter, } from "../gsd-db.js";
|
|
11
|
+
import { transaction, insertMilestone, insertSlice, insertTask, insertVerificationEvidence, getMilestone, getSlice, getTask, _getAdapter, } from "../gsd-db.js";
|
|
12
12
|
import { resolveSliceFile, resolveTasksDir, clearPathCache } from "../paths.js";
|
|
13
|
+
import { checkOwnership, taskUnitKey } from "../unit-ownership.js";
|
|
13
14
|
import { saveFile, clearParseCache } from "../files.js";
|
|
14
15
|
import { invalidateStateCache } from "../state.js";
|
|
15
16
|
import { renderPlanCheckboxes } from "../markdown-renderer.js";
|
|
17
|
+
import { renderAllProjections } from "../workflow-projections.js";
|
|
18
|
+
import { writeManifest } from "../workflow-manifest.js";
|
|
19
|
+
import { appendEvent } from "../workflow-events.js";
|
|
16
20
|
/**
|
|
17
21
|
* Render task summary markdown matching the template format.
|
|
18
22
|
* YAML frontmatter uses snake_case keys for parseSummary() compatibility.
|
|
@@ -105,9 +109,34 @@ export async function handleCompleteTask(params, basePath) {
|
|
|
105
109
|
if (!params.milestoneId || typeof params.milestoneId !== "string" || params.milestoneId.trim() === "") {
|
|
106
110
|
return { error: "milestoneId is required and must be a non-empty string" };
|
|
107
111
|
}
|
|
108
|
-
// ──
|
|
112
|
+
// ── Ownership check (opt-in: only enforced when claim file exists) ──────
|
|
113
|
+
const ownershipErr = checkOwnership(basePath, taskUnitKey(params.milestoneId, params.sliceId, params.taskId), params.actorName);
|
|
114
|
+
if (ownershipErr) {
|
|
115
|
+
return { error: ownershipErr };
|
|
116
|
+
}
|
|
117
|
+
// ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
|
|
109
118
|
const completedAt = new Date().toISOString();
|
|
119
|
+
let guardError = null;
|
|
110
120
|
transaction(() => {
|
|
121
|
+
// State machine preconditions (inside txn for atomicity).
|
|
122
|
+
// Milestone/slice not existing is OK — insertMilestone/insertSlice below will auto-create.
|
|
123
|
+
// Only block if they exist and are closed.
|
|
124
|
+
const milestone = getMilestone(params.milestoneId);
|
|
125
|
+
if (milestone && (milestone.status === "complete" || milestone.status === "done")) {
|
|
126
|
+
guardError = `cannot complete task in a closed milestone: ${params.milestoneId} (status: ${milestone.status})`;
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const slice = getSlice(params.milestoneId, params.sliceId);
|
|
130
|
+
if (slice && (slice.status === "complete" || slice.status === "done")) {
|
|
131
|
+
guardError = `cannot complete task in a closed slice: ${params.sliceId} (status: ${slice.status})`;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const existingTask = getTask(params.milestoneId, params.sliceId, params.taskId);
|
|
135
|
+
if (existingTask && (existingTask.status === "complete" || existingTask.status === "done")) {
|
|
136
|
+
guardError = `task ${params.taskId} is already complete — use gsd_task_reopen first if you need to redo it`;
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
// All guards passed — perform writes
|
|
111
140
|
insertMilestone({ id: params.milestoneId });
|
|
112
141
|
insertSlice({ id: params.sliceId, milestoneId: params.milestoneId });
|
|
113
142
|
insertTask({
|
|
@@ -138,6 +167,9 @@ export async function handleCompleteTask(params, basePath) {
|
|
|
138
167
|
});
|
|
139
168
|
}
|
|
140
169
|
});
|
|
170
|
+
if (guardError) {
|
|
171
|
+
return { error: guardError };
|
|
172
|
+
}
|
|
141
173
|
// ── Filesystem operations (outside transaction) ─────────────────────────
|
|
142
174
|
// If disk render fails, roll back the DB status so deriveState() and
|
|
143
175
|
// verifyExpectedArtifact() stay consistent (both say "not done").
|
|
@@ -195,6 +227,22 @@ export async function handleCompleteTask(params, basePath) {
|
|
|
195
227
|
invalidateStateCache();
|
|
196
228
|
clearPathCache();
|
|
197
229
|
clearParseCache();
|
|
230
|
+
// ── Post-mutation hook: projections, manifest, event log ───────────────
|
|
231
|
+
try {
|
|
232
|
+
await renderAllProjections(basePath, params.milestoneId);
|
|
233
|
+
writeManifest(basePath);
|
|
234
|
+
appendEvent(basePath, {
|
|
235
|
+
cmd: "complete-task",
|
|
236
|
+
params: { milestoneId: params.milestoneId, sliceId: params.sliceId, taskId: params.taskId },
|
|
237
|
+
ts: new Date().toISOString(),
|
|
238
|
+
actor: "agent",
|
|
239
|
+
actor_name: params.actorName,
|
|
240
|
+
trigger_reason: params.triggerReason,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
catch (hookErr) {
|
|
244
|
+
process.stderr.write(`gsd: complete-task post-mutation hook warning: ${hookErr.message}\n`);
|
|
245
|
+
}
|
|
198
246
|
return {
|
|
199
247
|
taskId: params.taskId,
|
|
200
248
|
sliceId: params.sliceId,
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { clearParseCache } from "../files.js";
|
|
2
|
-
import { transaction, insertMilestone, insertSlice, upsertMilestonePlanning, upsertSlicePlanning, } from "../gsd-db.js";
|
|
2
|
+
import { transaction, getMilestone, insertMilestone, insertSlice, upsertMilestonePlanning, upsertSlicePlanning, } from "../gsd-db.js";
|
|
3
3
|
import { invalidateStateCache } from "../state.js";
|
|
4
4
|
import { renderRoadmapFromDb } from "../markdown-renderer.js";
|
|
5
|
+
import { renderAllProjections } from "../workflow-projections.js";
|
|
6
|
+
import { writeManifest } from "../workflow-manifest.js";
|
|
7
|
+
import { appendEvent } from "../workflow-events.js";
|
|
5
8
|
function isNonEmptyString(value) {
|
|
6
9
|
return typeof value === "string" && value.trim().length > 0;
|
|
7
10
|
}
|
|
@@ -142,6 +145,23 @@ export async function handlePlanMilestone(rawParams, basePath) {
|
|
|
142
145
|
catch (err) {
|
|
143
146
|
return { error: `validation failed: ${err.message}` };
|
|
144
147
|
}
|
|
148
|
+
// ── State machine preconditions ─────────────────────────────────────────
|
|
149
|
+
const existingMilestone = getMilestone(params.milestoneId);
|
|
150
|
+
if (existingMilestone && (existingMilestone.status === "complete" || existingMilestone.status === "done")) {
|
|
151
|
+
return { error: `cannot re-plan milestone ${params.milestoneId}: it is already complete` };
|
|
152
|
+
}
|
|
153
|
+
// Validate depends_on: all dependencies must exist and be complete
|
|
154
|
+
if (params.dependsOn && params.dependsOn.length > 0) {
|
|
155
|
+
for (const depId of params.dependsOn) {
|
|
156
|
+
const dep = getMilestone(depId);
|
|
157
|
+
if (!dep) {
|
|
158
|
+
return { error: `depends_on references unknown milestone: ${depId}` };
|
|
159
|
+
}
|
|
160
|
+
if (dep.status !== "complete" && dep.status !== "done") {
|
|
161
|
+
return { error: `depends_on milestone ${depId} is not yet complete (status: ${dep.status})` };
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
145
165
|
try {
|
|
146
166
|
transaction(() => {
|
|
147
167
|
insertMilestone({
|
|
@@ -198,6 +218,22 @@ export async function handlePlanMilestone(rawParams, basePath) {
|
|
|
198
218
|
}
|
|
199
219
|
invalidateStateCache();
|
|
200
220
|
clearParseCache();
|
|
221
|
+
// ── Post-mutation hook: projections, manifest, event log ───────────────
|
|
222
|
+
try {
|
|
223
|
+
await renderAllProjections(basePath, params.milestoneId);
|
|
224
|
+
writeManifest(basePath);
|
|
225
|
+
appendEvent(basePath, {
|
|
226
|
+
cmd: "plan-milestone",
|
|
227
|
+
params: { milestoneId: params.milestoneId },
|
|
228
|
+
ts: new Date().toISOString(),
|
|
229
|
+
actor: "agent",
|
|
230
|
+
actor_name: params.actorName,
|
|
231
|
+
trigger_reason: params.triggerReason,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
catch (hookErr) {
|
|
235
|
+
process.stderr.write(`gsd: plan-milestone post-mutation hook warning: ${hookErr.message}\n`);
|
|
236
|
+
}
|
|
201
237
|
return {
|
|
202
238
|
milestoneId: params.milestoneId,
|
|
203
239
|
roadmapPath,
|