gsd-pi 2.43.0 → 2.44.0-dev.0b97ffd
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -12
- package/dist/cli.js +13 -1
- package/dist/help-text.js +24 -0
- package/dist/resources/extensions/bg-shell/overlay.js +3 -0
- package/dist/resources/extensions/github-sync/sync.js +2 -1
- package/dist/resources/extensions/gsd/auto/loop.js +0 -2
- package/dist/resources/extensions/gsd/auto/phases.js +7 -12
- package/dist/resources/extensions/gsd/auto-dashboard.js +19 -18
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +34 -19
- package/dist/resources/extensions/gsd/auto-dispatch.js +36 -21
- package/dist/resources/extensions/gsd/auto-post-unit.js +128 -14
- package/dist/resources/extensions/gsd/auto-prompts.js +202 -92
- package/dist/resources/extensions/gsd/auto-recovery.js +83 -135
- package/dist/resources/extensions/gsd/auto-start.js +10 -0
- package/dist/resources/extensions/gsd/auto-supervisor.js +14 -0
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +4 -7
- package/dist/resources/extensions/gsd/auto-verification.js +5 -10
- package/dist/resources/extensions/gsd/auto-worktree.js +123 -30
- package/dist/resources/extensions/gsd/auto.js +1 -4
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +611 -0
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +28 -3
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -0
- package/dist/resources/extensions/gsd/commands/catalog.js +3 -1
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +15 -1
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
- package/dist/resources/extensions/gsd/commands-handlers.js +1 -1
- package/dist/resources/extensions/gsd/commands-maintenance.js +78 -3
- package/dist/resources/extensions/gsd/dashboard-overlay.js +32 -31
- package/dist/resources/extensions/gsd/db-writer.js +95 -4
- package/dist/resources/extensions/gsd/dispatch-guard.js +35 -30
- package/dist/resources/extensions/gsd/doctor-checks.js +28 -11
- package/dist/resources/extensions/gsd/doctor-environment.js +28 -0
- package/dist/resources/extensions/gsd/doctor-types.js +0 -15
- package/dist/resources/extensions/gsd/doctor.js +46 -282
- package/dist/resources/extensions/gsd/file-watcher.js +5 -1
- package/dist/resources/extensions/gsd/files.js +14 -198
- package/dist/resources/extensions/gsd/git-service.js +4 -0
- package/dist/resources/extensions/gsd/gitignore.js +4 -0
- package/dist/resources/extensions/gsd/gsd-db.js +819 -197
- package/dist/resources/extensions/gsd/guided-flow.js +18 -8
- package/dist/resources/extensions/gsd/markdown-renderer.js +862 -0
- package/dist/resources/extensions/gsd/md-importer.js +182 -4
- package/dist/resources/extensions/gsd/native-git-bridge.js +10 -1
- package/dist/resources/extensions/gsd/parallel-eligibility.js +14 -19
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +38 -0
- package/dist/resources/extensions/gsd/parsers-legacy.js +239 -0
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +9 -0
- package/dist/resources/extensions/gsd/preferences.js +1 -0
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +22 -9
- package/dist/resources/extensions/gsd/prompts/discuss.md +2 -2
- package/dist/resources/extensions/gsd/prompts/execute-task.md +15 -5
- 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/plan-milestone.md +6 -10
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -7
- package/dist/resources/extensions/gsd/prompts/reactive-execute.md +3 -3
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -7
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +6 -6
- package/dist/resources/extensions/gsd/reactive-graph.js +33 -3
- package/dist/resources/extensions/gsd/skill-health.js +3 -1
- package/dist/resources/extensions/gsd/state.js +484 -30
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +128 -0
- package/dist/resources/extensions/gsd/tools/complete-slice.js +244 -0
- package/dist/resources/extensions/gsd/tools/complete-task.js +204 -0
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +205 -0
- package/dist/resources/extensions/gsd/tools/plan-slice.js +155 -0
- package/dist/resources/extensions/gsd/tools/plan-task.js +94 -0
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +152 -0
- package/dist/resources/extensions/gsd/tools/replan-slice.js +146 -0
- package/dist/resources/extensions/gsd/triage-resolution.js +17 -1
- package/dist/resources/extensions/gsd/undo.js +197 -3
- package/dist/resources/extensions/gsd/visualizer-data.js +53 -16
- package/dist/resources/extensions/gsd/workspace-index.js +63 -39
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +18 -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/routes-manifest.json +6 -0
- 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 +4 -4
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -4
- 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 +4 -4
- 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 +2 -2
- 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 -0
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -0
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -0
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +5 -5
- 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 +5 -5
- 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 +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +18 -17
- package/dist/web/standalone/.next/server/chunks/229.js +3 -3
- package/dist/web/standalone/.next/server/chunks/471.js +3 -3
- package/dist/web/standalone/.next/server/functions-config-manifest.json +1 -0
- 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/alS4hoANx0TK4UVZY27da/_buildManifest.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{4024.c195dc1fdd2adbea.js → 4024.0de81b543b28b9fe.js} +2 -2
- package/dist/web/standalone/.next/static/chunks/app/_global-error/{page-d07a2c023f1aef1e.js → page-d83ba70a25a85472.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/_not-found/page-f2a7482d42a5614b.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/boot/{route-d07a2c023f1aef1e.js → route-d83ba70a25a85472.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/input/{route-d07a2c023f1aef1e.js → route-d83ba70a25a85472.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/resize/{route-d07a2c023f1aef1e.js → route-d83ba70a25a85472.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/captures/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/files/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/git/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/history/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/projects/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/steer/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/switch-root/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/undo/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/update/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/page-b9367c5ae13b99c6.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{main-app-2f2ee7b85712c2bd.js → main-app-fdab67f7802d7832.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-d83ba70a25a85472.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/next/dist/client/components/builtin/not-found-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-d83ba70a25a85472.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-fa307370fcf9fb2c.js → webpack-9014b5adb127a98a.js} +1 -1
- package/dist/web/standalone/.next/static/css/8a727f372cf53002.css +1 -0
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/package.json +4 -4
- package/packages/pi-ai/dist/models.custom.d.ts +173 -0
- package/packages/pi-ai/dist/models.custom.d.ts.map +1 -0
- package/packages/pi-ai/dist/models.custom.js +170 -0
- package/packages/pi-ai/dist/models.custom.js.map +1 -0
- package/packages/pi-ai/dist/models.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.js +16 -1
- package/packages/pi-ai/dist/models.js.map +1 -1
- package/packages/pi-ai/dist/models.test.d.ts +2 -0
- package/packages/pi-ai/dist/models.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/models.test.js +67 -0
- package/packages/pi-ai/dist/models.test.js.map +1 -0
- package/packages/pi-ai/src/models.custom.ts +172 -0
- package/packages/pi-ai/src/models.test.ts +85 -0
- package/packages/pi-ai/src/models.ts +17 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +10 -3
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +21 -34
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts +2 -2
- package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +2 -2
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +4 -4
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +80 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.test.js +63 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +37 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/fallback-resolver.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/fallback-resolver.js +2 -3
- package/packages/pi-coding-agent/dist/core/fallback-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/fallback-resolver.test.js +12 -2
- package/packages/pi-coding-agent/dist/core/fallback-resolver.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
- package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts +38 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +192 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.js +69 -21
- package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +255 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +15 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +40 -3
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-commands.d.ts +25 -0
- package/packages/pi-coding-agent/dist/core/package-commands.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/package-commands.js +253 -0
- package/packages/pi-coding-agent/dist/core/package-commands.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/package-commands.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/package-commands.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/package-commands.test.js +225 -0
- package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +4 -0
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
- package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +3 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -0
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +11 -199
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts +6 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +21 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +8 -15
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/print-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/print-mode.js +45 -34
- package/packages/pi-coding-agent/dist/modes/print-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts +1 -0
- 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 +7 -2
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +2 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +26 -37
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
- package/packages/pi-coding-agent/src/core/compaction/branch-summarization.ts +2 -2
- package/packages/pi-coding-agent/src/core/compaction/compaction.ts +3 -3
- package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +4 -4
- package/packages/pi-coding-agent/src/core/extensions/index.ts +5 -0
- package/packages/pi-coding-agent/src/core/extensions/loader.test.ts +96 -0
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +89 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
- package/packages/pi-coding-agent/src/core/extensions/types.ts +44 -0
- package/packages/pi-coding-agent/src/core/fallback-resolver.test.ts +15 -2
- package/packages/pi-coding-agent/src/core/fallback-resolver.ts +2 -3
- package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
- package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +274 -0
- package/packages/pi-coding-agent/src/core/lsp/client.ts +83 -21
- package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +288 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +39 -3
- package/packages/pi-coding-agent/src/core/package-commands.test.ts +240 -0
- package/packages/pi-coding-agent/src/core/package-commands.ts +310 -0
- package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
- package/packages/pi-coding-agent/src/core/sdk.ts +4 -0
- package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
- package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
- package/packages/pi-coding-agent/src/index.ts +7 -0
- package/packages/pi-coding-agent/src/main.ts +11 -232
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +20 -0
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +9 -16
- package/packages/pi-coding-agent/src/modes/print-mode.ts +42 -32
- package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +8 -2
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -1
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
- package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme.js +8 -15
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/bg-shell/overlay.ts +4 -0
- package/src/resources/extensions/github-sync/sync.ts +2 -1
- package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -8
- package/src/resources/extensions/gsd/auto/loop.ts +0 -2
- package/src/resources/extensions/gsd/auto/phases.ts +7 -20
- package/src/resources/extensions/gsd/auto/types.ts +0 -1
- package/src/resources/extensions/gsd/auto-dashboard.ts +20 -16
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +34 -19
- package/src/resources/extensions/gsd/auto-dispatch.ts +38 -21
- package/src/resources/extensions/gsd/auto-post-unit.ts +150 -15
- package/src/resources/extensions/gsd/auto-prompts.ts +186 -103
- package/src/resources/extensions/gsd/auto-recovery.ts +77 -142
- package/src/resources/extensions/gsd/auto-start.ts +14 -0
- package/src/resources/extensions/gsd/auto-supervisor.ts +14 -0
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +6 -7
- package/src/resources/extensions/gsd/auto-verification.ts +4 -9
- package/src/resources/extensions/gsd/auto-worktree.ts +126 -30
- package/src/resources/extensions/gsd/auto.ts +0 -9
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +675 -4
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +31 -3
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +7 -0
- package/src/resources/extensions/gsd/commands/catalog.ts +3 -1
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +15 -1
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
- package/src/resources/extensions/gsd/commands-handlers.ts +1 -1
- package/src/resources/extensions/gsd/commands-maintenance.ts +86 -3
- package/src/resources/extensions/gsd/dashboard-overlay.ts +17 -13
- package/src/resources/extensions/gsd/db-writer.ts +105 -4
- package/src/resources/extensions/gsd/dispatch-guard.ts +32 -30
- package/src/resources/extensions/gsd/doctor-checks.ts +25 -11
- package/src/resources/extensions/gsd/doctor-environment.ts +31 -0
- package/src/resources/extensions/gsd/doctor-types.ts +0 -23
- package/src/resources/extensions/gsd/doctor.ts +45 -295
- package/src/resources/extensions/gsd/file-watcher.ts +4 -1
- package/src/resources/extensions/gsd/files.ts +16 -220
- package/src/resources/extensions/gsd/git-service.ts +4 -0
- package/src/resources/extensions/gsd/gitignore.ts +4 -0
- package/src/resources/extensions/gsd/gsd-db.ts +1157 -370
- package/src/resources/extensions/gsd/guided-flow.ts +20 -8
- package/src/resources/extensions/gsd/markdown-renderer.ts +1098 -0
- package/src/resources/extensions/gsd/md-importer.ts +211 -2
- package/src/resources/extensions/gsd/native-git-bridge.ts +12 -1
- package/src/resources/extensions/gsd/parallel-eligibility.ts +14 -18
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +43 -0
- package/src/resources/extensions/gsd/parsers-legacy.ts +271 -0
- package/src/resources/extensions/gsd/preferences-types.ts +3 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +9 -0
- package/src/resources/extensions/gsd/preferences.ts +1 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +22 -9
- package/src/resources/extensions/gsd/prompts/discuss.md +2 -2
- package/src/resources/extensions/gsd/prompts/execute-task.md +15 -5
- 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/plan-milestone.md +6 -10
- package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -7
- package/src/resources/extensions/gsd/prompts/reactive-execute.md +3 -3
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -7
- package/src/resources/extensions/gsd/prompts/replan-slice.md +6 -6
- package/src/resources/extensions/gsd/reactive-graph.ts +33 -3
- package/src/resources/extensions/gsd/skill-health.ts +2 -1
- package/src/resources/extensions/gsd/state.ts +547 -29
- package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
- package/src/resources/extensions/gsd/tests/atomic-task-closeout.test.ts +8 -120
- package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +20 -11
- package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
- package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +600 -513
- package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
- package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
- package/src/resources/extensions/gsd/tests/auto-stash-merge.test.ts +121 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +540 -668
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
- package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
- package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
- package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
- package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
- package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
- package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +39 -60
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +375 -0
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +387 -0
- package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
- package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +0 -2
- package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +390 -420
- package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
- package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +512 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +644 -84
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
- package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
- package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +192 -161
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
- package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
- package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +30 -90
- package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
- package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +57 -80
- package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +164 -0
- package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +55 -153
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
- package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +53 -97
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
- package/src/resources/extensions/gsd/tests/doctor.test.ts +109 -149
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
- package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
- package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
- package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +278 -0
- package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +232 -0
- package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
- package/src/resources/extensions/gsd/tests/git-service.test.ts +291 -390
- package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
- package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
- package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
- package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +440 -0
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
- package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
- package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -270
- package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
- package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
- package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
- package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
- package/src/resources/extensions/gsd/tests/integration-proof.test.ts +643 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -3
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +1161 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
- package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +81 -94
- package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
- package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +429 -0
- package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
- package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
- package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +89 -97
- package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +127 -164
- package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +35 -36
- package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
- package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
- package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
- package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
- package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
- package/src/resources/extensions/gsd/tests/parsers.test.ts +548 -612
- package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
- package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +176 -113
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +179 -0
- package/src/resources/extensions/gsd/tests/plan-task.test.ts +145 -0
- package/src/resources/extensions/gsd/tests/planning-crossval.test.ts +305 -0
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +139 -0
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
- package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
- package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
- package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
- package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
- package/src/resources/extensions/gsd/tests/reassess-handler.test.ts +325 -0
- package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
- package/src/resources/extensions/gsd/tests/replan-handler.test.ts +410 -0
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
- package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
- package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
- package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
- package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +296 -0
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
- package/src/resources/extensions/gsd/tests/schema-v9-sequence.test.ts +176 -0
- package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
- package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
- package/src/resources/extensions/gsd/tests/shared-wal.test.ts +209 -0
- package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
- package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
- package/src/resources/extensions/gsd/tests/token-cost-display.test.ts +118 -0
- package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +19 -13
- package/src/resources/extensions/gsd/tests/undo.test.ts +321 -1
- package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/verification-evidence.test.ts +0 -142
- package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
- package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
- package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
- package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
- package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
- package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +12 -5
- package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
- package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
- package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +176 -0
- package/src/resources/extensions/gsd/tools/complete-slice.ts +300 -0
- package/src/resources/extensions/gsd/tools/complete-task.ts +245 -0
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +249 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +194 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +116 -0
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +203 -0
- package/src/resources/extensions/gsd/tools/replan-slice.ts +192 -0
- package/src/resources/extensions/gsd/triage-resolution.ts +20 -1
- package/src/resources/extensions/gsd/types.ts +50 -0
- package/src/resources/extensions/gsd/undo.ts +247 -3
- package/src/resources/extensions/gsd/visualizer-data.ts +54 -17
- package/src/resources/extensions/gsd/workspace-index.ts +64 -46
- package/dist/resources/extensions/gsd/auto-observability.js +0 -56
- package/dist/resources/extensions/gsd/observability-validator.js +0 -422
- package/dist/resources/extensions/gsd/roadmap-mutations.js +0 -110
- package/dist/web/standalone/.next/static/VvclDCW6TAWjEyLU-EYL1/_buildManifest.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/_not-found/page-e07acdb7dd069836.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/captures/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/files/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/git/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/history/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/projects/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/steer/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/undo/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/update/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/layout-745c6ed5fea5fb06.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/page-801b53eff6e83579.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-e6255954dccfcf0a.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-d07a2c023f1aef1e.js +0 -1
- package/dist/web/standalone/.next/static/css/123c0bb039697968.css +0 -1
- package/src/resources/extensions/gsd/auto-observability.ts +0 -74
- package/src/resources/extensions/gsd/observability-validator.ts +0 -456
- package/src/resources/extensions/gsd/roadmap-mutations.ts +0 -134
- package/src/resources/extensions/gsd/tests/doctor-task-done-missing-summary-slice-loop.test.ts +0 -174
- package/src/resources/extensions/gsd/tests/plan-quality-validator.test.ts +0 -474
- /package/dist/web/standalone/.next/static/{VvclDCW6TAWjEyLU-EYL1 → alS4hoANx0TK4UVZY27da}/_ssgManifest.js +0 -0
|
@@ -11,15 +11,8 @@ import { dirname } from "node:path";
|
|
|
11
11
|
import type { Decision, Requirement } from "./types.js";
|
|
12
12
|
import { GSDError, GSD_STALE_STATE } from "./errors.js";
|
|
13
13
|
|
|
14
|
-
// Create a require function for loading native modules in ESM context
|
|
15
14
|
const _require = createRequire(import.meta.url);
|
|
16
15
|
|
|
17
|
-
// ─── Provider Abstraction ──────────────────────────────────────────────────
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Minimal interface over both node:sqlite DatabaseSync and better-sqlite3 Database.
|
|
21
|
-
* Both expose prepare().run/get/all — the adapter normalizes row objects.
|
|
22
|
-
*/
|
|
23
16
|
interface DbStatement {
|
|
24
17
|
run(...params: unknown[]): unknown;
|
|
25
18
|
get(...params: unknown[]): Record<string, unknown> | undefined;
|
|
@@ -38,13 +31,9 @@ let providerName: ProviderName | null = null;
|
|
|
38
31
|
let providerModule: unknown = null;
|
|
39
32
|
let loadAttempted = false;
|
|
40
33
|
|
|
41
|
-
/**
|
|
42
|
-
* Suppress the ExperimentalWarning for SQLite from node:sqlite.
|
|
43
|
-
* Must be called before require('node:sqlite').
|
|
44
|
-
*/
|
|
45
34
|
function suppressSqliteWarning(): void {
|
|
46
35
|
const origEmit = process.emit;
|
|
47
|
-
// @ts-expect-error
|
|
36
|
+
// @ts-expect-error overriding process.emit for warning filter
|
|
48
37
|
process.emit = function (event: string, ...args: unknown[]): boolean {
|
|
49
38
|
if (
|
|
50
39
|
event === "warning" &&
|
|
@@ -58,9 +47,7 @@ function suppressSqliteWarning(): void {
|
|
|
58
47
|
) {
|
|
59
48
|
return false;
|
|
60
49
|
}
|
|
61
|
-
return origEmit.apply(process, [event, ...args] as Parameters<
|
|
62
|
-
typeof process.emit
|
|
63
|
-
>) as unknown as boolean;
|
|
50
|
+
return origEmit.apply(process, [event, ...args] as Parameters<typeof process.emit>) as unknown as boolean;
|
|
64
51
|
};
|
|
65
52
|
}
|
|
66
53
|
|
|
@@ -68,7 +55,6 @@ function loadProvider(): void {
|
|
|
68
55
|
if (loadAttempted) return;
|
|
69
56
|
loadAttempted = true;
|
|
70
57
|
|
|
71
|
-
// Try node:sqlite first
|
|
72
58
|
try {
|
|
73
59
|
suppressSqliteWarning();
|
|
74
60
|
const mod = _require("node:sqlite");
|
|
@@ -78,10 +64,9 @@ function loadProvider(): void {
|
|
|
78
64
|
return;
|
|
79
65
|
}
|
|
80
66
|
} catch {
|
|
81
|
-
//
|
|
67
|
+
// unavailable
|
|
82
68
|
}
|
|
83
69
|
|
|
84
|
-
// Try better-sqlite3
|
|
85
70
|
try {
|
|
86
71
|
const mod = _require("better-sqlite3");
|
|
87
72
|
if (typeof mod === "function" || (mod && mod.default)) {
|
|
@@ -90,7 +75,7 @@ function loadProvider(): void {
|
|
|
90
75
|
return;
|
|
91
76
|
}
|
|
92
77
|
} catch {
|
|
93
|
-
//
|
|
78
|
+
// unavailable
|
|
94
79
|
}
|
|
95
80
|
|
|
96
81
|
process.stderr.write(
|
|
@@ -98,11 +83,6 @@ function loadProvider(): void {
|
|
|
98
83
|
);
|
|
99
84
|
}
|
|
100
85
|
|
|
101
|
-
// ─── Database Adapter ──────────────────────────────────────────────────────
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Normalize a row from node:sqlite (null-prototype) to a plain object.
|
|
105
|
-
*/
|
|
106
86
|
function normalizeRow(row: unknown): Record<string, unknown> | undefined {
|
|
107
87
|
if (row == null) return undefined;
|
|
108
88
|
if (Object.getPrototypeOf(row) === null) {
|
|
@@ -161,20 +141,16 @@ function openRawDb(path: string): unknown {
|
|
|
161
141
|
return new DatabaseSync(path);
|
|
162
142
|
}
|
|
163
143
|
|
|
164
|
-
// better-sqlite3
|
|
165
144
|
const Database = providerModule as new (path: string) => unknown;
|
|
166
145
|
return new Database(path);
|
|
167
146
|
}
|
|
168
147
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const SCHEMA_VERSION = 4;
|
|
148
|
+
const SCHEMA_VERSION = 10;
|
|
172
149
|
|
|
173
150
|
function initSchema(db: DbAdapter, fileBacked: boolean): void {
|
|
174
|
-
|
|
175
|
-
if (fileBacked)
|
|
176
|
-
|
|
177
|
-
}
|
|
151
|
+
if (fileBacked) db.exec("PRAGMA journal_mode=WAL");
|
|
152
|
+
if (fileBacked) db.exec("PRAGMA busy_timeout = 5000");
|
|
153
|
+
db.exec("PRAGMA foreign_keys = ON");
|
|
178
154
|
|
|
179
155
|
db.exec("BEGIN");
|
|
180
156
|
try {
|
|
@@ -253,25 +229,135 @@ function initSchema(db: DbAdapter, fileBacked: boolean): void {
|
|
|
253
229
|
)
|
|
254
230
|
`);
|
|
255
231
|
|
|
256
|
-
db.exec(
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
232
|
+
db.exec(`
|
|
233
|
+
CREATE TABLE IF NOT EXISTS milestones (
|
|
234
|
+
id TEXT PRIMARY KEY,
|
|
235
|
+
title TEXT NOT NULL DEFAULT '',
|
|
236
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
237
|
+
depends_on TEXT NOT NULL DEFAULT '[]',
|
|
238
|
+
created_at TEXT NOT NULL DEFAULT '',
|
|
239
|
+
completed_at TEXT DEFAULT NULL,
|
|
240
|
+
vision TEXT NOT NULL DEFAULT '',
|
|
241
|
+
success_criteria TEXT NOT NULL DEFAULT '[]',
|
|
242
|
+
key_risks TEXT NOT NULL DEFAULT '[]',
|
|
243
|
+
proof_strategy TEXT NOT NULL DEFAULT '[]',
|
|
244
|
+
verification_contract TEXT NOT NULL DEFAULT '',
|
|
245
|
+
verification_integration TEXT NOT NULL DEFAULT '',
|
|
246
|
+
verification_operational TEXT NOT NULL DEFAULT '',
|
|
247
|
+
verification_uat TEXT NOT NULL DEFAULT '',
|
|
248
|
+
definition_of_done TEXT NOT NULL DEFAULT '[]',
|
|
249
|
+
requirement_coverage TEXT NOT NULL DEFAULT '',
|
|
250
|
+
boundary_map_markdown TEXT NOT NULL DEFAULT ''
|
|
251
|
+
)
|
|
252
|
+
`);
|
|
253
|
+
|
|
254
|
+
db.exec(`
|
|
255
|
+
CREATE TABLE IF NOT EXISTS slices (
|
|
256
|
+
milestone_id TEXT NOT NULL,
|
|
257
|
+
id TEXT NOT NULL,
|
|
258
|
+
title TEXT NOT NULL DEFAULT '',
|
|
259
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
260
|
+
risk TEXT NOT NULL DEFAULT 'medium',
|
|
261
|
+
depends TEXT NOT NULL DEFAULT '[]',
|
|
262
|
+
demo TEXT NOT NULL DEFAULT '',
|
|
263
|
+
created_at TEXT NOT NULL DEFAULT '',
|
|
264
|
+
completed_at TEXT DEFAULT NULL,
|
|
265
|
+
full_summary_md TEXT NOT NULL DEFAULT '',
|
|
266
|
+
full_uat_md TEXT NOT NULL DEFAULT '',
|
|
267
|
+
goal TEXT NOT NULL DEFAULT '',
|
|
268
|
+
success_criteria TEXT NOT NULL DEFAULT '',
|
|
269
|
+
proof_level TEXT NOT NULL DEFAULT '',
|
|
270
|
+
integration_closure TEXT NOT NULL DEFAULT '',
|
|
271
|
+
observability_impact TEXT NOT NULL DEFAULT '',
|
|
272
|
+
sequence INTEGER DEFAULT 0, -- DEAD CODE: no tool exposes sequence — always 0
|
|
273
|
+
replan_triggered_at TEXT DEFAULT NULL,
|
|
274
|
+
PRIMARY KEY (milestone_id, id),
|
|
275
|
+
FOREIGN KEY (milestone_id) REFERENCES milestones(id)
|
|
276
|
+
)
|
|
277
|
+
`);
|
|
278
|
+
|
|
279
|
+
db.exec(`
|
|
280
|
+
CREATE TABLE IF NOT EXISTS tasks (
|
|
281
|
+
milestone_id TEXT NOT NULL,
|
|
282
|
+
slice_id TEXT NOT NULL,
|
|
283
|
+
id TEXT NOT NULL,
|
|
284
|
+
title TEXT NOT NULL DEFAULT '',
|
|
285
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
286
|
+
one_liner TEXT NOT NULL DEFAULT '',
|
|
287
|
+
narrative TEXT NOT NULL DEFAULT '',
|
|
288
|
+
verification_result TEXT NOT NULL DEFAULT '',
|
|
289
|
+
duration TEXT NOT NULL DEFAULT '',
|
|
290
|
+
completed_at TEXT DEFAULT NULL,
|
|
291
|
+
blocker_discovered INTEGER DEFAULT 0,
|
|
292
|
+
deviations TEXT NOT NULL DEFAULT '',
|
|
293
|
+
known_issues TEXT NOT NULL DEFAULT '',
|
|
294
|
+
key_files TEXT NOT NULL DEFAULT '[]',
|
|
295
|
+
key_decisions TEXT NOT NULL DEFAULT '[]',
|
|
296
|
+
full_summary_md TEXT NOT NULL DEFAULT '',
|
|
297
|
+
description TEXT NOT NULL DEFAULT '',
|
|
298
|
+
estimate TEXT NOT NULL DEFAULT '',
|
|
299
|
+
files TEXT NOT NULL DEFAULT '[]',
|
|
300
|
+
verify TEXT NOT NULL DEFAULT '',
|
|
301
|
+
inputs TEXT NOT NULL DEFAULT '[]',
|
|
302
|
+
expected_output TEXT NOT NULL DEFAULT '[]',
|
|
303
|
+
observability_impact TEXT NOT NULL DEFAULT '',
|
|
304
|
+
sequence INTEGER DEFAULT 0, -- DEAD CODE: no tool exposes sequence — always 0
|
|
305
|
+
PRIMARY KEY (milestone_id, slice_id, id),
|
|
306
|
+
FOREIGN KEY (milestone_id, slice_id) REFERENCES slices(milestone_id, id)
|
|
307
|
+
)
|
|
308
|
+
`);
|
|
309
|
+
|
|
310
|
+
db.exec(`
|
|
311
|
+
CREATE TABLE IF NOT EXISTS verification_evidence (
|
|
312
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
313
|
+
task_id TEXT NOT NULL DEFAULT '',
|
|
314
|
+
slice_id TEXT NOT NULL DEFAULT '',
|
|
315
|
+
milestone_id TEXT NOT NULL DEFAULT '',
|
|
316
|
+
command TEXT NOT NULL DEFAULT '',
|
|
317
|
+
exit_code INTEGER DEFAULT 0,
|
|
318
|
+
verdict TEXT NOT NULL DEFAULT '',
|
|
319
|
+
duration_ms INTEGER DEFAULT 0,
|
|
320
|
+
created_at TEXT NOT NULL DEFAULT '',
|
|
321
|
+
FOREIGN KEY (milestone_id, slice_id, task_id) REFERENCES tasks(milestone_id, slice_id, id)
|
|
322
|
+
)
|
|
323
|
+
`);
|
|
324
|
+
|
|
325
|
+
db.exec(`
|
|
326
|
+
CREATE TABLE IF NOT EXISTS replan_history (
|
|
327
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
328
|
+
milestone_id TEXT NOT NULL DEFAULT '',
|
|
329
|
+
slice_id TEXT DEFAULT NULL,
|
|
330
|
+
task_id TEXT DEFAULT NULL,
|
|
331
|
+
summary TEXT NOT NULL DEFAULT '',
|
|
332
|
+
previous_artifact_path TEXT DEFAULT NULL,
|
|
333
|
+
replacement_artifact_path TEXT DEFAULT NULL,
|
|
334
|
+
created_at TEXT NOT NULL DEFAULT '',
|
|
335
|
+
FOREIGN KEY (milestone_id) REFERENCES milestones(id)
|
|
336
|
+
)
|
|
337
|
+
`);
|
|
338
|
+
|
|
339
|
+
db.exec(`
|
|
340
|
+
CREATE TABLE IF NOT EXISTS assessments (
|
|
341
|
+
path TEXT PRIMARY KEY,
|
|
342
|
+
milestone_id TEXT NOT NULL DEFAULT '',
|
|
343
|
+
slice_id TEXT DEFAULT NULL,
|
|
344
|
+
task_id TEXT DEFAULT NULL,
|
|
345
|
+
status TEXT NOT NULL DEFAULT '',
|
|
346
|
+
scope TEXT NOT NULL DEFAULT '',
|
|
347
|
+
full_content TEXT NOT NULL DEFAULT '',
|
|
348
|
+
created_at TEXT NOT NULL DEFAULT '',
|
|
349
|
+
FOREIGN KEY (milestone_id) REFERENCES milestones(id)
|
|
350
|
+
)
|
|
351
|
+
`);
|
|
352
|
+
|
|
353
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_memories_active ON memories(superseded_by)");
|
|
354
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_replan_history_milestone ON replan_history(milestone_id, created_at)");
|
|
355
|
+
|
|
356
|
+
db.exec(`CREATE VIEW IF NOT EXISTS active_decisions AS SELECT * FROM decisions WHERE superseded_by IS NULL`);
|
|
357
|
+
db.exec(`CREATE VIEW IF NOT EXISTS active_requirements AS SELECT * FROM requirements WHERE superseded_by IS NULL`);
|
|
358
|
+
db.exec(`CREATE VIEW IF NOT EXISTS active_memories AS SELECT * FROM memories WHERE superseded_by IS NULL`);
|
|
359
|
+
|
|
360
|
+
const existing = db.prepare("SELECT count(*) as cnt FROM schema_version").get();
|
|
275
361
|
if (existing && (existing["cnt"] as number) === 0) {
|
|
276
362
|
db.prepare(
|
|
277
363
|
"INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)",
|
|
@@ -287,23 +373,25 @@ function initSchema(db: DbAdapter, fileBacked: boolean): void {
|
|
|
287
373
|
throw err;
|
|
288
374
|
}
|
|
289
375
|
|
|
290
|
-
// Run incremental migrations for existing databases
|
|
291
376
|
migrateSchema(db);
|
|
292
377
|
}
|
|
293
378
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
379
|
+
function columnExists(db: DbAdapter, table: string, column: string): boolean {
|
|
380
|
+
const rows = db.prepare(`PRAGMA table_info(${table})`).all();
|
|
381
|
+
return rows.some((row) => row["name"] === column);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function ensureColumn(db: DbAdapter, table: string, column: string, ddl: string): void {
|
|
385
|
+
if (!columnExists(db, table, column)) db.exec(ddl);
|
|
386
|
+
}
|
|
387
|
+
|
|
298
388
|
function migrateSchema(db: DbAdapter): void {
|
|
299
389
|
const row = db.prepare("SELECT MAX(version) as v FROM schema_version").get();
|
|
300
390
|
const currentVersion = row ? (row["v"] as number) : 0;
|
|
301
|
-
|
|
302
391
|
if (currentVersion >= SCHEMA_VERSION) return;
|
|
303
392
|
|
|
304
393
|
db.exec("BEGIN");
|
|
305
394
|
try {
|
|
306
|
-
// v1 → v2: add artifacts table
|
|
307
395
|
if (currentVersion < 2) {
|
|
308
396
|
db.exec(`
|
|
309
397
|
CREATE TABLE IF NOT EXISTS artifacts (
|
|
@@ -316,13 +404,12 @@ function migrateSchema(db: DbAdapter): void {
|
|
|
316
404
|
imported_at TEXT NOT NULL DEFAULT ''
|
|
317
405
|
)
|
|
318
406
|
`);
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
"
|
|
322
|
-
|
|
407
|
+
db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
|
|
408
|
+
":version": 2,
|
|
409
|
+
":applied_at": new Date().toISOString(),
|
|
410
|
+
});
|
|
323
411
|
}
|
|
324
412
|
|
|
325
|
-
// v2 → v3: add memories + memory_processed_units tables
|
|
326
413
|
if (currentVersion < 3) {
|
|
327
414
|
db.exec(`
|
|
328
415
|
CREATE TABLE IF NOT EXISTS memories (
|
|
@@ -339,7 +426,6 @@ function migrateSchema(db: DbAdapter): void {
|
|
|
339
426
|
hit_count INTEGER NOT NULL DEFAULT 0
|
|
340
427
|
)
|
|
341
428
|
`);
|
|
342
|
-
|
|
343
429
|
db.exec(`
|
|
344
430
|
CREATE TABLE IF NOT EXISTS memory_processed_units (
|
|
345
431
|
unit_key TEXT PRIMARY KEY,
|
|
@@ -347,34 +433,187 @@ function migrateSchema(db: DbAdapter): void {
|
|
|
347
433
|
processed_at TEXT NOT NULL
|
|
348
434
|
)
|
|
349
435
|
`);
|
|
350
|
-
|
|
351
|
-
db.exec(
|
|
352
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_active ON memories(superseded_by)",
|
|
353
|
-
);
|
|
436
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_memories_active ON memories(superseded_by)");
|
|
354
437
|
db.exec("DROP VIEW IF EXISTS active_memories");
|
|
355
|
-
db.exec(
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
"INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)",
|
|
361
|
-
).run({ ":version": 3, ":applied_at": new Date().toISOString() });
|
|
438
|
+
db.exec("CREATE VIEW active_memories AS SELECT * FROM memories WHERE superseded_by IS NULL");
|
|
439
|
+
db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
|
|
440
|
+
":version": 3,
|
|
441
|
+
":applied_at": new Date().toISOString(),
|
|
442
|
+
});
|
|
362
443
|
}
|
|
363
444
|
|
|
364
|
-
// v3 → v4: add made_by column to decisions table
|
|
365
445
|
if (currentVersion < 4) {
|
|
366
|
-
|
|
367
|
-
db.exec(`ALTER TABLE decisions ADD COLUMN made_by TEXT NOT NULL DEFAULT 'agent'`);
|
|
368
|
-
|
|
369
|
-
// Recreate views to pick up new columns (SQLite expands SELECT * at view creation time)
|
|
446
|
+
ensureColumn(db, "decisions", "made_by", `ALTER TABLE decisions ADD COLUMN made_by TEXT NOT NULL DEFAULT 'agent'`);
|
|
370
447
|
db.exec("DROP VIEW IF EXISTS active_decisions");
|
|
371
|
-
db.exec(
|
|
372
|
-
|
|
373
|
-
|
|
448
|
+
db.exec("CREATE VIEW active_decisions AS SELECT * FROM decisions WHERE superseded_by IS NULL");
|
|
449
|
+
db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
|
|
450
|
+
":version": 4,
|
|
451
|
+
":applied_at": new Date().toISOString(),
|
|
452
|
+
});
|
|
453
|
+
}
|
|
374
454
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
455
|
+
if (currentVersion < 5) {
|
|
456
|
+
db.exec(`
|
|
457
|
+
CREATE TABLE IF NOT EXISTS milestones (
|
|
458
|
+
id TEXT PRIMARY KEY,
|
|
459
|
+
title TEXT NOT NULL DEFAULT '',
|
|
460
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
461
|
+
created_at TEXT NOT NULL,
|
|
462
|
+
completed_at TEXT DEFAULT NULL
|
|
463
|
+
)
|
|
464
|
+
`);
|
|
465
|
+
db.exec(`
|
|
466
|
+
CREATE TABLE IF NOT EXISTS slices (
|
|
467
|
+
milestone_id TEXT NOT NULL,
|
|
468
|
+
id TEXT NOT NULL,
|
|
469
|
+
title TEXT NOT NULL DEFAULT '',
|
|
470
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
471
|
+
risk TEXT NOT NULL DEFAULT 'medium',
|
|
472
|
+
created_at TEXT NOT NULL DEFAULT '',
|
|
473
|
+
completed_at TEXT DEFAULT NULL,
|
|
474
|
+
PRIMARY KEY (milestone_id, id),
|
|
475
|
+
FOREIGN KEY (milestone_id) REFERENCES milestones(id)
|
|
476
|
+
)
|
|
477
|
+
`);
|
|
478
|
+
db.exec(`
|
|
479
|
+
CREATE TABLE IF NOT EXISTS tasks (
|
|
480
|
+
milestone_id TEXT NOT NULL,
|
|
481
|
+
slice_id TEXT NOT NULL,
|
|
482
|
+
id TEXT NOT NULL,
|
|
483
|
+
title TEXT NOT NULL DEFAULT '',
|
|
484
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
485
|
+
one_liner TEXT NOT NULL DEFAULT '',
|
|
486
|
+
narrative TEXT NOT NULL DEFAULT '',
|
|
487
|
+
verification_result TEXT NOT NULL DEFAULT '',
|
|
488
|
+
duration TEXT NOT NULL DEFAULT '',
|
|
489
|
+
completed_at TEXT DEFAULT NULL,
|
|
490
|
+
blocker_discovered INTEGER DEFAULT 0,
|
|
491
|
+
deviations TEXT NOT NULL DEFAULT '',
|
|
492
|
+
known_issues TEXT NOT NULL DEFAULT '',
|
|
493
|
+
key_files TEXT NOT NULL DEFAULT '[]',
|
|
494
|
+
key_decisions TEXT NOT NULL DEFAULT '[]',
|
|
495
|
+
full_summary_md TEXT NOT NULL DEFAULT '',
|
|
496
|
+
PRIMARY KEY (milestone_id, slice_id, id),
|
|
497
|
+
FOREIGN KEY (milestone_id, slice_id) REFERENCES slices(milestone_id, id)
|
|
498
|
+
)
|
|
499
|
+
`);
|
|
500
|
+
db.exec(`
|
|
501
|
+
CREATE TABLE IF NOT EXISTS verification_evidence (
|
|
502
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
503
|
+
task_id TEXT NOT NULL DEFAULT '',
|
|
504
|
+
slice_id TEXT NOT NULL DEFAULT '',
|
|
505
|
+
milestone_id TEXT NOT NULL DEFAULT '',
|
|
506
|
+
command TEXT NOT NULL DEFAULT '',
|
|
507
|
+
exit_code INTEGER DEFAULT 0,
|
|
508
|
+
verdict TEXT NOT NULL DEFAULT '',
|
|
509
|
+
duration_ms INTEGER DEFAULT 0,
|
|
510
|
+
created_at TEXT NOT NULL DEFAULT '',
|
|
511
|
+
FOREIGN KEY (milestone_id, slice_id, task_id) REFERENCES tasks(milestone_id, slice_id, id)
|
|
512
|
+
)
|
|
513
|
+
`);
|
|
514
|
+
db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
|
|
515
|
+
":version": 5,
|
|
516
|
+
":applied_at": new Date().toISOString(),
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (currentVersion < 6) {
|
|
521
|
+
ensureColumn(db, "slices", "full_summary_md", `ALTER TABLE slices ADD COLUMN full_summary_md TEXT NOT NULL DEFAULT ''`);
|
|
522
|
+
ensureColumn(db, "slices", "full_uat_md", `ALTER TABLE slices ADD COLUMN full_uat_md TEXT NOT NULL DEFAULT ''`);
|
|
523
|
+
db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
|
|
524
|
+
":version": 6,
|
|
525
|
+
":applied_at": new Date().toISOString(),
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (currentVersion < 7) {
|
|
530
|
+
ensureColumn(db, "slices", "depends", `ALTER TABLE slices ADD COLUMN depends TEXT NOT NULL DEFAULT '[]'`);
|
|
531
|
+
ensureColumn(db, "slices", "demo", `ALTER TABLE slices ADD COLUMN demo TEXT NOT NULL DEFAULT ''`);
|
|
532
|
+
ensureColumn(db, "milestones", "depends_on", `ALTER TABLE milestones ADD COLUMN depends_on TEXT NOT NULL DEFAULT '[]'`);
|
|
533
|
+
db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
|
|
534
|
+
":version": 7,
|
|
535
|
+
":applied_at": new Date().toISOString(),
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
if (currentVersion < 8) {
|
|
540
|
+
ensureColumn(db, "milestones", "vision", `ALTER TABLE milestones ADD COLUMN vision TEXT NOT NULL DEFAULT ''`);
|
|
541
|
+
ensureColumn(db, "milestones", "success_criteria", `ALTER TABLE milestones ADD COLUMN success_criteria TEXT NOT NULL DEFAULT '[]'`);
|
|
542
|
+
ensureColumn(db, "milestones", "key_risks", `ALTER TABLE milestones ADD COLUMN key_risks TEXT NOT NULL DEFAULT '[]'`);
|
|
543
|
+
ensureColumn(db, "milestones", "proof_strategy", `ALTER TABLE milestones ADD COLUMN proof_strategy TEXT NOT NULL DEFAULT '[]'`);
|
|
544
|
+
ensureColumn(db, "milestones", "verification_contract", `ALTER TABLE milestones ADD COLUMN verification_contract TEXT NOT NULL DEFAULT ''`);
|
|
545
|
+
ensureColumn(db, "milestones", "verification_integration", `ALTER TABLE milestones ADD COLUMN verification_integration TEXT NOT NULL DEFAULT ''`);
|
|
546
|
+
ensureColumn(db, "milestones", "verification_operational", `ALTER TABLE milestones ADD COLUMN verification_operational TEXT NOT NULL DEFAULT ''`);
|
|
547
|
+
ensureColumn(db, "milestones", "verification_uat", `ALTER TABLE milestones ADD COLUMN verification_uat TEXT NOT NULL DEFAULT ''`);
|
|
548
|
+
ensureColumn(db, "milestones", "definition_of_done", `ALTER TABLE milestones ADD COLUMN definition_of_done TEXT NOT NULL DEFAULT '[]'`);
|
|
549
|
+
ensureColumn(db, "milestones", "requirement_coverage", `ALTER TABLE milestones ADD COLUMN requirement_coverage TEXT NOT NULL DEFAULT ''`);
|
|
550
|
+
ensureColumn(db, "milestones", "boundary_map_markdown", `ALTER TABLE milestones ADD COLUMN boundary_map_markdown TEXT NOT NULL DEFAULT ''`);
|
|
551
|
+
|
|
552
|
+
ensureColumn(db, "slices", "goal", `ALTER TABLE slices ADD COLUMN goal TEXT NOT NULL DEFAULT ''`);
|
|
553
|
+
ensureColumn(db, "slices", "success_criteria", `ALTER TABLE slices ADD COLUMN success_criteria TEXT NOT NULL DEFAULT ''`);
|
|
554
|
+
ensureColumn(db, "slices", "proof_level", `ALTER TABLE slices ADD COLUMN proof_level TEXT NOT NULL DEFAULT ''`);
|
|
555
|
+
ensureColumn(db, "slices", "integration_closure", `ALTER TABLE slices ADD COLUMN integration_closure TEXT NOT NULL DEFAULT ''`);
|
|
556
|
+
ensureColumn(db, "slices", "observability_impact", `ALTER TABLE slices ADD COLUMN observability_impact TEXT NOT NULL DEFAULT ''`);
|
|
557
|
+
|
|
558
|
+
ensureColumn(db, "tasks", "description", `ALTER TABLE tasks ADD COLUMN description TEXT NOT NULL DEFAULT ''`);
|
|
559
|
+
ensureColumn(db, "tasks", "estimate", `ALTER TABLE tasks ADD COLUMN estimate TEXT NOT NULL DEFAULT ''`);
|
|
560
|
+
ensureColumn(db, "tasks", "files", `ALTER TABLE tasks ADD COLUMN files TEXT NOT NULL DEFAULT '[]'`);
|
|
561
|
+
ensureColumn(db, "tasks", "verify", `ALTER TABLE tasks ADD COLUMN verify TEXT NOT NULL DEFAULT ''`);
|
|
562
|
+
ensureColumn(db, "tasks", "inputs", `ALTER TABLE tasks ADD COLUMN inputs TEXT NOT NULL DEFAULT '[]'`);
|
|
563
|
+
ensureColumn(db, "tasks", "expected_output", `ALTER TABLE tasks ADD COLUMN expected_output TEXT NOT NULL DEFAULT '[]'`);
|
|
564
|
+
ensureColumn(db, "tasks", "observability_impact", `ALTER TABLE tasks ADD COLUMN observability_impact TEXT NOT NULL DEFAULT ''`);
|
|
565
|
+
|
|
566
|
+
db.exec(`
|
|
567
|
+
CREATE TABLE IF NOT EXISTS replan_history (
|
|
568
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
569
|
+
milestone_id TEXT NOT NULL DEFAULT '',
|
|
570
|
+
slice_id TEXT DEFAULT NULL,
|
|
571
|
+
task_id TEXT DEFAULT NULL,
|
|
572
|
+
summary TEXT NOT NULL DEFAULT '',
|
|
573
|
+
previous_artifact_path TEXT DEFAULT NULL,
|
|
574
|
+
replacement_artifact_path TEXT DEFAULT NULL,
|
|
575
|
+
created_at TEXT NOT NULL DEFAULT '',
|
|
576
|
+
FOREIGN KEY (milestone_id) REFERENCES milestones(id)
|
|
577
|
+
)
|
|
578
|
+
`);
|
|
579
|
+
db.exec(`
|
|
580
|
+
CREATE TABLE IF NOT EXISTS assessments (
|
|
581
|
+
path TEXT PRIMARY KEY,
|
|
582
|
+
milestone_id TEXT NOT NULL DEFAULT '',
|
|
583
|
+
slice_id TEXT DEFAULT NULL,
|
|
584
|
+
task_id TEXT DEFAULT NULL,
|
|
585
|
+
status TEXT NOT NULL DEFAULT '',
|
|
586
|
+
scope TEXT NOT NULL DEFAULT '',
|
|
587
|
+
full_content TEXT NOT NULL DEFAULT '',
|
|
588
|
+
created_at TEXT NOT NULL DEFAULT '',
|
|
589
|
+
FOREIGN KEY (milestone_id) REFERENCES milestones(id)
|
|
590
|
+
)
|
|
591
|
+
`);
|
|
592
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_replan_history_milestone ON replan_history(milestone_id, created_at)");
|
|
593
|
+
|
|
594
|
+
db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
|
|
595
|
+
":version": 8,
|
|
596
|
+
":applied_at": new Date().toISOString(),
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
if (currentVersion < 9) {
|
|
601
|
+
ensureColumn(db, "slices", "sequence", `ALTER TABLE slices ADD COLUMN sequence INTEGER DEFAULT 0`);
|
|
602
|
+
ensureColumn(db, "tasks", "sequence", `ALTER TABLE tasks ADD COLUMN sequence INTEGER DEFAULT 0`);
|
|
603
|
+
|
|
604
|
+
db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
|
|
605
|
+
":version": 9,
|
|
606
|
+
":applied_at": new Date().toISOString(),
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if (currentVersion < 10) {
|
|
611
|
+
ensureColumn(db, "slices", "replan_triggered_at", `ALTER TABLE slices ADD COLUMN replan_triggered_at TEXT DEFAULT NULL`);
|
|
612
|
+
|
|
613
|
+
db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
|
|
614
|
+
":version": 10,
|
|
615
|
+
":applied_at": new Date().toISOString(),
|
|
616
|
+
});
|
|
378
617
|
}
|
|
379
618
|
|
|
380
619
|
db.exec("COMMIT");
|
|
@@ -384,72 +623,53 @@ function migrateSchema(db: DbAdapter): void {
|
|
|
384
623
|
}
|
|
385
624
|
}
|
|
386
625
|
|
|
387
|
-
// ─── Module State ──────────────────────────────────────────────────────────
|
|
388
|
-
|
|
389
626
|
let currentDb: DbAdapter | null = null;
|
|
390
627
|
let currentPath: string | null = null;
|
|
391
|
-
/** PID that opened the current connection — used for diagnostic logging. */
|
|
392
628
|
let currentPid: number = 0;
|
|
629
|
+
let _exitHandlerRegistered = false;
|
|
393
630
|
|
|
394
|
-
// ─── Public API ────────────────────────────────────────────────────────────
|
|
395
|
-
|
|
396
|
-
/**
|
|
397
|
-
* Returns which SQLite provider is available, or null if none.
|
|
398
|
-
*/
|
|
399
631
|
export function getDbProvider(): ProviderName | null {
|
|
400
632
|
loadProvider();
|
|
401
633
|
return providerName;
|
|
402
634
|
}
|
|
403
635
|
|
|
404
|
-
/**
|
|
405
|
-
* Returns true if a database is currently open and usable.
|
|
406
|
-
*/
|
|
407
636
|
export function isDbAvailable(): boolean {
|
|
408
637
|
return currentDb !== null;
|
|
409
638
|
}
|
|
410
639
|
|
|
411
|
-
/**
|
|
412
|
-
* Opens (or creates) a SQLite database at the given path.
|
|
413
|
-
* Initializes schema if needed. Sets WAL mode for file-backed DBs.
|
|
414
|
-
* Returns true on success, false if no provider is available.
|
|
415
|
-
*/
|
|
416
640
|
export function openDatabase(path: string): boolean {
|
|
417
|
-
|
|
418
|
-
if (currentDb && currentPath
|
|
419
|
-
closeDatabase();
|
|
420
|
-
}
|
|
421
|
-
if (currentDb && currentPath === path) {
|
|
422
|
-
return true; // already open
|
|
423
|
-
}
|
|
641
|
+
if (currentDb && currentPath !== path) closeDatabase();
|
|
642
|
+
if (currentDb && currentPath === path) return true;
|
|
424
643
|
|
|
425
644
|
const rawDb = openRawDb(path);
|
|
426
645
|
if (!rawDb) return false;
|
|
427
646
|
|
|
428
647
|
const adapter = createAdapter(rawDb);
|
|
429
648
|
const fileBacked = path !== ":memory:";
|
|
430
|
-
|
|
431
649
|
try {
|
|
432
650
|
initSchema(adapter, fileBacked);
|
|
433
651
|
} catch (err) {
|
|
434
|
-
try {
|
|
435
|
-
adapter.close();
|
|
436
|
-
} catch {
|
|
437
|
-
/* swallow */
|
|
438
|
-
}
|
|
652
|
+
try { adapter.close(); } catch { /* swallow */ }
|
|
439
653
|
throw err;
|
|
440
654
|
}
|
|
441
655
|
|
|
442
656
|
currentDb = adapter;
|
|
443
657
|
currentPath = path;
|
|
444
658
|
currentPid = process.pid;
|
|
659
|
+
|
|
660
|
+
if (!_exitHandlerRegistered) {
|
|
661
|
+
_exitHandlerRegistered = true;
|
|
662
|
+
process.on("exit", () => { try { closeDatabase(); } catch {} });
|
|
663
|
+
}
|
|
664
|
+
|
|
445
665
|
return true;
|
|
446
666
|
}
|
|
447
667
|
|
|
448
|
-
/**
|
|
449
|
-
* Closes the current database connection.
|
|
450
|
-
*/
|
|
451
668
|
export function closeDatabase(): void {
|
|
452
669
|
if (currentDb) {
|
|
670
|
+
try {
|
|
671
|
+
currentDb.exec('PRAGMA wal_checkpoint(TRUNCATE)');
|
|
672
|
+
} catch { /* non-fatal — best effort before close */ }
|
|
453
673
|
try {
|
|
454
674
|
currentDb.close();
|
|
455
675
|
} catch {
|
|
@@ -461,12 +681,8 @@ export function closeDatabase(): void {
|
|
|
461
681
|
}
|
|
462
682
|
}
|
|
463
683
|
|
|
464
|
-
/**
|
|
465
|
-
* Runs a function inside a transaction. Rolls back on error.
|
|
466
|
-
*/
|
|
467
684
|
export function transaction<T>(fn: () => T): T {
|
|
468
|
-
if (!currentDb)
|
|
469
|
-
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
685
|
+
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
470
686
|
currentDb.exec("BEGIN");
|
|
471
687
|
try {
|
|
472
688
|
const result = fn();
|
|
@@ -478,35 +694,24 @@ export function transaction<T>(fn: () => T): T {
|
|
|
478
694
|
}
|
|
479
695
|
}
|
|
480
696
|
|
|
481
|
-
// ─── Decision Wrappers ────────────────────────────────────────────────────
|
|
482
|
-
|
|
483
|
-
/**
|
|
484
|
-
* Insert a decision. The `seq` field is auto-generated.
|
|
485
|
-
*/
|
|
486
697
|
export function insertDecision(d: Omit<Decision, "seq">): void {
|
|
487
|
-
if (!currentDb)
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
.prepare(
|
|
491
|
-
`INSERT INTO decisions (id, when_context, scope, decision, choice, rationale, revisable, made_by, superseded_by)
|
|
698
|
+
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
699
|
+
currentDb.prepare(
|
|
700
|
+
`INSERT INTO decisions (id, when_context, scope, decision, choice, rationale, revisable, made_by, superseded_by)
|
|
492
701
|
VALUES (:id, :when_context, :scope, :decision, :choice, :rationale, :revisable, :made_by, :superseded_by)`,
|
|
493
|
-
|
|
494
|
-
.
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
/**
|
|
508
|
-
* Get a decision by its ID (e.g. "D001"). Returns null if not found.
|
|
509
|
-
*/
|
|
702
|
+
).run({
|
|
703
|
+
":id": d.id,
|
|
704
|
+
":when_context": d.when_context,
|
|
705
|
+
":scope": d.scope,
|
|
706
|
+
":decision": d.decision,
|
|
707
|
+
":choice": d.choice,
|
|
708
|
+
":rationale": d.rationale,
|
|
709
|
+
":revisable": d.revisable,
|
|
710
|
+
":made_by": d.made_by ?? "agent",
|
|
711
|
+
":superseded_by": d.superseded_by,
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
|
|
510
715
|
export function getDecisionById(id: string): Decision | null {
|
|
511
716
|
if (!currentDb) return null;
|
|
512
717
|
const row = currentDb.prepare("SELECT * FROM decisions WHERE id = ?").get(id);
|
|
@@ -525,9 +730,6 @@ export function getDecisionById(id: string): Decision | null {
|
|
|
525
730
|
};
|
|
526
731
|
}
|
|
527
732
|
|
|
528
|
-
/**
|
|
529
|
-
* Get all active (non-superseded) decisions.
|
|
530
|
-
*/
|
|
531
733
|
export function getActiveDecisions(): Decision[] {
|
|
532
734
|
if (!currentDb) return [];
|
|
533
735
|
const rows = currentDb.prepare("SELECT * FROM active_decisions").all();
|
|
@@ -545,43 +747,30 @@ export function getActiveDecisions(): Decision[] {
|
|
|
545
747
|
}));
|
|
546
748
|
}
|
|
547
749
|
|
|
548
|
-
// ─── Requirement Wrappers ─────────────────────────────────────────────────
|
|
549
|
-
|
|
550
|
-
/**
|
|
551
|
-
* Insert a requirement.
|
|
552
|
-
*/
|
|
553
750
|
export function insertRequirement(r: Requirement): void {
|
|
554
|
-
if (!currentDb)
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
.prepare(
|
|
558
|
-
`INSERT INTO requirements (id, class, status, description, why, source, primary_owner, supporting_slices, validation, notes, full_content, superseded_by)
|
|
751
|
+
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
752
|
+
currentDb.prepare(
|
|
753
|
+
`INSERT INTO requirements (id, class, status, description, why, source, primary_owner, supporting_slices, validation, notes, full_content, superseded_by)
|
|
559
754
|
VALUES (:id, :class, :status, :description, :why, :source, :primary_owner, :supporting_slices, :validation, :notes, :full_content, :superseded_by)`,
|
|
560
|
-
|
|
561
|
-
.
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
/**
|
|
578
|
-
* Get a requirement by its ID (e.g. "R001"). Returns null if not found.
|
|
579
|
-
*/
|
|
755
|
+
).run({
|
|
756
|
+
":id": r.id,
|
|
757
|
+
":class": r.class,
|
|
758
|
+
":status": r.status,
|
|
759
|
+
":description": r.description,
|
|
760
|
+
":why": r.why,
|
|
761
|
+
":source": r.source,
|
|
762
|
+
":primary_owner": r.primary_owner,
|
|
763
|
+
":supporting_slices": r.supporting_slices,
|
|
764
|
+
":validation": r.validation,
|
|
765
|
+
":notes": r.notes,
|
|
766
|
+
":full_content": r.full_content,
|
|
767
|
+
":superseded_by": r.superseded_by,
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
|
|
580
771
|
export function getRequirementById(id: string): Requirement | null {
|
|
581
772
|
if (!currentDb) return null;
|
|
582
|
-
const row = currentDb
|
|
583
|
-
.prepare("SELECT * FROM requirements WHERE id = ?")
|
|
584
|
-
.get(id);
|
|
773
|
+
const row = currentDb.prepare("SELECT * FROM requirements WHERE id = ?").get(id);
|
|
585
774
|
if (!row) return null;
|
|
586
775
|
return {
|
|
587
776
|
id: row["id"] as string,
|
|
@@ -599,9 +788,6 @@ export function getRequirementById(id: string): Requirement | null {
|
|
|
599
788
|
};
|
|
600
789
|
}
|
|
601
790
|
|
|
602
|
-
/**
|
|
603
|
-
* Get all active (non-superseded) requirements.
|
|
604
|
-
*/
|
|
605
791
|
export function getActiveRequirements(): Requirement[] {
|
|
606
792
|
if (!currentDb) return [];
|
|
607
793
|
const rows = currentDb.prepare("SELECT * FROM active_requirements").all();
|
|
@@ -621,108 +807,66 @@ export function getActiveRequirements(): Requirement[] {
|
|
|
621
807
|
}));
|
|
622
808
|
}
|
|
623
809
|
|
|
624
|
-
/**
|
|
625
|
-
* Returns the PID of the process that opened the current DB connection.
|
|
626
|
-
* Returns 0 if no connection is open.
|
|
627
|
-
*/
|
|
628
810
|
export function getDbOwnerPid(): number {
|
|
629
811
|
return currentPid;
|
|
630
812
|
}
|
|
631
813
|
|
|
632
|
-
/**
|
|
633
|
-
* Returns the path of the currently open database, or null if none.
|
|
634
|
-
*/
|
|
635
814
|
export function getDbPath(): string | null {
|
|
636
815
|
return currentPath;
|
|
637
816
|
}
|
|
638
817
|
|
|
639
|
-
// ─── Internal Access (for testing) ─────────────────────────────────────────
|
|
640
|
-
|
|
641
|
-
/**
|
|
642
|
-
* Get the raw adapter for direct queries (testing only).
|
|
643
|
-
*/
|
|
644
818
|
export function _getAdapter(): DbAdapter | null {
|
|
645
819
|
return currentDb;
|
|
646
820
|
}
|
|
647
821
|
|
|
648
|
-
/**
|
|
649
|
-
* Reset provider state (testing only — allows re-detection).
|
|
650
|
-
*/
|
|
651
822
|
export function _resetProvider(): void {
|
|
652
823
|
loadAttempted = false;
|
|
653
824
|
providerModule = null;
|
|
654
825
|
providerName = null;
|
|
655
826
|
}
|
|
656
827
|
|
|
657
|
-
// ─── Upsert Wrappers (for idempotent import) ─────────────────────────────
|
|
658
|
-
|
|
659
|
-
/**
|
|
660
|
-
* Insert or replace a decision. Uses the `id` UNIQUE constraint for idempotency.
|
|
661
|
-
*/
|
|
662
828
|
export function upsertDecision(d: Omit<Decision, "seq">): void {
|
|
663
|
-
if (!currentDb)
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
.prepare(
|
|
667
|
-
`INSERT OR REPLACE INTO decisions (id, when_context, scope, decision, choice, rationale, revisable, made_by, superseded_by)
|
|
829
|
+
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
830
|
+
currentDb.prepare(
|
|
831
|
+
`INSERT OR REPLACE INTO decisions (id, when_context, scope, decision, choice, rationale, revisable, made_by, superseded_by)
|
|
668
832
|
VALUES (:id, :when_context, :scope, :decision, :choice, :rationale, :revisable, :made_by, :superseded_by)`,
|
|
669
|
-
|
|
670
|
-
.
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
/**
|
|
684
|
-
* Insert or replace a requirement. Uses the `id` PK for idempotency.
|
|
685
|
-
*/
|
|
833
|
+
).run({
|
|
834
|
+
":id": d.id,
|
|
835
|
+
":when_context": d.when_context,
|
|
836
|
+
":scope": d.scope,
|
|
837
|
+
":decision": d.decision,
|
|
838
|
+
":choice": d.choice,
|
|
839
|
+
":rationale": d.rationale,
|
|
840
|
+
":revisable": d.revisable,
|
|
841
|
+
":made_by": d.made_by ?? "agent",
|
|
842
|
+
":superseded_by": d.superseded_by ?? null,
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
|
|
686
846
|
export function upsertRequirement(r: Requirement): void {
|
|
687
|
-
if (!currentDb)
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
.prepare(
|
|
691
|
-
`INSERT OR REPLACE INTO requirements (id, class, status, description, why, source, primary_owner, supporting_slices, validation, notes, full_content, superseded_by)
|
|
847
|
+
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
848
|
+
currentDb.prepare(
|
|
849
|
+
`INSERT OR REPLACE INTO requirements (id, class, status, description, why, source, primary_owner, supporting_slices, validation, notes, full_content, superseded_by)
|
|
692
850
|
VALUES (:id, :class, :status, :description, :why, :source, :primary_owner, :supporting_slices, :validation, :notes, :full_content, :superseded_by)`,
|
|
693
|
-
|
|
694
|
-
.
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
/**
|
|
711
|
-
* Insert or replace an artifact. Uses the `path` PK for idempotency.
|
|
712
|
-
*/
|
|
713
|
-
/**
|
|
714
|
-
* Delete all rows from the artifacts table.
|
|
715
|
-
* The artifacts table is a read cache — clearing it forces the next
|
|
716
|
-
* deriveState() to fall through to disk reads (native Rust batch parse).
|
|
717
|
-
* Safe to call when no database is open (no-op).
|
|
718
|
-
*/
|
|
851
|
+
).run({
|
|
852
|
+
":id": r.id,
|
|
853
|
+
":class": r.class,
|
|
854
|
+
":status": r.status,
|
|
855
|
+
":description": r.description,
|
|
856
|
+
":why": r.why,
|
|
857
|
+
":source": r.source,
|
|
858
|
+
":primary_owner": r.primary_owner,
|
|
859
|
+
":supporting_slices": r.supporting_slices,
|
|
860
|
+
":validation": r.validation,
|
|
861
|
+
":notes": r.notes,
|
|
862
|
+
":full_content": r.full_content,
|
|
863
|
+
":superseded_by": r.superseded_by ?? null,
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
|
|
719
867
|
export function clearArtifacts(): void {
|
|
720
868
|
if (!currentDb) return;
|
|
721
|
-
try {
|
|
722
|
-
currentDb.exec("DELETE FROM artifacts");
|
|
723
|
-
} catch {
|
|
724
|
-
// Clearing a cache should never be fatal
|
|
725
|
-
}
|
|
869
|
+
try { currentDb.exec("DELETE FROM artifacts"); } catch { /* cache clear is best effort */ }
|
|
726
870
|
}
|
|
727
871
|
|
|
728
872
|
export function insertArtifact(a: {
|
|
@@ -733,22 +877,598 @@ export function insertArtifact(a: {
|
|
|
733
877
|
task_id: string | null;
|
|
734
878
|
full_content: string;
|
|
735
879
|
}): void {
|
|
736
|
-
if (!currentDb)
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
.prepare(
|
|
740
|
-
`INSERT OR REPLACE INTO artifacts (path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at)
|
|
880
|
+
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
881
|
+
currentDb.prepare(
|
|
882
|
+
`INSERT OR REPLACE INTO artifacts (path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at)
|
|
741
883
|
VALUES (:path, :artifact_type, :milestone_id, :slice_id, :task_id, :full_content, :imported_at)`,
|
|
884
|
+
).run({
|
|
885
|
+
":path": a.path,
|
|
886
|
+
":artifact_type": a.artifact_type,
|
|
887
|
+
":milestone_id": a.milestone_id,
|
|
888
|
+
":slice_id": a.slice_id,
|
|
889
|
+
":task_id": a.task_id,
|
|
890
|
+
":full_content": a.full_content,
|
|
891
|
+
":imported_at": new Date().toISOString(),
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
export interface MilestonePlanningRecord {
|
|
896
|
+
vision: string;
|
|
897
|
+
successCriteria: string[];
|
|
898
|
+
keyRisks: Array<{ risk: string; whyItMatters: string }>;
|
|
899
|
+
proofStrategy: Array<{ riskOrUnknown: string; retireIn: string; whatWillBeProven: string }>;
|
|
900
|
+
verificationContract: string;
|
|
901
|
+
verificationIntegration: string;
|
|
902
|
+
verificationOperational: string;
|
|
903
|
+
verificationUat: string;
|
|
904
|
+
definitionOfDone: string[];
|
|
905
|
+
requirementCoverage: string;
|
|
906
|
+
boundaryMapMarkdown: string;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
export interface SlicePlanningRecord {
|
|
910
|
+
goal: string;
|
|
911
|
+
successCriteria: string;
|
|
912
|
+
proofLevel: string;
|
|
913
|
+
integrationClosure: string;
|
|
914
|
+
observabilityImpact: string;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
export interface TaskPlanningRecord {
|
|
918
|
+
title?: string;
|
|
919
|
+
description: string;
|
|
920
|
+
estimate: string;
|
|
921
|
+
files: string[];
|
|
922
|
+
verify: string;
|
|
923
|
+
inputs: string[];
|
|
924
|
+
expectedOutput: string[];
|
|
925
|
+
observabilityImpact: string;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
export function insertMilestone(m: {
|
|
929
|
+
id: string;
|
|
930
|
+
title?: string;
|
|
931
|
+
status?: string;
|
|
932
|
+
depends_on?: string[];
|
|
933
|
+
planning?: Partial<MilestonePlanningRecord>;
|
|
934
|
+
}): void {
|
|
935
|
+
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
936
|
+
currentDb.prepare(
|
|
937
|
+
`INSERT OR IGNORE INTO milestones (
|
|
938
|
+
id, title, status, depends_on, created_at,
|
|
939
|
+
vision, success_criteria, key_risks, proof_strategy,
|
|
940
|
+
verification_contract, verification_integration, verification_operational, verification_uat,
|
|
941
|
+
definition_of_done, requirement_coverage, boundary_map_markdown
|
|
942
|
+
) VALUES (
|
|
943
|
+
:id, :title, :status, :depends_on, :created_at,
|
|
944
|
+
:vision, :success_criteria, :key_risks, :proof_strategy,
|
|
945
|
+
:verification_contract, :verification_integration, :verification_operational, :verification_uat,
|
|
946
|
+
:definition_of_done, :requirement_coverage, :boundary_map_markdown
|
|
947
|
+
)`,
|
|
948
|
+
).run({
|
|
949
|
+
":id": m.id,
|
|
950
|
+
":title": m.title ?? "",
|
|
951
|
+
":status": m.status ?? "active",
|
|
952
|
+
":depends_on": JSON.stringify(m.depends_on ?? []),
|
|
953
|
+
":created_at": new Date().toISOString(),
|
|
954
|
+
":vision": m.planning?.vision ?? "",
|
|
955
|
+
":success_criteria": JSON.stringify(m.planning?.successCriteria ?? []),
|
|
956
|
+
":key_risks": JSON.stringify(m.planning?.keyRisks ?? []),
|
|
957
|
+
":proof_strategy": JSON.stringify(m.planning?.proofStrategy ?? []),
|
|
958
|
+
":verification_contract": m.planning?.verificationContract ?? "",
|
|
959
|
+
":verification_integration": m.planning?.verificationIntegration ?? "",
|
|
960
|
+
":verification_operational": m.planning?.verificationOperational ?? "",
|
|
961
|
+
":verification_uat": m.planning?.verificationUat ?? "",
|
|
962
|
+
":definition_of_done": JSON.stringify(m.planning?.definitionOfDone ?? []),
|
|
963
|
+
":requirement_coverage": m.planning?.requirementCoverage ?? "",
|
|
964
|
+
":boundary_map_markdown": m.planning?.boundaryMapMarkdown ?? "",
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
export function upsertMilestonePlanning(milestoneId: string, planning: Partial<MilestonePlanningRecord>): void {
|
|
969
|
+
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
970
|
+
currentDb.prepare(
|
|
971
|
+
`UPDATE milestones SET
|
|
972
|
+
vision = COALESCE(:vision, vision),
|
|
973
|
+
success_criteria = COALESCE(:success_criteria, success_criteria),
|
|
974
|
+
key_risks = COALESCE(:key_risks, key_risks),
|
|
975
|
+
proof_strategy = COALESCE(:proof_strategy, proof_strategy),
|
|
976
|
+
verification_contract = COALESCE(:verification_contract, verification_contract),
|
|
977
|
+
verification_integration = COALESCE(:verification_integration, verification_integration),
|
|
978
|
+
verification_operational = COALESCE(:verification_operational, verification_operational),
|
|
979
|
+
verification_uat = COALESCE(:verification_uat, verification_uat),
|
|
980
|
+
definition_of_done = COALESCE(:definition_of_done, definition_of_done),
|
|
981
|
+
requirement_coverage = COALESCE(:requirement_coverage, requirement_coverage),
|
|
982
|
+
boundary_map_markdown = COALESCE(:boundary_map_markdown, boundary_map_markdown)
|
|
983
|
+
WHERE id = :id`,
|
|
984
|
+
).run({
|
|
985
|
+
":id": milestoneId,
|
|
986
|
+
":vision": planning.vision ?? null,
|
|
987
|
+
":success_criteria": planning.successCriteria ? JSON.stringify(planning.successCriteria) : null,
|
|
988
|
+
":key_risks": planning.keyRisks ? JSON.stringify(planning.keyRisks) : null,
|
|
989
|
+
":proof_strategy": planning.proofStrategy ? JSON.stringify(planning.proofStrategy) : null,
|
|
990
|
+
":verification_contract": planning.verificationContract ?? null,
|
|
991
|
+
":verification_integration": planning.verificationIntegration ?? null,
|
|
992
|
+
":verification_operational": planning.verificationOperational ?? null,
|
|
993
|
+
":verification_uat": planning.verificationUat ?? null,
|
|
994
|
+
":definition_of_done": planning.definitionOfDone ? JSON.stringify(planning.definitionOfDone) : null,
|
|
995
|
+
":requirement_coverage": planning.requirementCoverage ?? null,
|
|
996
|
+
":boundary_map_markdown": planning.boundaryMapMarkdown ?? null,
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
export function insertSlice(s: {
|
|
1001
|
+
id: string;
|
|
1002
|
+
milestoneId: string;
|
|
1003
|
+
title?: string;
|
|
1004
|
+
status?: string;
|
|
1005
|
+
risk?: string;
|
|
1006
|
+
depends?: string[];
|
|
1007
|
+
demo?: string;
|
|
1008
|
+
sequence?: number;
|
|
1009
|
+
planning?: Partial<SlicePlanningRecord>;
|
|
1010
|
+
}): void {
|
|
1011
|
+
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1012
|
+
currentDb.prepare(
|
|
1013
|
+
`INSERT OR IGNORE INTO slices (
|
|
1014
|
+
milestone_id, id, title, status, risk, depends, demo, created_at,
|
|
1015
|
+
goal, success_criteria, proof_level, integration_closure, observability_impact, sequence
|
|
1016
|
+
) VALUES (
|
|
1017
|
+
:milestone_id, :id, :title, :status, :risk, :depends, :demo, :created_at,
|
|
1018
|
+
:goal, :success_criteria, :proof_level, :integration_closure, :observability_impact, :sequence
|
|
1019
|
+
)`,
|
|
1020
|
+
).run({
|
|
1021
|
+
":milestone_id": s.milestoneId,
|
|
1022
|
+
":id": s.id,
|
|
1023
|
+
":title": s.title ?? "",
|
|
1024
|
+
":status": s.status ?? "pending",
|
|
1025
|
+
":risk": s.risk ?? "medium",
|
|
1026
|
+
":depends": JSON.stringify(s.depends ?? []),
|
|
1027
|
+
":demo": s.demo ?? "",
|
|
1028
|
+
":created_at": new Date().toISOString(),
|
|
1029
|
+
":goal": s.planning?.goal ?? "",
|
|
1030
|
+
":success_criteria": s.planning?.successCriteria ?? "",
|
|
1031
|
+
":proof_level": s.planning?.proofLevel ?? "",
|
|
1032
|
+
":integration_closure": s.planning?.integrationClosure ?? "",
|
|
1033
|
+
":observability_impact": s.planning?.observabilityImpact ?? "",
|
|
1034
|
+
":sequence": s.sequence ?? 0,
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
export function upsertSlicePlanning(milestoneId: string, sliceId: string, planning: Partial<SlicePlanningRecord>): void {
|
|
1039
|
+
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1040
|
+
currentDb.prepare(
|
|
1041
|
+
`UPDATE slices SET
|
|
1042
|
+
goal = COALESCE(:goal, goal),
|
|
1043
|
+
success_criteria = COALESCE(:success_criteria, success_criteria),
|
|
1044
|
+
proof_level = COALESCE(:proof_level, proof_level),
|
|
1045
|
+
integration_closure = COALESCE(:integration_closure, integration_closure),
|
|
1046
|
+
observability_impact = COALESCE(:observability_impact, observability_impact)
|
|
1047
|
+
WHERE milestone_id = :milestone_id AND id = :id`,
|
|
1048
|
+
).run({
|
|
1049
|
+
":milestone_id": milestoneId,
|
|
1050
|
+
":id": sliceId,
|
|
1051
|
+
":goal": planning.goal ?? null,
|
|
1052
|
+
":success_criteria": planning.successCriteria ?? null,
|
|
1053
|
+
":proof_level": planning.proofLevel ?? null,
|
|
1054
|
+
":integration_closure": planning.integrationClosure ?? null,
|
|
1055
|
+
":observability_impact": planning.observabilityImpact ?? null,
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
export function insertTask(t: {
|
|
1060
|
+
id: string;
|
|
1061
|
+
sliceId: string;
|
|
1062
|
+
milestoneId: string;
|
|
1063
|
+
title?: string;
|
|
1064
|
+
status?: string;
|
|
1065
|
+
oneLiner?: string;
|
|
1066
|
+
narrative?: string;
|
|
1067
|
+
verificationResult?: string;
|
|
1068
|
+
duration?: string;
|
|
1069
|
+
blockerDiscovered?: boolean;
|
|
1070
|
+
deviations?: string;
|
|
1071
|
+
knownIssues?: string;
|
|
1072
|
+
keyFiles?: string[];
|
|
1073
|
+
keyDecisions?: string[];
|
|
1074
|
+
fullSummaryMd?: string;
|
|
1075
|
+
sequence?: number;
|
|
1076
|
+
planning?: Partial<TaskPlanningRecord>;
|
|
1077
|
+
}): void {
|
|
1078
|
+
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1079
|
+
currentDb.prepare(
|
|
1080
|
+
`INSERT INTO tasks (
|
|
1081
|
+
milestone_id, slice_id, id, title, status, one_liner, narrative,
|
|
1082
|
+
verification_result, duration, completed_at, blocker_discovered,
|
|
1083
|
+
deviations, known_issues, key_files, key_decisions, full_summary_md,
|
|
1084
|
+
description, estimate, files, verify, inputs, expected_output, observability_impact, sequence
|
|
1085
|
+
) VALUES (
|
|
1086
|
+
:milestone_id, :slice_id, :id, :title, :status, :one_liner, :narrative,
|
|
1087
|
+
:verification_result, :duration, :completed_at, :blocker_discovered,
|
|
1088
|
+
:deviations, :known_issues, :key_files, :key_decisions, :full_summary_md,
|
|
1089
|
+
:description, :estimate, :files, :verify, :inputs, :expected_output, :observability_impact, :sequence
|
|
742
1090
|
)
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
1091
|
+
ON CONFLICT(milestone_id, slice_id, id) DO UPDATE SET
|
|
1092
|
+
title = CASE WHEN NULLIF(:title, '') IS NOT NULL THEN :title ELSE tasks.title END,
|
|
1093
|
+
status = :status,
|
|
1094
|
+
one_liner = :one_liner,
|
|
1095
|
+
narrative = :narrative,
|
|
1096
|
+
verification_result = :verification_result,
|
|
1097
|
+
duration = :duration,
|
|
1098
|
+
completed_at = :completed_at,
|
|
1099
|
+
blocker_discovered = :blocker_discovered,
|
|
1100
|
+
deviations = :deviations,
|
|
1101
|
+
known_issues = :known_issues,
|
|
1102
|
+
key_files = :key_files,
|
|
1103
|
+
key_decisions = :key_decisions,
|
|
1104
|
+
full_summary_md = :full_summary_md,
|
|
1105
|
+
description = CASE WHEN NULLIF(:description, '') IS NOT NULL THEN :description ELSE tasks.description END,
|
|
1106
|
+
estimate = CASE WHEN NULLIF(:estimate, '') IS NOT NULL THEN :estimate ELSE tasks.estimate END,
|
|
1107
|
+
files = CASE WHEN NULLIF(:files, '[]') IS NOT NULL THEN :files ELSE tasks.files END,
|
|
1108
|
+
verify = CASE WHEN NULLIF(:verify, '') IS NOT NULL THEN :verify ELSE tasks.verify END,
|
|
1109
|
+
inputs = CASE WHEN NULLIF(:inputs, '[]') IS NOT NULL THEN :inputs ELSE tasks.inputs END,
|
|
1110
|
+
expected_output = CASE WHEN NULLIF(:expected_output, '[]') IS NOT NULL THEN :expected_output ELSE tasks.expected_output END,
|
|
1111
|
+
observability_impact = CASE WHEN NULLIF(:observability_impact, '') IS NOT NULL THEN :observability_impact ELSE tasks.observability_impact END,
|
|
1112
|
+
sequence = :sequence`,
|
|
1113
|
+
).run({
|
|
1114
|
+
":milestone_id": t.milestoneId,
|
|
1115
|
+
":slice_id": t.sliceId,
|
|
1116
|
+
":id": t.id,
|
|
1117
|
+
":title": t.title ?? "",
|
|
1118
|
+
":status": t.status ?? "pending",
|
|
1119
|
+
":one_liner": t.oneLiner ?? "",
|
|
1120
|
+
":narrative": t.narrative ?? "",
|
|
1121
|
+
":verification_result": t.verificationResult ?? "",
|
|
1122
|
+
":duration": t.duration ?? "",
|
|
1123
|
+
":completed_at": t.status === "done" || t.status === "complete" ? new Date().toISOString() : null,
|
|
1124
|
+
":blocker_discovered": t.blockerDiscovered ? 1 : 0,
|
|
1125
|
+
":deviations": t.deviations ?? "",
|
|
1126
|
+
":known_issues": t.knownIssues ?? "",
|
|
1127
|
+
":key_files": JSON.stringify(t.keyFiles ?? []),
|
|
1128
|
+
":key_decisions": JSON.stringify(t.keyDecisions ?? []),
|
|
1129
|
+
":full_summary_md": t.fullSummaryMd ?? "",
|
|
1130
|
+
":description": t.planning?.description ?? "",
|
|
1131
|
+
":estimate": t.planning?.estimate ?? "",
|
|
1132
|
+
":files": JSON.stringify(t.planning?.files ?? []),
|
|
1133
|
+
":verify": t.planning?.verify ?? "",
|
|
1134
|
+
":inputs": JSON.stringify(t.planning?.inputs ?? []),
|
|
1135
|
+
":expected_output": JSON.stringify(t.planning?.expectedOutput ?? []),
|
|
1136
|
+
":observability_impact": t.planning?.observabilityImpact ?? "",
|
|
1137
|
+
":sequence": t.sequence ?? 0,
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
export function updateTaskStatus(milestoneId: string, sliceId: string, taskId: string, status: string, completedAt?: string): void {
|
|
1142
|
+
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1143
|
+
currentDb.prepare(
|
|
1144
|
+
`UPDATE tasks SET status = :status, completed_at = :completed_at
|
|
1145
|
+
WHERE milestone_id = :milestone_id AND slice_id = :slice_id AND id = :id`,
|
|
1146
|
+
).run({
|
|
1147
|
+
":status": status,
|
|
1148
|
+
":completed_at": completedAt ?? null,
|
|
1149
|
+
":milestone_id": milestoneId,
|
|
1150
|
+
":slice_id": sliceId,
|
|
1151
|
+
":id": taskId,
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
export function upsertTaskPlanning(milestoneId: string, sliceId: string, taskId: string, planning: Partial<TaskPlanningRecord>): void {
|
|
1156
|
+
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1157
|
+
currentDb.prepare(
|
|
1158
|
+
`UPDATE tasks SET
|
|
1159
|
+
title = COALESCE(:title, title),
|
|
1160
|
+
description = COALESCE(:description, description),
|
|
1161
|
+
estimate = COALESCE(:estimate, estimate),
|
|
1162
|
+
files = COALESCE(:files, files),
|
|
1163
|
+
verify = COALESCE(:verify, verify),
|
|
1164
|
+
inputs = COALESCE(:inputs, inputs),
|
|
1165
|
+
expected_output = COALESCE(:expected_output, expected_output),
|
|
1166
|
+
observability_impact = COALESCE(:observability_impact, observability_impact)
|
|
1167
|
+
WHERE milestone_id = :milestone_id AND slice_id = :slice_id AND id = :id`,
|
|
1168
|
+
).run({
|
|
1169
|
+
":milestone_id": milestoneId,
|
|
1170
|
+
":slice_id": sliceId,
|
|
1171
|
+
":id": taskId,
|
|
1172
|
+
":title": planning.title ?? null,
|
|
1173
|
+
":description": planning.description ?? null,
|
|
1174
|
+
":estimate": planning.estimate ?? null,
|
|
1175
|
+
":files": planning.files ? JSON.stringify(planning.files) : null,
|
|
1176
|
+
":verify": planning.verify ?? null,
|
|
1177
|
+
":inputs": planning.inputs ? JSON.stringify(planning.inputs) : null,
|
|
1178
|
+
":expected_output": planning.expectedOutput ? JSON.stringify(planning.expectedOutput) : null,
|
|
1179
|
+
":observability_impact": planning.observabilityImpact ?? null,
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
export interface SliceRow {
|
|
1184
|
+
milestone_id: string;
|
|
1185
|
+
id: string;
|
|
1186
|
+
title: string;
|
|
1187
|
+
status: string;
|
|
1188
|
+
risk: string;
|
|
1189
|
+
depends: string[];
|
|
1190
|
+
demo: string;
|
|
1191
|
+
created_at: string;
|
|
1192
|
+
completed_at: string | null;
|
|
1193
|
+
full_summary_md: string;
|
|
1194
|
+
full_uat_md: string;
|
|
1195
|
+
goal: string;
|
|
1196
|
+
success_criteria: string;
|
|
1197
|
+
proof_level: string;
|
|
1198
|
+
integration_closure: string;
|
|
1199
|
+
observability_impact: string;
|
|
1200
|
+
sequence: number;
|
|
1201
|
+
replan_triggered_at: string | null;
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
function rowToSlice(row: Record<string, unknown>): SliceRow {
|
|
1205
|
+
return {
|
|
1206
|
+
milestone_id: row["milestone_id"] as string,
|
|
1207
|
+
id: row["id"] as string,
|
|
1208
|
+
title: row["title"] as string,
|
|
1209
|
+
status: row["status"] as string,
|
|
1210
|
+
risk: row["risk"] as string,
|
|
1211
|
+
depends: JSON.parse((row["depends"] as string) || "[]"),
|
|
1212
|
+
demo: (row["demo"] as string) ?? "",
|
|
1213
|
+
created_at: row["created_at"] as string,
|
|
1214
|
+
completed_at: (row["completed_at"] as string) ?? null,
|
|
1215
|
+
full_summary_md: (row["full_summary_md"] as string) ?? "",
|
|
1216
|
+
full_uat_md: (row["full_uat_md"] as string) ?? "",
|
|
1217
|
+
goal: (row["goal"] as string) ?? "",
|
|
1218
|
+
success_criteria: (row["success_criteria"] as string) ?? "",
|
|
1219
|
+
proof_level: (row["proof_level"] as string) ?? "",
|
|
1220
|
+
integration_closure: (row["integration_closure"] as string) ?? "",
|
|
1221
|
+
observability_impact: (row["observability_impact"] as string) ?? "",
|
|
1222
|
+
sequence: (row["sequence"] as number) ?? 0,
|
|
1223
|
+
replan_triggered_at: (row["replan_triggered_at"] as string) ?? null,
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
export function getSlice(milestoneId: string, sliceId: string): SliceRow | null {
|
|
1228
|
+
if (!currentDb) return null;
|
|
1229
|
+
const row = currentDb.prepare("SELECT * FROM slices WHERE milestone_id = :mid AND id = :sid").get({ ":mid": milestoneId, ":sid": sliceId });
|
|
1230
|
+
if (!row) return null;
|
|
1231
|
+
return rowToSlice(row);
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
export function updateSliceStatus(milestoneId: string, sliceId: string, status: string, completedAt?: string): void {
|
|
1235
|
+
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1236
|
+
currentDb.prepare(
|
|
1237
|
+
`UPDATE slices SET status = :status, completed_at = :completed_at
|
|
1238
|
+
WHERE milestone_id = :milestone_id AND id = :id`,
|
|
1239
|
+
).run({
|
|
1240
|
+
":status": status,
|
|
1241
|
+
":completed_at": completedAt ?? null,
|
|
1242
|
+
":milestone_id": milestoneId,
|
|
1243
|
+
":id": sliceId,
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
export interface TaskRow {
|
|
1248
|
+
milestone_id: string;
|
|
1249
|
+
slice_id: string;
|
|
1250
|
+
id: string;
|
|
1251
|
+
title: string;
|
|
1252
|
+
status: string;
|
|
1253
|
+
one_liner: string;
|
|
1254
|
+
narrative: string;
|
|
1255
|
+
verification_result: string;
|
|
1256
|
+
duration: string;
|
|
1257
|
+
completed_at: string | null;
|
|
1258
|
+
blocker_discovered: boolean;
|
|
1259
|
+
deviations: string;
|
|
1260
|
+
known_issues: string;
|
|
1261
|
+
key_files: string[];
|
|
1262
|
+
key_decisions: string[];
|
|
1263
|
+
full_summary_md: string;
|
|
1264
|
+
description: string;
|
|
1265
|
+
estimate: string;
|
|
1266
|
+
files: string[];
|
|
1267
|
+
verify: string;
|
|
1268
|
+
inputs: string[];
|
|
1269
|
+
expected_output: string[];
|
|
1270
|
+
observability_impact: string;
|
|
1271
|
+
sequence: number;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
function rowToTask(row: Record<string, unknown>): TaskRow {
|
|
1275
|
+
return {
|
|
1276
|
+
milestone_id: row["milestone_id"] as string,
|
|
1277
|
+
slice_id: row["slice_id"] as string,
|
|
1278
|
+
id: row["id"] as string,
|
|
1279
|
+
title: row["title"] as string,
|
|
1280
|
+
status: row["status"] as string,
|
|
1281
|
+
one_liner: row["one_liner"] as string,
|
|
1282
|
+
narrative: row["narrative"] as string,
|
|
1283
|
+
verification_result: row["verification_result"] as string,
|
|
1284
|
+
duration: row["duration"] as string,
|
|
1285
|
+
completed_at: (row["completed_at"] as string) ?? null,
|
|
1286
|
+
blocker_discovered: (row["blocker_discovered"] as number) === 1,
|
|
1287
|
+
deviations: row["deviations"] as string,
|
|
1288
|
+
known_issues: row["known_issues"] as string,
|
|
1289
|
+
key_files: JSON.parse((row["key_files"] as string) || "[]"),
|
|
1290
|
+
key_decisions: JSON.parse((row["key_decisions"] as string) || "[]"),
|
|
1291
|
+
full_summary_md: row["full_summary_md"] as string,
|
|
1292
|
+
description: (row["description"] as string) ?? "",
|
|
1293
|
+
estimate: (row["estimate"] as string) ?? "",
|
|
1294
|
+
files: JSON.parse((row["files"] as string) || "[]"),
|
|
1295
|
+
verify: (row["verify"] as string) ?? "",
|
|
1296
|
+
inputs: JSON.parse((row["inputs"] as string) || "[]"),
|
|
1297
|
+
expected_output: JSON.parse((row["expected_output"] as string) || "[]"),
|
|
1298
|
+
observability_impact: (row["observability_impact"] as string) ?? "",
|
|
1299
|
+
sequence: (row["sequence"] as number) ?? 0,
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
export function getTask(milestoneId: string, sliceId: string, taskId: string): TaskRow | null {
|
|
1304
|
+
if (!currentDb) return null;
|
|
1305
|
+
const row = currentDb.prepare(
|
|
1306
|
+
"SELECT * FROM tasks WHERE milestone_id = :mid AND slice_id = :sid AND id = :tid",
|
|
1307
|
+
).get({ ":mid": milestoneId, ":sid": sliceId, ":tid": taskId });
|
|
1308
|
+
if (!row) return null;
|
|
1309
|
+
return rowToTask(row);
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
export function getSliceTasks(milestoneId: string, sliceId: string): TaskRow[] {
|
|
1313
|
+
if (!currentDb) return [];
|
|
1314
|
+
const rows = currentDb.prepare(
|
|
1315
|
+
"SELECT * FROM tasks WHERE milestone_id = :mid AND slice_id = :sid ORDER BY sequence, id",
|
|
1316
|
+
).all({ ":mid": milestoneId, ":sid": sliceId });
|
|
1317
|
+
return rows.map(rowToTask);
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
export function insertVerificationEvidence(e: {
|
|
1321
|
+
taskId: string;
|
|
1322
|
+
sliceId: string;
|
|
1323
|
+
milestoneId: string;
|
|
1324
|
+
command: string;
|
|
1325
|
+
exitCode: number;
|
|
1326
|
+
verdict: string;
|
|
1327
|
+
durationMs: number;
|
|
1328
|
+
}): void {
|
|
1329
|
+
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1330
|
+
currentDb.prepare(
|
|
1331
|
+
`INSERT INTO verification_evidence (task_id, slice_id, milestone_id, command, exit_code, verdict, duration_ms, created_at)
|
|
1332
|
+
VALUES (:task_id, :slice_id, :milestone_id, :command, :exit_code, :verdict, :duration_ms, :created_at)`,
|
|
1333
|
+
).run({
|
|
1334
|
+
":task_id": e.taskId,
|
|
1335
|
+
":slice_id": e.sliceId,
|
|
1336
|
+
":milestone_id": e.milestoneId,
|
|
1337
|
+
":command": e.command,
|
|
1338
|
+
":exit_code": e.exitCode,
|
|
1339
|
+
":verdict": e.verdict,
|
|
1340
|
+
":duration_ms": e.durationMs,
|
|
1341
|
+
":created_at": new Date().toISOString(),
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
export interface MilestoneRow {
|
|
1346
|
+
id: string;
|
|
1347
|
+
title: string;
|
|
1348
|
+
status: string;
|
|
1349
|
+
depends_on: string[];
|
|
1350
|
+
created_at: string;
|
|
1351
|
+
completed_at: string | null;
|
|
1352
|
+
vision: string;
|
|
1353
|
+
success_criteria: string[];
|
|
1354
|
+
key_risks: Array<{ risk: string; whyItMatters: string }>;
|
|
1355
|
+
proof_strategy: Array<{ riskOrUnknown: string; retireIn: string; whatWillBeProven: string }>;
|
|
1356
|
+
verification_contract: string;
|
|
1357
|
+
verification_integration: string;
|
|
1358
|
+
verification_operational: string;
|
|
1359
|
+
verification_uat: string;
|
|
1360
|
+
definition_of_done: string[];
|
|
1361
|
+
requirement_coverage: string;
|
|
1362
|
+
boundary_map_markdown: string;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
function rowToMilestone(row: Record<string, unknown>): MilestoneRow {
|
|
1366
|
+
return {
|
|
1367
|
+
id: row["id"] as string,
|
|
1368
|
+
title: row["title"] as string,
|
|
1369
|
+
status: row["status"] as string,
|
|
1370
|
+
depends_on: JSON.parse((row["depends_on"] as string) || "[]"),
|
|
1371
|
+
created_at: row["created_at"] as string,
|
|
1372
|
+
completed_at: (row["completed_at"] as string) ?? null,
|
|
1373
|
+
vision: (row["vision"] as string) ?? "",
|
|
1374
|
+
success_criteria: JSON.parse((row["success_criteria"] as string) || "[]"),
|
|
1375
|
+
key_risks: JSON.parse((row["key_risks"] as string) || "[]"),
|
|
1376
|
+
proof_strategy: JSON.parse((row["proof_strategy"] as string) || "[]"),
|
|
1377
|
+
verification_contract: (row["verification_contract"] as string) ?? "",
|
|
1378
|
+
verification_integration: (row["verification_integration"] as string) ?? "",
|
|
1379
|
+
verification_operational: (row["verification_operational"] as string) ?? "",
|
|
1380
|
+
verification_uat: (row["verification_uat"] as string) ?? "",
|
|
1381
|
+
definition_of_done: JSON.parse((row["definition_of_done"] as string) || "[]"),
|
|
1382
|
+
requirement_coverage: (row["requirement_coverage"] as string) ?? "",
|
|
1383
|
+
boundary_map_markdown: (row["boundary_map_markdown"] as string) ?? "",
|
|
1384
|
+
};
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
export interface ArtifactRow {
|
|
1388
|
+
path: string;
|
|
1389
|
+
artifact_type: string;
|
|
1390
|
+
milestone_id: string | null;
|
|
1391
|
+
slice_id: string | null;
|
|
1392
|
+
task_id: string | null;
|
|
1393
|
+
full_content: string;
|
|
1394
|
+
imported_at: string;
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
function rowToArtifact(row: Record<string, unknown>): ArtifactRow {
|
|
1398
|
+
return {
|
|
1399
|
+
path: row["path"] as string,
|
|
1400
|
+
artifact_type: row["artifact_type"] as string,
|
|
1401
|
+
milestone_id: (row["milestone_id"] as string) ?? null,
|
|
1402
|
+
slice_id: (row["slice_id"] as string) ?? null,
|
|
1403
|
+
task_id: (row["task_id"] as string) ?? null,
|
|
1404
|
+
full_content: row["full_content"] as string,
|
|
1405
|
+
imported_at: row["imported_at"] as string,
|
|
1406
|
+
};
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
export function getAllMilestones(): MilestoneRow[] {
|
|
1410
|
+
if (!currentDb) return [];
|
|
1411
|
+
const rows = currentDb.prepare("SELECT * FROM milestones ORDER BY id").all();
|
|
1412
|
+
return rows.map(rowToMilestone);
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
export function getMilestone(id: string): MilestoneRow | null {
|
|
1416
|
+
if (!currentDb) return null;
|
|
1417
|
+
const row = currentDb.prepare("SELECT * FROM milestones WHERE id = :id").get({ ":id": id });
|
|
1418
|
+
if (!row) return null;
|
|
1419
|
+
return rowToMilestone(row);
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
export function getActiveMilestoneFromDb(): MilestoneRow | null {
|
|
1423
|
+
if (!currentDb) return null;
|
|
1424
|
+
const row = currentDb.prepare(
|
|
1425
|
+
"SELECT * FROM milestones WHERE status NOT IN ('complete', 'parked') ORDER BY id LIMIT 1",
|
|
1426
|
+
).get();
|
|
1427
|
+
if (!row) return null;
|
|
1428
|
+
return rowToMilestone(row);
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
export function getActiveSliceFromDb(milestoneId: string): SliceRow | null {
|
|
1432
|
+
if (!currentDb) return null;
|
|
1433
|
+
const rows = currentDb.prepare(
|
|
1434
|
+
"SELECT * FROM slices WHERE milestone_id = :mid AND status NOT IN ('complete', 'done') ORDER BY sequence, id",
|
|
1435
|
+
).all({ ":mid": milestoneId });
|
|
1436
|
+
if (rows.length === 0) return null;
|
|
1437
|
+
|
|
1438
|
+
const completedRows = currentDb.prepare(
|
|
1439
|
+
"SELECT id FROM slices WHERE milestone_id = :mid AND status IN ('complete', 'done')",
|
|
1440
|
+
).all({ ":mid": milestoneId });
|
|
1441
|
+
const completedIds = new Set(completedRows.map((r) => r["id"] as string));
|
|
1442
|
+
|
|
1443
|
+
for (const row of rows) {
|
|
1444
|
+
const slice = rowToSlice(row);
|
|
1445
|
+
if (slice.depends.length === 0 || slice.depends.every((d) => completedIds.has(d))) {
|
|
1446
|
+
return slice;
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
return null;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
export function getActiveTaskFromDb(milestoneId: string, sliceId: string): TaskRow | null {
|
|
1453
|
+
if (!currentDb) return null;
|
|
1454
|
+
const row = currentDb.prepare(
|
|
1455
|
+
"SELECT * FROM tasks WHERE milestone_id = :mid AND slice_id = :sid AND status NOT IN ('complete', 'done') ORDER BY sequence, id LIMIT 1",
|
|
1456
|
+
).get({ ":mid": milestoneId, ":sid": sliceId });
|
|
1457
|
+
if (!row) return null;
|
|
1458
|
+
return rowToTask(row);
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
export function getMilestoneSlices(milestoneId: string): SliceRow[] {
|
|
1462
|
+
if (!currentDb) return [];
|
|
1463
|
+
const rows = currentDb.prepare("SELECT * FROM slices WHERE milestone_id = :mid ORDER BY sequence, id").all({ ":mid": milestoneId });
|
|
1464
|
+
return rows.map(rowToSlice);
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
export function getArtifact(path: string): ArtifactRow | null {
|
|
1468
|
+
if (!currentDb) return null;
|
|
1469
|
+
const row = currentDb.prepare("SELECT * FROM artifacts WHERE path = :path").get({ ":path": path });
|
|
1470
|
+
if (!row) return null;
|
|
1471
|
+
return rowToArtifact(row);
|
|
752
1472
|
}
|
|
753
1473
|
|
|
754
1474
|
// ─── Worktree DB Helpers ──────────────────────────────────────────────────
|
|
@@ -761,9 +1481,7 @@ export function copyWorktreeDb(srcDbPath: string, destDbPath: string): boolean {
|
|
|
761
1481
|
copyFileSync(srcDbPath, destDbPath);
|
|
762
1482
|
return true;
|
|
763
1483
|
} catch (err) {
|
|
764
|
-
process.stderr.write(
|
|
765
|
-
`gsd-db: failed to copy DB to worktree: ${(err as Error).message}\n`,
|
|
766
|
-
);
|
|
1484
|
+
process.stderr.write(`gsd-db: failed to copy DB to worktree: ${(err as Error).message}\n`);
|
|
767
1485
|
return false;
|
|
768
1486
|
}
|
|
769
1487
|
}
|
|
@@ -777,25 +1495,16 @@ export function reconcileWorktreeDb(
|
|
|
777
1495
|
artifacts: number;
|
|
778
1496
|
conflicts: string[];
|
|
779
1497
|
} {
|
|
780
|
-
const zero = {
|
|
781
|
-
decisions: 0,
|
|
782
|
-
requirements: 0,
|
|
783
|
-
artifacts: 0,
|
|
784
|
-
conflicts: [] as string[],
|
|
785
|
-
};
|
|
1498
|
+
const zero = { decisions: 0, requirements: 0, artifacts: 0, conflicts: [] as string[] };
|
|
786
1499
|
if (!existsSync(worktreeDbPath)) return zero;
|
|
787
1500
|
if (worktreeDbPath.includes("'")) {
|
|
788
|
-
process.stderr.write(
|
|
789
|
-
`gsd-db: worktree DB reconciliation failed: path contains unsafe characters\n`,
|
|
790
|
-
);
|
|
1501
|
+
process.stderr.write("gsd-db: worktree DB reconciliation failed: path contains unsafe characters\n");
|
|
791
1502
|
return zero;
|
|
792
1503
|
}
|
|
793
1504
|
if (!currentDb) {
|
|
794
1505
|
const opened = openDatabase(mainDbPath);
|
|
795
1506
|
if (!opened) {
|
|
796
|
-
process.stderr.write(
|
|
797
|
-
`gsd-db: worktree DB reconciliation failed: cannot open main DB\n`,
|
|
798
|
-
);
|
|
1507
|
+
process.stderr.write("gsd-db: worktree DB reconciliation failed: cannot open main DB\n");
|
|
799
1508
|
return zero;
|
|
800
1509
|
}
|
|
801
1510
|
}
|
|
@@ -804,106 +1513,184 @@ export function reconcileWorktreeDb(
|
|
|
804
1513
|
try {
|
|
805
1514
|
adapter.exec(`ATTACH DATABASE '${worktreeDbPath}' AS wt`);
|
|
806
1515
|
try {
|
|
807
|
-
// Check if attached wt database has the made_by column (legacy v3 worktrees won't)
|
|
808
1516
|
const wtInfo = adapter.prepare("PRAGMA wt.table_info('decisions')").all();
|
|
809
1517
|
const hasMadeBy = wtInfo.some((col) => col["name"] === "made_by");
|
|
810
1518
|
|
|
811
|
-
const decConf = adapter
|
|
812
|
-
.
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
.prepare(
|
|
824
|
-
`SELECT m.id FROM requirements m INNER JOIN wt.requirements w ON m.id = w.id WHERE m.description != w.description OR m.status != w.status OR m.notes != w.notes OR m.superseded_by IS NOT w.superseded_by`,
|
|
825
|
-
)
|
|
826
|
-
.all();
|
|
827
|
-
for (const row of reqConf)
|
|
828
|
-
conflicts.push(
|
|
829
|
-
`requirement ${(row as Record<string, unknown>)["id"]}: modified in both`,
|
|
830
|
-
);
|
|
1519
|
+
const decConf = adapter.prepare(
|
|
1520
|
+
`SELECT m.id FROM decisions m INNER JOIN wt.decisions w ON m.id = w.id WHERE m.decision != w.decision OR m.choice != w.choice OR m.rationale != w.rationale OR ${
|
|
1521
|
+
hasMadeBy ? "m.made_by != w.made_by" : "'agent' != 'agent'"
|
|
1522
|
+
} OR m.superseded_by IS NOT w.superseded_by`,
|
|
1523
|
+
).all();
|
|
1524
|
+
for (const row of decConf) conflicts.push(`decision ${(row as Record<string, unknown>)["id"]}: modified in both`);
|
|
1525
|
+
|
|
1526
|
+
const reqConf = adapter.prepare(
|
|
1527
|
+
`SELECT m.id FROM requirements m INNER JOIN wt.requirements w ON m.id = w.id WHERE m.description != w.description OR m.status != w.status OR m.notes != w.notes OR m.superseded_by IS NOT w.superseded_by`,
|
|
1528
|
+
).all();
|
|
1529
|
+
for (const row of reqConf) conflicts.push(`requirement ${(row as Record<string, unknown>)["id"]}: modified in both`);
|
|
1530
|
+
|
|
831
1531
|
const merged = { decisions: 0, requirements: 0, artifacts: 0 };
|
|
832
1532
|
adapter.exec("BEGIN");
|
|
833
1533
|
try {
|
|
834
|
-
const dR = adapter
|
|
835
|
-
.prepare(
|
|
836
|
-
`
|
|
1534
|
+
const dR = adapter.prepare(`
|
|
837
1535
|
INSERT OR REPLACE INTO decisions (
|
|
838
1536
|
id, when_context, scope, decision, choice, rationale, revisable, made_by, superseded_by
|
|
839
1537
|
)
|
|
840
|
-
SELECT
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
.run();
|
|
848
|
-
merged.decisions =
|
|
849
|
-
typeof dR === "object" && dR !== null
|
|
850
|
-
? ((dR as { changes?: number }).changes ?? 0)
|
|
851
|
-
: 0;
|
|
852
|
-
const rR = adapter
|
|
853
|
-
.prepare(
|
|
854
|
-
`
|
|
1538
|
+
SELECT id, when_context, scope, decision, choice, rationale, revisable, ${
|
|
1539
|
+
hasMadeBy ? "made_by" : "'agent'"
|
|
1540
|
+
}, superseded_by FROM wt.decisions
|
|
1541
|
+
`).run();
|
|
1542
|
+
merged.decisions = typeof dR === "object" && dR !== null ? ((dR as { changes?: number }).changes ?? 0) : 0;
|
|
1543
|
+
|
|
1544
|
+
const rR = adapter.prepare(`
|
|
855
1545
|
INSERT OR REPLACE INTO requirements (
|
|
856
1546
|
id, class, status, description, why, source, primary_owner,
|
|
857
1547
|
supporting_slices, validation, notes, full_content, superseded_by
|
|
858
1548
|
)
|
|
859
|
-
SELECT
|
|
860
|
-
|
|
861
|
-
supporting_slices, validation, notes, full_content, superseded_by
|
|
1549
|
+
SELECT id, class, status, description, why, source, primary_owner,
|
|
1550
|
+
supporting_slices, validation, notes, full_content, superseded_by
|
|
862
1551
|
FROM wt.requirements
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
typeof rR === "object" && rR !== null
|
|
868
|
-
? ((rR as { changes?: number }).changes ?? 0)
|
|
869
|
-
: 0;
|
|
870
|
-
const aR = adapter
|
|
871
|
-
.prepare(
|
|
872
|
-
`
|
|
1552
|
+
`).run();
|
|
1553
|
+
merged.requirements = typeof rR === "object" && rR !== null ? ((rR as { changes?: number }).changes ?? 0) : 0;
|
|
1554
|
+
|
|
1555
|
+
const aR = adapter.prepare(`
|
|
873
1556
|
INSERT OR REPLACE INTO artifacts (
|
|
874
1557
|
path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at
|
|
875
1558
|
)
|
|
876
|
-
SELECT
|
|
877
|
-
path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at
|
|
1559
|
+
SELECT path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at
|
|
878
1560
|
FROM wt.artifacts
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
merged.artifacts =
|
|
883
|
-
typeof aR === "object" && aR !== null
|
|
884
|
-
? ((aR as { changes?: number }).changes ?? 0)
|
|
885
|
-
: 0;
|
|
1561
|
+
`).run();
|
|
1562
|
+
merged.artifacts = typeof aR === "object" && aR !== null ? ((aR as { changes?: number }).changes ?? 0) : 0;
|
|
1563
|
+
|
|
886
1564
|
adapter.exec("COMMIT");
|
|
887
1565
|
} catch (txErr) {
|
|
888
|
-
try {
|
|
889
|
-
adapter.exec("ROLLBACK");
|
|
890
|
-
} catch {
|
|
891
|
-
/* best-effort */
|
|
892
|
-
}
|
|
1566
|
+
try { adapter.exec("ROLLBACK"); } catch { /* best effort */ }
|
|
893
1567
|
throw txErr;
|
|
894
1568
|
}
|
|
895
1569
|
return { ...merged, conflicts };
|
|
896
1570
|
} finally {
|
|
897
|
-
try {
|
|
898
|
-
adapter.exec("DETACH DATABASE wt");
|
|
899
|
-
} catch {
|
|
900
|
-
/* best-effort */
|
|
901
|
-
}
|
|
1571
|
+
try { adapter.exec("DETACH DATABASE wt"); } catch { /* best effort */ }
|
|
902
1572
|
}
|
|
903
1573
|
} catch (err) {
|
|
904
|
-
process.stderr.write(
|
|
905
|
-
`gsd-db: worktree DB reconciliation failed: ${(err as Error).message}\n`,
|
|
906
|
-
);
|
|
1574
|
+
process.stderr.write(`gsd-db: worktree DB reconciliation failed: ${(err as Error).message}\n`);
|
|
907
1575
|
return { ...zero, conflicts };
|
|
908
1576
|
}
|
|
909
1577
|
}
|
|
1578
|
+
|
|
1579
|
+
// ─── Replan & Assessment Helpers ──────────────────────────────────────────
|
|
1580
|
+
|
|
1581
|
+
export function insertReplanHistory(entry: {
|
|
1582
|
+
milestoneId: string;
|
|
1583
|
+
sliceId?: string | null;
|
|
1584
|
+
taskId?: string | null;
|
|
1585
|
+
summary: string;
|
|
1586
|
+
previousArtifactPath?: string | null;
|
|
1587
|
+
replacementArtifactPath?: string | null;
|
|
1588
|
+
}): void {
|
|
1589
|
+
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1590
|
+
currentDb.prepare(
|
|
1591
|
+
`INSERT INTO replan_history (milestone_id, slice_id, task_id, summary, previous_artifact_path, replacement_artifact_path, created_at)
|
|
1592
|
+
VALUES (:milestone_id, :slice_id, :task_id, :summary, :previous_artifact_path, :replacement_artifact_path, :created_at)`,
|
|
1593
|
+
).run({
|
|
1594
|
+
":milestone_id": entry.milestoneId,
|
|
1595
|
+
":slice_id": entry.sliceId ?? null,
|
|
1596
|
+
":task_id": entry.taskId ?? null,
|
|
1597
|
+
":summary": entry.summary,
|
|
1598
|
+
":previous_artifact_path": entry.previousArtifactPath ?? null,
|
|
1599
|
+
":replacement_artifact_path": entry.replacementArtifactPath ?? null,
|
|
1600
|
+
":created_at": new Date().toISOString(),
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
export function insertAssessment(entry: {
|
|
1605
|
+
path: string;
|
|
1606
|
+
milestoneId: string;
|
|
1607
|
+
sliceId?: string | null;
|
|
1608
|
+
taskId?: string | null;
|
|
1609
|
+
status: string;
|
|
1610
|
+
scope: string;
|
|
1611
|
+
fullContent: string;
|
|
1612
|
+
}): void {
|
|
1613
|
+
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1614
|
+
currentDb.prepare(
|
|
1615
|
+
`INSERT OR REPLACE INTO assessments (path, milestone_id, slice_id, task_id, status, scope, full_content, created_at)
|
|
1616
|
+
VALUES (:path, :milestone_id, :slice_id, :task_id, :status, :scope, :full_content, :created_at)`,
|
|
1617
|
+
).run({
|
|
1618
|
+
":path": entry.path,
|
|
1619
|
+
":milestone_id": entry.milestoneId,
|
|
1620
|
+
":slice_id": entry.sliceId ?? null,
|
|
1621
|
+
":task_id": entry.taskId ?? null,
|
|
1622
|
+
":status": entry.status,
|
|
1623
|
+
":scope": entry.scope,
|
|
1624
|
+
":full_content": entry.fullContent,
|
|
1625
|
+
":created_at": new Date().toISOString(),
|
|
1626
|
+
});
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
export function deleteTask(milestoneId: string, sliceId: string, taskId: string): void {
|
|
1630
|
+
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1631
|
+
// Must delete verification_evidence first (FK constraint)
|
|
1632
|
+
currentDb.prepare(
|
|
1633
|
+
`DELETE FROM verification_evidence WHERE milestone_id = :mid AND slice_id = :sid AND task_id = :tid`,
|
|
1634
|
+
).run({ ":mid": milestoneId, ":sid": sliceId, ":tid": taskId });
|
|
1635
|
+
currentDb.prepare(
|
|
1636
|
+
`DELETE FROM tasks WHERE milestone_id = :mid AND slice_id = :sid AND id = :tid`,
|
|
1637
|
+
).run({ ":mid": milestoneId, ":sid": sliceId, ":tid": taskId });
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
export function deleteSlice(milestoneId: string, sliceId: string): void {
|
|
1641
|
+
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1642
|
+
// Cascade-style manual deletion: evidence → tasks → slice
|
|
1643
|
+
currentDb.prepare(
|
|
1644
|
+
`DELETE FROM verification_evidence WHERE milestone_id = :mid AND slice_id = :sid`,
|
|
1645
|
+
).run({ ":mid": milestoneId, ":sid": sliceId });
|
|
1646
|
+
currentDb.prepare(
|
|
1647
|
+
`DELETE FROM tasks WHERE milestone_id = :mid AND slice_id = :sid`,
|
|
1648
|
+
).run({ ":mid": milestoneId, ":sid": sliceId });
|
|
1649
|
+
currentDb.prepare(
|
|
1650
|
+
`DELETE FROM slices WHERE milestone_id = :mid AND id = :sid`,
|
|
1651
|
+
).run({ ":mid": milestoneId, ":sid": sliceId });
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
export function updateSliceFields(milestoneId: string, sliceId: string, fields: {
|
|
1655
|
+
title?: string;
|
|
1656
|
+
risk?: string;
|
|
1657
|
+
depends?: string[];
|
|
1658
|
+
demo?: string;
|
|
1659
|
+
}): void {
|
|
1660
|
+
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1661
|
+
currentDb.prepare(
|
|
1662
|
+
`UPDATE slices SET
|
|
1663
|
+
title = COALESCE(:title, title),
|
|
1664
|
+
risk = COALESCE(:risk, risk),
|
|
1665
|
+
depends = COALESCE(:depends, depends),
|
|
1666
|
+
demo = COALESCE(:demo, demo)
|
|
1667
|
+
WHERE milestone_id = :milestone_id AND id = :id`,
|
|
1668
|
+
).run({
|
|
1669
|
+
":milestone_id": milestoneId,
|
|
1670
|
+
":id": sliceId,
|
|
1671
|
+
":title": fields.title ?? null,
|
|
1672
|
+
":risk": fields.risk ?? null,
|
|
1673
|
+
":depends": fields.depends ? JSON.stringify(fields.depends) : null,
|
|
1674
|
+
":demo": fields.demo ?? null,
|
|
1675
|
+
});
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
export function getReplanHistory(milestoneId: string, sliceId?: string): Array<Record<string, unknown>> {
|
|
1679
|
+
if (!currentDb) return [];
|
|
1680
|
+
if (sliceId) {
|
|
1681
|
+
return currentDb.prepare(
|
|
1682
|
+
`SELECT * FROM replan_history WHERE milestone_id = :mid AND slice_id = :sid ORDER BY created_at DESC`,
|
|
1683
|
+
).all({ ":mid": milestoneId, ":sid": sliceId });
|
|
1684
|
+
}
|
|
1685
|
+
return currentDb.prepare(
|
|
1686
|
+
`SELECT * FROM replan_history WHERE milestone_id = :mid ORDER BY created_at DESC`,
|
|
1687
|
+
).all({ ":mid": milestoneId });
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
export function getAssessment(path: string): Record<string, unknown> | null {
|
|
1691
|
+
if (!currentDb) return null;
|
|
1692
|
+
const row = currentDb.prepare(
|
|
1693
|
+
`SELECT * FROM assessments WHERE path = :path`,
|
|
1694
|
+
).get({ ":path": path });
|
|
1695
|
+
return row ?? null;
|
|
1696
|
+
}
|