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
|
@@ -0,0 +1,1161 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import * as os from 'node:os';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import {
|
|
5
|
+
openDatabase,
|
|
6
|
+
closeDatabase,
|
|
7
|
+
insertMilestone,
|
|
8
|
+
insertSlice,
|
|
9
|
+
insertTask,
|
|
10
|
+
insertArtifact,
|
|
11
|
+
getArtifact,
|
|
12
|
+
getAllMilestones,
|
|
13
|
+
getMilestoneSlices,
|
|
14
|
+
getSliceTasks,
|
|
15
|
+
updateSliceStatus,
|
|
16
|
+
_getAdapter,
|
|
17
|
+
} from '../gsd-db.ts';
|
|
18
|
+
import {
|
|
19
|
+
renderRoadmapCheckboxes,
|
|
20
|
+
renderPlanCheckboxes,
|
|
21
|
+
renderTaskSummary,
|
|
22
|
+
renderSliceSummary,
|
|
23
|
+
renderAllFromDb,
|
|
24
|
+
renderPlanFromDb,
|
|
25
|
+
renderTaskPlanFromDb,
|
|
26
|
+
detectStaleRenders,
|
|
27
|
+
repairStaleRenders,
|
|
28
|
+
} from '../markdown-renderer.ts';
|
|
29
|
+
import {
|
|
30
|
+
parseRoadmap,
|
|
31
|
+
parsePlan,
|
|
32
|
+
} from '../parsers-legacy.ts';
|
|
33
|
+
import {
|
|
34
|
+
parseSummary,
|
|
35
|
+
parseTaskPlanFile,
|
|
36
|
+
clearParseCache,
|
|
37
|
+
} from '../files.ts';
|
|
38
|
+
import { clearPathCache, _clearGsdRootCache } from '../paths.ts';
|
|
39
|
+
import { invalidateStateCache } from '../state.ts';
|
|
40
|
+
import { describe, test, beforeEach, afterEach } from 'node:test';
|
|
41
|
+
import assert from 'node:assert/strict';
|
|
42
|
+
|
|
43
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
44
|
+
// Helpers
|
|
45
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
46
|
+
|
|
47
|
+
function makeTmpDir(): string {
|
|
48
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-renderer-'));
|
|
49
|
+
fs.mkdirSync(path.join(dir, '.gsd'), { recursive: true });
|
|
50
|
+
return dir;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function cleanupDir(dir: string): void {
|
|
54
|
+
try {
|
|
55
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
56
|
+
} catch { /* swallow */ }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function clearAllCaches(): void {
|
|
60
|
+
clearParseCache();
|
|
61
|
+
clearPathCache();
|
|
62
|
+
_clearGsdRootCache();
|
|
63
|
+
invalidateStateCache();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Create on-disk directory structure for a milestone/slice/task tree
|
|
68
|
+
* so that path resolvers work correctly.
|
|
69
|
+
*/
|
|
70
|
+
function scaffoldDirs(tmpDir: string, mid: string, sliceIds: string[]): void {
|
|
71
|
+
const msDir = path.join(tmpDir, '.gsd', 'milestones', mid);
|
|
72
|
+
fs.mkdirSync(msDir, { recursive: true });
|
|
73
|
+
|
|
74
|
+
for (const sid of sliceIds) {
|
|
75
|
+
const sliceDir = path.join(msDir, 'slices', sid);
|
|
76
|
+
fs.mkdirSync(path.join(sliceDir, 'tasks'), { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ─── Fixture: Roadmap Template ────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
function makeRoadmapContent(slices: Array<{ id: string; title: string; done: boolean }>): string {
|
|
83
|
+
const lines: string[] = [];
|
|
84
|
+
lines.push('# M001 Roadmap');
|
|
85
|
+
lines.push('');
|
|
86
|
+
lines.push('**Vision:** Test milestone');
|
|
87
|
+
lines.push('');
|
|
88
|
+
lines.push('## Slices');
|
|
89
|
+
lines.push('');
|
|
90
|
+
for (const s of slices) {
|
|
91
|
+
const checkbox = s.done ? '[x]' : '[ ]';
|
|
92
|
+
lines.push(`- ${checkbox} **${s.id}: ${s.title}** \`risk:medium\` \`depends:[]\``);
|
|
93
|
+
}
|
|
94
|
+
lines.push('');
|
|
95
|
+
return lines.join('\n');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ─── Fixture: Plan Template ───────────────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
function makePlanContent(
|
|
101
|
+
sliceId: string,
|
|
102
|
+
tasks: Array<{ id: string; title: string; done: boolean }>,
|
|
103
|
+
): string {
|
|
104
|
+
const lines: string[] = [];
|
|
105
|
+
lines.push(`# ${sliceId}: Test Slice`);
|
|
106
|
+
lines.push('');
|
|
107
|
+
lines.push('**Goal:** Test slice goal');
|
|
108
|
+
lines.push('**Demo:** Test demo');
|
|
109
|
+
lines.push('');
|
|
110
|
+
lines.push('## Must-Haves');
|
|
111
|
+
lines.push('');
|
|
112
|
+
lines.push('- Everything works');
|
|
113
|
+
lines.push('');
|
|
114
|
+
lines.push('## Tasks');
|
|
115
|
+
lines.push('');
|
|
116
|
+
for (const t of tasks) {
|
|
117
|
+
const checkbox = t.done ? '[x]' : '[ ]';
|
|
118
|
+
lines.push(`- ${checkbox} **${t.id}: ${t.title}** \`est:1h\``);
|
|
119
|
+
}
|
|
120
|
+
lines.push('');
|
|
121
|
+
return lines.join('\n');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ─── Fixture: Task Summary Template ───────────────────────────────────────
|
|
125
|
+
|
|
126
|
+
function makeTaskSummaryContent(taskId: string): string {
|
|
127
|
+
return [
|
|
128
|
+
'---',
|
|
129
|
+
`id: ${taskId}`,
|
|
130
|
+
'parent: S01',
|
|
131
|
+
'milestone: M001',
|
|
132
|
+
'duration: 45m',
|
|
133
|
+
'verification_result: all-pass',
|
|
134
|
+
`completed_at: ${new Date().toISOString()}`,
|
|
135
|
+
'blocker_discovered: false',
|
|
136
|
+
'provides: []',
|
|
137
|
+
'requires: []',
|
|
138
|
+
'affects: []',
|
|
139
|
+
'key_files:',
|
|
140
|
+
' - src/test.ts',
|
|
141
|
+
'key_decisions: []',
|
|
142
|
+
'patterns_established: []',
|
|
143
|
+
'drill_down_paths: []',
|
|
144
|
+
'observability_surfaces: []',
|
|
145
|
+
'---',
|
|
146
|
+
'',
|
|
147
|
+
`# ${taskId}: Test Task Summary`,
|
|
148
|
+
'',
|
|
149
|
+
'**Implemented test functionality**',
|
|
150
|
+
'',
|
|
151
|
+
'## What Happened',
|
|
152
|
+
'',
|
|
153
|
+
'Built the test feature.',
|
|
154
|
+
'',
|
|
155
|
+
'## Deviations',
|
|
156
|
+
'',
|
|
157
|
+
'None.',
|
|
158
|
+
'',
|
|
159
|
+
'## Files Created/Modified',
|
|
160
|
+
'',
|
|
161
|
+
'- `src/test.ts` — main implementation',
|
|
162
|
+
'',
|
|
163
|
+
'## Verification Evidence',
|
|
164
|
+
'',
|
|
165
|
+
'| Command | Exit | Verdict | Duration |',
|
|
166
|
+
'|---------|------|---------|----------|',
|
|
167
|
+
'| `npm test` | 0 | ✅ pass | 2.1s |',
|
|
168
|
+
'',
|
|
169
|
+
].join('\n');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
173
|
+
// DB Accessor Tests
|
|
174
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
175
|
+
|
|
176
|
+
test('── markdown-renderer: DB accessor basics ──', () => {
|
|
177
|
+
openDatabase(':memory:');
|
|
178
|
+
|
|
179
|
+
// getAllMilestones — empty
|
|
180
|
+
const empty = getAllMilestones();
|
|
181
|
+
assert.deepStrictEqual(empty.length, 0, 'getAllMilestones returns empty when no milestones');
|
|
182
|
+
|
|
183
|
+
// Insert and retrieve
|
|
184
|
+
insertMilestone({ id: 'M001', title: 'Test MS', status: 'active' });
|
|
185
|
+
insertMilestone({ id: 'M002', title: 'Second MS', status: 'active' });
|
|
186
|
+
|
|
187
|
+
const all = getAllMilestones();
|
|
188
|
+
assert.deepStrictEqual(all.length, 2, 'getAllMilestones returns 2 milestones');
|
|
189
|
+
assert.deepStrictEqual(all[0].id, 'M001', 'first milestone is M001');
|
|
190
|
+
assert.deepStrictEqual(all[1].id, 'M002', 'second milestone is M002');
|
|
191
|
+
assert.deepStrictEqual(all[0].title, 'Test MS', 'milestone title correct');
|
|
192
|
+
assert.deepStrictEqual(all[0].status, 'active', 'milestone status correct');
|
|
193
|
+
|
|
194
|
+
// getMilestoneSlices — empty
|
|
195
|
+
const noSlices = getMilestoneSlices('M001');
|
|
196
|
+
assert.deepStrictEqual(noSlices.length, 0, 'getMilestoneSlices returns empty when no slices');
|
|
197
|
+
|
|
198
|
+
// Insert slices and retrieve
|
|
199
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice 1', status: 'complete' });
|
|
200
|
+
insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Slice 2', status: 'pending' });
|
|
201
|
+
insertSlice({ id: 'S01', milestoneId: 'M002', title: 'M2 Slice', status: 'pending' });
|
|
202
|
+
|
|
203
|
+
const m1Slices = getMilestoneSlices('M001');
|
|
204
|
+
assert.deepStrictEqual(m1Slices.length, 2, 'M001 has 2 slices');
|
|
205
|
+
assert.deepStrictEqual(m1Slices[0].id, 'S01', 'first slice is S01');
|
|
206
|
+
assert.deepStrictEqual(m1Slices[0].status, 'complete', 'S01 status is complete');
|
|
207
|
+
assert.deepStrictEqual(m1Slices[1].id, 'S02', 'second slice is S02');
|
|
208
|
+
assert.deepStrictEqual(m1Slices[1].status, 'pending', 'S02 status is pending');
|
|
209
|
+
|
|
210
|
+
const m2Slices = getMilestoneSlices('M002');
|
|
211
|
+
assert.deepStrictEqual(m2Slices.length, 1, 'M002 has 1 slice');
|
|
212
|
+
|
|
213
|
+
closeDatabase();
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test('── markdown-renderer: getArtifact accessor ──', () => {
|
|
217
|
+
openDatabase(':memory:');
|
|
218
|
+
|
|
219
|
+
// Not found
|
|
220
|
+
const missing = getArtifact('nonexistent/path');
|
|
221
|
+
assert.deepStrictEqual(missing, null, 'getArtifact returns null for missing path');
|
|
222
|
+
|
|
223
|
+
// Insert and retrieve
|
|
224
|
+
insertArtifact({
|
|
225
|
+
path: 'milestones/M001/M001-ROADMAP.md',
|
|
226
|
+
artifact_type: 'ROADMAP',
|
|
227
|
+
milestone_id: 'M001',
|
|
228
|
+
slice_id: null,
|
|
229
|
+
task_id: null,
|
|
230
|
+
full_content: '# Roadmap content',
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
const found = getArtifact('milestones/M001/M001-ROADMAP.md');
|
|
234
|
+
assert.ok(found !== null, 'getArtifact returns non-null for existing path');
|
|
235
|
+
assert.deepStrictEqual(found!.artifact_type, 'ROADMAP', 'artifact type correct');
|
|
236
|
+
assert.deepStrictEqual(found!.milestone_id, 'M001', 'milestone_id correct');
|
|
237
|
+
assert.deepStrictEqual(found!.full_content, '# Roadmap content', 'content correct');
|
|
238
|
+
|
|
239
|
+
closeDatabase();
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
243
|
+
// Roadmap Checkbox Round-Trip
|
|
244
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
245
|
+
|
|
246
|
+
test('── markdown-renderer: renderRoadmapCheckboxes round-trip ──', async () => {
|
|
247
|
+
const tmpDir = makeTmpDir();
|
|
248
|
+
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
249
|
+
openDatabase(dbPath);
|
|
250
|
+
clearAllCaches();
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
scaffoldDirs(tmpDir, 'M001', ['S01', 'S02']);
|
|
254
|
+
|
|
255
|
+
// Seed DB with milestone and slices
|
|
256
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
257
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Core setup', status: 'complete' });
|
|
258
|
+
insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Rendering', status: 'pending' });
|
|
259
|
+
|
|
260
|
+
// Write a roadmap file on disk with BOTH slices unchecked
|
|
261
|
+
const roadmapContent = makeRoadmapContent([
|
|
262
|
+
{ id: 'S01', title: 'Core setup', done: false },
|
|
263
|
+
{ id: 'S02', title: 'Rendering', done: false },
|
|
264
|
+
]);
|
|
265
|
+
const roadmapPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'M001-ROADMAP.md');
|
|
266
|
+
fs.writeFileSync(roadmapPath, roadmapContent);
|
|
267
|
+
clearAllCaches();
|
|
268
|
+
|
|
269
|
+
// Render — should set S01 [x] and leave S02 [ ]
|
|
270
|
+
const ok = await renderRoadmapCheckboxes(tmpDir, 'M001');
|
|
271
|
+
assert.ok(ok, 'renderRoadmapCheckboxes returns true');
|
|
272
|
+
|
|
273
|
+
// Read rendered file and parse
|
|
274
|
+
const rendered = fs.readFileSync(roadmapPath, 'utf-8');
|
|
275
|
+
clearAllCaches();
|
|
276
|
+
const parsed = parseRoadmap(rendered);
|
|
277
|
+
|
|
278
|
+
assert.deepStrictEqual(parsed.slices.length, 2, 'roadmap has 2 slices after render');
|
|
279
|
+
|
|
280
|
+
const s01 = parsed.slices.find(s => s.id === 'S01');
|
|
281
|
+
const s02 = parsed.slices.find(s => s.id === 'S02');
|
|
282
|
+
assert.ok(!!s01, 'S01 found in parsed roadmap');
|
|
283
|
+
assert.ok(!!s02, 'S02 found in parsed roadmap');
|
|
284
|
+
assert.ok(s01!.done, 'S01 is checked (done) after render');
|
|
285
|
+
assert.ok(!s02!.done, 'S02 is unchecked (pending) after render');
|
|
286
|
+
|
|
287
|
+
// Verify artifact stored in DB
|
|
288
|
+
const artifact = getArtifact('milestones/M001/M001-ROADMAP.md');
|
|
289
|
+
assert.ok(artifact !== null, 'roadmap artifact stored in DB after render');
|
|
290
|
+
assert.ok(artifact!.full_content.includes('[x] **S01:'), 'DB artifact has S01 checked');
|
|
291
|
+
assert.ok(artifact!.full_content.includes('[ ] **S02:'), 'DB artifact has S02 unchecked');
|
|
292
|
+
} finally {
|
|
293
|
+
closeDatabase();
|
|
294
|
+
cleanupDir(tmpDir);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
test('── markdown-renderer: renderRoadmapCheckboxes bidirectional ──', async () => {
|
|
299
|
+
const tmpDir = makeTmpDir();
|
|
300
|
+
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
301
|
+
openDatabase(dbPath);
|
|
302
|
+
clearAllCaches();
|
|
303
|
+
|
|
304
|
+
try {
|
|
305
|
+
scaffoldDirs(tmpDir, 'M001', ['S01', 'S02']);
|
|
306
|
+
|
|
307
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
308
|
+
// S01 is PENDING in DB, but checked on disk — should be unchecked
|
|
309
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Core setup', status: 'pending' });
|
|
310
|
+
insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Rendering', status: 'complete' });
|
|
311
|
+
|
|
312
|
+
// Write roadmap with S01 checked and S02 unchecked (opposite of DB state)
|
|
313
|
+
const roadmapContent = makeRoadmapContent([
|
|
314
|
+
{ id: 'S01', title: 'Core setup', done: true },
|
|
315
|
+
{ id: 'S02', title: 'Rendering', done: false },
|
|
316
|
+
]);
|
|
317
|
+
const roadmapPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'M001-ROADMAP.md');
|
|
318
|
+
fs.writeFileSync(roadmapPath, roadmapContent);
|
|
319
|
+
clearAllCaches();
|
|
320
|
+
|
|
321
|
+
const ok = await renderRoadmapCheckboxes(tmpDir, 'M001');
|
|
322
|
+
assert.ok(ok, 'bidirectional render returns true');
|
|
323
|
+
|
|
324
|
+
const rendered = fs.readFileSync(roadmapPath, 'utf-8');
|
|
325
|
+
clearAllCaches();
|
|
326
|
+
const parsed = parseRoadmap(rendered);
|
|
327
|
+
|
|
328
|
+
const s01 = parsed.slices.find(s => s.id === 'S01');
|
|
329
|
+
const s02 = parsed.slices.find(s => s.id === 'S02');
|
|
330
|
+
assert.ok(!s01!.done, 'S01 unchecked (DB says pending, was checked on disk)');
|
|
331
|
+
assert.ok(s02!.done, 'S02 checked (DB says complete, was unchecked on disk)');
|
|
332
|
+
} finally {
|
|
333
|
+
closeDatabase();
|
|
334
|
+
cleanupDir(tmpDir);
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
339
|
+
// Plan Checkbox Round-Trip
|
|
340
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
341
|
+
|
|
342
|
+
test('── markdown-renderer: renderPlanCheckboxes round-trip ──', async () => {
|
|
343
|
+
const tmpDir = makeTmpDir();
|
|
344
|
+
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
345
|
+
openDatabase(dbPath);
|
|
346
|
+
clearAllCaches();
|
|
347
|
+
|
|
348
|
+
try {
|
|
349
|
+
scaffoldDirs(tmpDir, 'M001', ['S01']);
|
|
350
|
+
|
|
351
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
352
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'pending' });
|
|
353
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'First task', status: 'done' });
|
|
354
|
+
insertTask({ id: 'T02', sliceId: 'S01', milestoneId: 'M001', title: 'Second task', status: 'done' });
|
|
355
|
+
insertTask({ id: 'T03', sliceId: 'S01', milestoneId: 'M001', title: 'Third task', status: 'pending' });
|
|
356
|
+
|
|
357
|
+
// Write plan with all tasks unchecked
|
|
358
|
+
const planContent = makePlanContent('S01', [
|
|
359
|
+
{ id: 'T01', title: 'First task', done: false },
|
|
360
|
+
{ id: 'T02', title: 'Second task', done: false },
|
|
361
|
+
{ id: 'T03', title: 'Third task', done: false },
|
|
362
|
+
]);
|
|
363
|
+
const planPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-PLAN.md');
|
|
364
|
+
fs.writeFileSync(planPath, planContent);
|
|
365
|
+
clearAllCaches();
|
|
366
|
+
|
|
367
|
+
const ok = await renderPlanCheckboxes(tmpDir, 'M001', 'S01');
|
|
368
|
+
assert.ok(ok, 'renderPlanCheckboxes returns true');
|
|
369
|
+
|
|
370
|
+
const rendered = fs.readFileSync(planPath, 'utf-8');
|
|
371
|
+
clearAllCaches();
|
|
372
|
+
const parsed = parsePlan(rendered);
|
|
373
|
+
|
|
374
|
+
assert.deepStrictEqual(parsed.tasks.length, 3, 'plan has 3 tasks after render');
|
|
375
|
+
|
|
376
|
+
const t01 = parsed.tasks.find(t => t.id === 'T01');
|
|
377
|
+
const t02 = parsed.tasks.find(t => t.id === 'T02');
|
|
378
|
+
const t03 = parsed.tasks.find(t => t.id === 'T03');
|
|
379
|
+
assert.ok(t01!.done, 'T01 checked (done in DB)');
|
|
380
|
+
assert.ok(t02!.done, 'T02 checked (done in DB)');
|
|
381
|
+
assert.ok(!t03!.done, 'T03 unchecked (pending in DB)');
|
|
382
|
+
} finally {
|
|
383
|
+
closeDatabase();
|
|
384
|
+
cleanupDir(tmpDir);
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
test('── markdown-renderer: renderPlanCheckboxes bidirectional ──', async () => {
|
|
389
|
+
const tmpDir = makeTmpDir();
|
|
390
|
+
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
391
|
+
openDatabase(dbPath);
|
|
392
|
+
clearAllCaches();
|
|
393
|
+
|
|
394
|
+
try {
|
|
395
|
+
scaffoldDirs(tmpDir, 'M001', ['S01']);
|
|
396
|
+
|
|
397
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
398
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'pending' });
|
|
399
|
+
// T01 pending in DB but checked on disk
|
|
400
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'First task', status: 'pending' });
|
|
401
|
+
insertTask({ id: 'T02', sliceId: 'S01', milestoneId: 'M001', title: 'Second task', status: 'done' });
|
|
402
|
+
|
|
403
|
+
const planContent = makePlanContent('S01', [
|
|
404
|
+
{ id: 'T01', title: 'First task', done: true }, // checked but DB says pending
|
|
405
|
+
{ id: 'T02', title: 'Second task', done: false }, // unchecked but DB says done
|
|
406
|
+
]);
|
|
407
|
+
const planPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-PLAN.md');
|
|
408
|
+
fs.writeFileSync(planPath, planContent);
|
|
409
|
+
clearAllCaches();
|
|
410
|
+
|
|
411
|
+
const ok = await renderPlanCheckboxes(tmpDir, 'M001', 'S01');
|
|
412
|
+
assert.ok(ok, 'bidirectional plan render returns true');
|
|
413
|
+
|
|
414
|
+
const rendered = fs.readFileSync(planPath, 'utf-8');
|
|
415
|
+
clearAllCaches();
|
|
416
|
+
const parsed = parsePlan(rendered);
|
|
417
|
+
|
|
418
|
+
const t01 = parsed.tasks.find(t => t.id === 'T01');
|
|
419
|
+
const t02 = parsed.tasks.find(t => t.id === 'T02');
|
|
420
|
+
assert.ok(!t01!.done, 'T01 unchecked (DB says pending, was checked)');
|
|
421
|
+
assert.ok(t02!.done, 'T02 checked (DB says done, was unchecked)');
|
|
422
|
+
} finally {
|
|
423
|
+
closeDatabase();
|
|
424
|
+
cleanupDir(tmpDir);
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
test('── markdown-renderer: renderPlanFromDb creates parse-compatible slice plan + task plan files ──', async () => {
|
|
429
|
+
const tmpDir = makeTmpDir();
|
|
430
|
+
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
431
|
+
openDatabase(dbPath);
|
|
432
|
+
clearAllCaches();
|
|
433
|
+
|
|
434
|
+
try {
|
|
435
|
+
scaffoldDirs(tmpDir, 'M001', ['S02']);
|
|
436
|
+
|
|
437
|
+
insertMilestone({ id: 'M001', title: 'Milestone', status: 'active' });
|
|
438
|
+
insertSlice({
|
|
439
|
+
id: 'S02',
|
|
440
|
+
milestoneId: 'M001',
|
|
441
|
+
title: 'DB-backed planning',
|
|
442
|
+
status: 'pending',
|
|
443
|
+
demo: 'Rendered plans exist on disk.',
|
|
444
|
+
planning: {
|
|
445
|
+
goal: 'Render slice plans from DB state.',
|
|
446
|
+
successCriteria: '- Slice plan stays parse-compatible\n- Task plan files are regenerated',
|
|
447
|
+
proofLevel: 'integration',
|
|
448
|
+
integrationClosure: 'Wires DB planning rows to markdown artifacts.',
|
|
449
|
+
observabilityImpact: '- Run renderer contract tests\n- Inspect stale-render diagnostics on mismatch',
|
|
450
|
+
},
|
|
451
|
+
});
|
|
452
|
+
insertTask({
|
|
453
|
+
id: 'T01',
|
|
454
|
+
sliceId: 'S02',
|
|
455
|
+
milestoneId: 'M001',
|
|
456
|
+
title: 'Render slice plan',
|
|
457
|
+
status: 'pending',
|
|
458
|
+
planning: {
|
|
459
|
+
description: 'Implement the DB-backed slice plan renderer.',
|
|
460
|
+
estimate: '45m',
|
|
461
|
+
files: ['src/resources/extensions/gsd/markdown-renderer.ts'],
|
|
462
|
+
verify: 'node --test markdown-renderer.test.ts',
|
|
463
|
+
inputs: ['src/resources/extensions/gsd/markdown-renderer.ts'],
|
|
464
|
+
expectedOutput: ['src/resources/extensions/gsd/tests/markdown-renderer.test.ts'],
|
|
465
|
+
observabilityImpact: 'Renderer tests cover stale render failure paths.',
|
|
466
|
+
},
|
|
467
|
+
});
|
|
468
|
+
insertTask({
|
|
469
|
+
id: 'T02',
|
|
470
|
+
sliceId: 'S02',
|
|
471
|
+
milestoneId: 'M001',
|
|
472
|
+
title: 'Render task plan',
|
|
473
|
+
status: 'pending',
|
|
474
|
+
planning: {
|
|
475
|
+
description: 'Emit the task plan file with conservative frontmatter.',
|
|
476
|
+
estimate: '30m',
|
|
477
|
+
files: ['src/resources/extensions/gsd/files.ts'],
|
|
478
|
+
verify: 'node --test auto-recovery.test.ts',
|
|
479
|
+
inputs: ['src/resources/extensions/gsd/files.ts'],
|
|
480
|
+
expectedOutput: ['src/resources/extensions/gsd/tests/auto-recovery.test.ts'],
|
|
481
|
+
observabilityImpact: 'Missing task-plan files fail recovery verification.',
|
|
482
|
+
},
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
const rendered = await renderPlanFromDb(tmpDir, 'M001', 'S02');
|
|
486
|
+
assert.ok(fs.existsSync(rendered.planPath), 'slice plan written to disk');
|
|
487
|
+
assert.strictEqual(rendered.taskPlanPaths.length, 2, 'task plan paths returned for each task');
|
|
488
|
+
assert.ok(rendered.taskPlanPaths.every((p) => fs.existsSync(p)), 'all task plan files written to disk');
|
|
489
|
+
|
|
490
|
+
const planContent = fs.readFileSync(rendered.planPath, 'utf-8');
|
|
491
|
+
clearAllCaches();
|
|
492
|
+
const parsedPlan = parsePlan(planContent);
|
|
493
|
+
assert.strictEqual(parsedPlan.id, 'S02', 'rendered slice plan parses with correct slice id');
|
|
494
|
+
assert.strictEqual(parsedPlan.goal, 'Render slice plans from DB state.', 'rendered slice plan preserves goal');
|
|
495
|
+
assert.strictEqual(parsedPlan.demo, 'Rendered plans exist on disk.', 'rendered slice plan preserves demo');
|
|
496
|
+
assert.strictEqual(parsedPlan.mustHaves.length, 2, 'rendered slice plan exposes must-haves');
|
|
497
|
+
assert.strictEqual(parsedPlan.tasks.length, 2, 'rendered slice plan exposes all tasks');
|
|
498
|
+
assert.strictEqual(parsedPlan.tasks[0].id, 'T01', 'first task parses correctly');
|
|
499
|
+
assert.ok(parsedPlan.tasks[0].description.includes('DB-backed slice plan renderer'), 'task description preserved in slice plan');
|
|
500
|
+
assert.strictEqual(parsedPlan.tasks[0].files?.[0], 'src/resources/extensions/gsd/markdown-renderer.ts', 'files list preserved in slice plan');
|
|
501
|
+
assert.strictEqual(parsedPlan.tasks[0].verify, 'node --test markdown-renderer.test.ts', 'verify line preserved in slice plan');
|
|
502
|
+
|
|
503
|
+
const planArtifact = getArtifact('milestones/M001/slices/S02/S02-PLAN.md');
|
|
504
|
+
assert.ok(planArtifact !== null, 'slice plan artifact stored in DB');
|
|
505
|
+
assert.ok(planArtifact!.full_content.includes('## Tasks'), 'stored plan artifact contains task section');
|
|
506
|
+
|
|
507
|
+
const taskPlanPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S02', 'tasks', 'T01-PLAN.md');
|
|
508
|
+
const taskPlanContent = fs.readFileSync(taskPlanPath, 'utf-8');
|
|
509
|
+
const taskPlanFile = parseTaskPlanFile(taskPlanContent);
|
|
510
|
+
assert.strictEqual(taskPlanFile.frontmatter.estimated_steps, 1, 'task plan frontmatter exposes estimated_steps');
|
|
511
|
+
assert.strictEqual(taskPlanFile.frontmatter.estimated_files, 1, 'task plan frontmatter exposes estimated_files');
|
|
512
|
+
assert.strictEqual(taskPlanFile.frontmatter.skills_used.length, 0, 'task plan frontmatter uses conservative empty skills list');
|
|
513
|
+
assert.match(taskPlanContent, /^# T01: Render slice plan/m, 'task plan renders task heading');
|
|
514
|
+
assert.match(taskPlanContent, /^## Inputs$/m, 'task plan renders Inputs section');
|
|
515
|
+
assert.match(taskPlanContent, /^## Expected Output$/m, 'task plan renders Expected Output section');
|
|
516
|
+
assert.match(taskPlanContent, /^## Verification$/m, 'task plan renders Verification section');
|
|
517
|
+
|
|
518
|
+
const taskArtifact = getArtifact('milestones/M001/slices/S02/tasks/T01-PLAN.md');
|
|
519
|
+
assert.ok(taskArtifact !== null, 'task plan artifact stored in DB');
|
|
520
|
+
assert.ok(taskArtifact!.full_content.includes('skills_used: []'), 'stored task plan artifact preserves conservative skills_used');
|
|
521
|
+
} finally {
|
|
522
|
+
closeDatabase();
|
|
523
|
+
cleanupDir(tmpDir);
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
test('── markdown-renderer: renderTaskPlanFromDb throws for missing task ──', async () => {
|
|
528
|
+
const tmpDir = makeTmpDir();
|
|
529
|
+
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
530
|
+
openDatabase(dbPath);
|
|
531
|
+
clearAllCaches();
|
|
532
|
+
|
|
533
|
+
try {
|
|
534
|
+
scaffoldDirs(tmpDir, 'M001', ['S02']);
|
|
535
|
+
insertMilestone({ id: 'M001', title: 'Milestone', status: 'active' });
|
|
536
|
+
insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Slice', status: 'pending' });
|
|
537
|
+
|
|
538
|
+
let threw = false;
|
|
539
|
+
try {
|
|
540
|
+
await renderTaskPlanFromDb(tmpDir, 'M001', 'S02', 'T99');
|
|
541
|
+
} catch (error) {
|
|
542
|
+
threw = true;
|
|
543
|
+
assert.match(String((error as Error).message), /task M001\/S02\/T99 not found/, 'renderTaskPlanFromDb should fail clearly when task row is missing');
|
|
544
|
+
}
|
|
545
|
+
assert.ok(threw, 'renderTaskPlanFromDb throws when the task row is missing');
|
|
546
|
+
} finally {
|
|
547
|
+
closeDatabase();
|
|
548
|
+
cleanupDir(tmpDir);
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
554
|
+
// Task Summary Rendering
|
|
555
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
556
|
+
|
|
557
|
+
test('── markdown-renderer: renderTaskSummary round-trip ──', async () => {
|
|
558
|
+
const tmpDir = makeTmpDir();
|
|
559
|
+
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
560
|
+
openDatabase(dbPath);
|
|
561
|
+
clearAllCaches();
|
|
562
|
+
|
|
563
|
+
try {
|
|
564
|
+
scaffoldDirs(tmpDir, 'M001', ['S01']);
|
|
565
|
+
|
|
566
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
567
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'pending' });
|
|
568
|
+
|
|
569
|
+
const summaryContent = makeTaskSummaryContent('T01');
|
|
570
|
+
insertTask({
|
|
571
|
+
id: 'T01',
|
|
572
|
+
sliceId: 'S01',
|
|
573
|
+
milestoneId: 'M001',
|
|
574
|
+
title: 'Test Task',
|
|
575
|
+
status: 'done',
|
|
576
|
+
fullSummaryMd: summaryContent,
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
const ok = await renderTaskSummary(tmpDir, 'M001', 'S01', 'T01');
|
|
580
|
+
assert.ok(ok, 'renderTaskSummary returns true');
|
|
581
|
+
|
|
582
|
+
// Verify file exists on disk
|
|
583
|
+
const summaryPath = path.join(
|
|
584
|
+
tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'tasks', 'T01-SUMMARY.md',
|
|
585
|
+
);
|
|
586
|
+
assert.ok(fs.existsSync(summaryPath), 'T01-SUMMARY.md written to disk');
|
|
587
|
+
|
|
588
|
+
// Parse and verify
|
|
589
|
+
const rendered = fs.readFileSync(summaryPath, 'utf-8');
|
|
590
|
+
clearAllCaches();
|
|
591
|
+
const parsed = parseSummary(rendered);
|
|
592
|
+
assert.deepStrictEqual(parsed.frontmatter.id, 'T01', 'parsed summary has correct id');
|
|
593
|
+
assert.deepStrictEqual(parsed.frontmatter.parent, 'S01', 'parsed summary has correct parent');
|
|
594
|
+
assert.deepStrictEqual(parsed.frontmatter.milestone, 'M001', 'parsed summary has correct milestone');
|
|
595
|
+
assert.deepStrictEqual(parsed.frontmatter.duration, '45m', 'parsed summary has correct duration');
|
|
596
|
+
assert.ok(parsed.title.includes('T01'), 'parsed summary title contains task ID');
|
|
597
|
+
assert.ok(parsed.whatHappened.includes('Built the test feature'), 'whatHappened content preserved');
|
|
598
|
+
} finally {
|
|
599
|
+
closeDatabase();
|
|
600
|
+
cleanupDir(tmpDir);
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
test('── markdown-renderer: renderTaskSummary skips empty ──', async () => {
|
|
605
|
+
const tmpDir = makeTmpDir();
|
|
606
|
+
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
607
|
+
openDatabase(dbPath);
|
|
608
|
+
clearAllCaches();
|
|
609
|
+
|
|
610
|
+
try {
|
|
611
|
+
scaffoldDirs(tmpDir, 'M001', ['S01']);
|
|
612
|
+
|
|
613
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
614
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'pending' });
|
|
615
|
+
insertTask({
|
|
616
|
+
id: 'T01',
|
|
617
|
+
sliceId: 'S01',
|
|
618
|
+
milestoneId: 'M001',
|
|
619
|
+
title: 'Task without summary',
|
|
620
|
+
status: 'pending',
|
|
621
|
+
fullSummaryMd: '', // empty summary
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
const ok = await renderTaskSummary(tmpDir, 'M001', 'S01', 'T01');
|
|
625
|
+
assert.ok(!ok, 'renderTaskSummary returns false for empty summary');
|
|
626
|
+
} finally {
|
|
627
|
+
closeDatabase();
|
|
628
|
+
cleanupDir(tmpDir);
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
633
|
+
// Slice Summary Rendering
|
|
634
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
635
|
+
|
|
636
|
+
test('── markdown-renderer: renderSliceSummary round-trip ──', async () => {
|
|
637
|
+
const tmpDir = makeTmpDir();
|
|
638
|
+
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
639
|
+
openDatabase(dbPath);
|
|
640
|
+
clearAllCaches();
|
|
641
|
+
|
|
642
|
+
try {
|
|
643
|
+
scaffoldDirs(tmpDir, 'M001', ['S01']);
|
|
644
|
+
|
|
645
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
646
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'complete' });
|
|
647
|
+
|
|
648
|
+
// Update slice with summary and UAT content
|
|
649
|
+
// Since insertSlice uses INSERT OR IGNORE, we need to set the content via raw adapter
|
|
650
|
+
const db = await import('../gsd-db.ts');
|
|
651
|
+
const adapter = db._getAdapter()!;
|
|
652
|
+
adapter.prepare(
|
|
653
|
+
`UPDATE slices SET full_summary_md = :sm, full_uat_md = :um WHERE milestone_id = 'M001' AND id = 'S01'`,
|
|
654
|
+
).run({
|
|
655
|
+
':sm': '---\nid: S01\nparent: M001\nmilestone: M001\nduration: 2h\nverification_result: all-pass\ncompleted_at: 2025-01-01\nblocker_discovered: false\nprovides: []\nrequires: []\naffects: []\nkey_files:\n - src/index.ts\nkey_decisions: []\npatterns_established: []\ndrill_down_paths: []\nobservability_surfaces: []\n---\n\n# S01: Test Slice Summary\n\n**Completed core functionality**\n\n## What Happened\n\nBuilt the slice.\n\n## Deviations\n\nNone.\n',
|
|
656
|
+
':um': '# S01 UAT\n\n## UAT Type\n\n- UAT mode: artifact-driven\n\n## Checks\n\n- All tests pass\n',
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
const ok = await renderSliceSummary(tmpDir, 'M001', 'S01');
|
|
660
|
+
assert.ok(ok, 'renderSliceSummary returns true');
|
|
661
|
+
|
|
662
|
+
// Verify SUMMARY file
|
|
663
|
+
const summaryPath = path.join(
|
|
664
|
+
tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-SUMMARY.md',
|
|
665
|
+
);
|
|
666
|
+
assert.ok(fs.existsSync(summaryPath), 'S01-SUMMARY.md written to disk');
|
|
667
|
+
|
|
668
|
+
const summaryContent = fs.readFileSync(summaryPath, 'utf-8');
|
|
669
|
+
assert.ok(summaryContent.includes('Test Slice Summary'), 'summary content correct');
|
|
670
|
+
|
|
671
|
+
// Verify UAT file
|
|
672
|
+
const uatPath = path.join(
|
|
673
|
+
tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-UAT.md',
|
|
674
|
+
);
|
|
675
|
+
assert.ok(fs.existsSync(uatPath), 'S01-UAT.md written to disk');
|
|
676
|
+
|
|
677
|
+
const uatContent = fs.readFileSync(uatPath, 'utf-8');
|
|
678
|
+
assert.ok(uatContent.includes('artifact-driven'), 'UAT content correct');
|
|
679
|
+
} finally {
|
|
680
|
+
closeDatabase();
|
|
681
|
+
cleanupDir(tmpDir);
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
686
|
+
// renderAllFromDb
|
|
687
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
688
|
+
|
|
689
|
+
test('── markdown-renderer: renderAllFromDb produces all files ──', async () => {
|
|
690
|
+
const tmpDir = makeTmpDir();
|
|
691
|
+
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
692
|
+
openDatabase(dbPath);
|
|
693
|
+
clearAllCaches();
|
|
694
|
+
|
|
695
|
+
try {
|
|
696
|
+
// Setup: 2 milestones, M001 has 2 slices with tasks, M002 has 1 slice
|
|
697
|
+
scaffoldDirs(tmpDir, 'M001', ['S01', 'S02']);
|
|
698
|
+
scaffoldDirs(tmpDir, 'M002', ['S01']);
|
|
699
|
+
|
|
700
|
+
insertMilestone({ id: 'M001', title: 'First', status: 'active' });
|
|
701
|
+
insertMilestone({ id: 'M002', title: 'Second', status: 'active' });
|
|
702
|
+
|
|
703
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Core', status: 'complete' });
|
|
704
|
+
insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Render', status: 'pending' });
|
|
705
|
+
insertSlice({ id: 'S01', milestoneId: 'M002', title: 'Future', status: 'pending' });
|
|
706
|
+
|
|
707
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'DB', status: 'done', fullSummaryMd: makeTaskSummaryContent('T01') });
|
|
708
|
+
insertTask({ id: 'T01', sliceId: 'S02', milestoneId: 'M001', title: 'Renderer', status: 'pending' });
|
|
709
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M002', title: 'Future task', status: 'pending' });
|
|
710
|
+
|
|
711
|
+
// Write roadmap and plan files on disk
|
|
712
|
+
const roadmap1 = makeRoadmapContent([
|
|
713
|
+
{ id: 'S01', title: 'Core', done: false },
|
|
714
|
+
{ id: 'S02', title: 'Render', done: false },
|
|
715
|
+
]);
|
|
716
|
+
fs.writeFileSync(
|
|
717
|
+
path.join(tmpDir, '.gsd', 'milestones', 'M001', 'M001-ROADMAP.md'),
|
|
718
|
+
roadmap1,
|
|
719
|
+
);
|
|
720
|
+
|
|
721
|
+
const roadmap2 = makeRoadmapContent([
|
|
722
|
+
{ id: 'S01', title: 'Future', done: false },
|
|
723
|
+
]);
|
|
724
|
+
fs.writeFileSync(
|
|
725
|
+
path.join(tmpDir, '.gsd', 'milestones', 'M002', 'M002-ROADMAP.md'),
|
|
726
|
+
roadmap2,
|
|
727
|
+
);
|
|
728
|
+
|
|
729
|
+
const plan1 = makePlanContent('S01', [
|
|
730
|
+
{ id: 'T01', title: 'DB', done: false },
|
|
731
|
+
]);
|
|
732
|
+
fs.writeFileSync(
|
|
733
|
+
path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-PLAN.md'),
|
|
734
|
+
plan1,
|
|
735
|
+
);
|
|
736
|
+
|
|
737
|
+
const plan2 = makePlanContent('S02', [
|
|
738
|
+
{ id: 'T01', title: 'Renderer', done: false },
|
|
739
|
+
]);
|
|
740
|
+
fs.writeFileSync(
|
|
741
|
+
path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S02', 'S02-PLAN.md'),
|
|
742
|
+
plan2,
|
|
743
|
+
);
|
|
744
|
+
|
|
745
|
+
const plan3 = makePlanContent('S01', [
|
|
746
|
+
{ id: 'T01', title: 'Future task', done: false },
|
|
747
|
+
]);
|
|
748
|
+
fs.writeFileSync(
|
|
749
|
+
path.join(tmpDir, '.gsd', 'milestones', 'M002', 'slices', 'S01', 'S01-PLAN.md'),
|
|
750
|
+
plan3,
|
|
751
|
+
);
|
|
752
|
+
|
|
753
|
+
clearAllCaches();
|
|
754
|
+
|
|
755
|
+
const result = await renderAllFromDb(tmpDir);
|
|
756
|
+
|
|
757
|
+
assert.ok(result.rendered > 0, 'renderAllFromDb rendered some files');
|
|
758
|
+
assert.deepStrictEqual(result.errors.length, 0, 'renderAllFromDb had no errors');
|
|
759
|
+
|
|
760
|
+
// Verify M001 roadmap has S01 checked
|
|
761
|
+
const m1Roadmap = fs.readFileSync(
|
|
762
|
+
path.join(tmpDir, '.gsd', 'milestones', 'M001', 'M001-ROADMAP.md'), 'utf-8',
|
|
763
|
+
);
|
|
764
|
+
clearAllCaches();
|
|
765
|
+
const parsed1 = parseRoadmap(m1Roadmap);
|
|
766
|
+
const s01 = parsed1.slices.find(s => s.id === 'S01');
|
|
767
|
+
assert.ok(s01!.done, 'M001 S01 checked after renderAll');
|
|
768
|
+
|
|
769
|
+
// Verify M001/S01 plan has T01 checked
|
|
770
|
+
const m1s1Plan = fs.readFileSync(
|
|
771
|
+
path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-PLAN.md'), 'utf-8',
|
|
772
|
+
);
|
|
773
|
+
clearAllCaches();
|
|
774
|
+
const parsedPlan = parsePlan(m1s1Plan);
|
|
775
|
+
assert.ok(parsedPlan.tasks[0].done, 'M001/S01 T01 checked after renderAll');
|
|
776
|
+
|
|
777
|
+
// Verify task summary written
|
|
778
|
+
const taskSummaryPath = path.join(
|
|
779
|
+
tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'tasks', 'T01-SUMMARY.md',
|
|
780
|
+
);
|
|
781
|
+
assert.ok(fs.existsSync(taskSummaryPath), 'T01 summary written by renderAll');
|
|
782
|
+
} finally {
|
|
783
|
+
closeDatabase();
|
|
784
|
+
cleanupDir(tmpDir);
|
|
785
|
+
}
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
789
|
+
// Graceful Degradation (Disk Fallback)
|
|
790
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
791
|
+
|
|
792
|
+
test('── markdown-renderer: graceful fallback reads from disk when artifact not in DB ──', async () => {
|
|
793
|
+
const tmpDir = makeTmpDir();
|
|
794
|
+
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
795
|
+
openDatabase(dbPath);
|
|
796
|
+
clearAllCaches();
|
|
797
|
+
|
|
798
|
+
try {
|
|
799
|
+
scaffoldDirs(tmpDir, 'M001', ['S01']);
|
|
800
|
+
|
|
801
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
802
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Core', status: 'complete' });
|
|
803
|
+
|
|
804
|
+
// Write roadmap to disk but NOT in artifacts DB
|
|
805
|
+
const roadmapContent = makeRoadmapContent([
|
|
806
|
+
{ id: 'S01', title: 'Core', done: false },
|
|
807
|
+
]);
|
|
808
|
+
const roadmapPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'M001-ROADMAP.md');
|
|
809
|
+
fs.writeFileSync(roadmapPath, roadmapContent);
|
|
810
|
+
clearAllCaches();
|
|
811
|
+
|
|
812
|
+
// Verify no artifact in DB
|
|
813
|
+
const before = getArtifact('milestones/M001/M001-ROADMAP.md');
|
|
814
|
+
assert.deepStrictEqual(before, null, 'artifact not in DB before render');
|
|
815
|
+
|
|
816
|
+
// Render — should read from disk, store in DB
|
|
817
|
+
const ok = await renderRoadmapCheckboxes(tmpDir, 'M001');
|
|
818
|
+
assert.ok(ok, 'render succeeds with disk fallback');
|
|
819
|
+
|
|
820
|
+
// Verify artifact now in DB (stored after reading from disk)
|
|
821
|
+
const after = getArtifact('milestones/M001/M001-ROADMAP.md');
|
|
822
|
+
assert.ok(after !== null, 'artifact stored in DB after disk fallback render');
|
|
823
|
+
assert.ok(after!.full_content.includes('[x] **S01:'), 'DB artifact reflects rendered state');
|
|
824
|
+
} finally {
|
|
825
|
+
closeDatabase();
|
|
826
|
+
cleanupDir(tmpDir);
|
|
827
|
+
}
|
|
828
|
+
});
|
|
829
|
+
|
|
830
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
831
|
+
// stderr warnings (graceful degradation diagnostics)
|
|
832
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
833
|
+
|
|
834
|
+
test('── markdown-renderer: stderr warning on missing content ──', async () => {
|
|
835
|
+
openDatabase(':memory:');
|
|
836
|
+
|
|
837
|
+
// No milestone/slices in DB, no files on disk — should return false and emit stderr
|
|
838
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
839
|
+
// No slices inserted — should warn about no slices
|
|
840
|
+
|
|
841
|
+
const ok = await renderRoadmapCheckboxes('/nonexistent/path', 'M001');
|
|
842
|
+
assert.ok(!ok, 'returns false when no slices in DB');
|
|
843
|
+
|
|
844
|
+
closeDatabase();
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
848
|
+
// Stale Detection — Plan Checkbox Mismatch
|
|
849
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
850
|
+
|
|
851
|
+
test('── markdown-renderer: detectStaleRenders finds plan checkbox mismatch ──', () => {
|
|
852
|
+
const tmpDir = makeTmpDir();
|
|
853
|
+
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
854
|
+
openDatabase(dbPath);
|
|
855
|
+
clearAllCaches();
|
|
856
|
+
|
|
857
|
+
try {
|
|
858
|
+
scaffoldDirs(tmpDir, 'M001', ['S01']);
|
|
859
|
+
|
|
860
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
861
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'pending' });
|
|
862
|
+
|
|
863
|
+
// T01 is done, T02 is also done in DB
|
|
864
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'First task', status: 'done' });
|
|
865
|
+
insertTask({ id: 'T02', sliceId: 'S01', milestoneId: 'M001', title: 'Second task', status: 'done' });
|
|
866
|
+
|
|
867
|
+
// Write plan with T01 checked but T02 unchecked
|
|
868
|
+
// T01 matches DB (done + checked) but T02 is stale (done but unchecked)
|
|
869
|
+
const planContent = makePlanContent('S01', [
|
|
870
|
+
{ id: 'T01', title: 'First task', done: true },
|
|
871
|
+
{ id: 'T02', title: 'Second task', done: false },
|
|
872
|
+
]);
|
|
873
|
+
const planPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-PLAN.md');
|
|
874
|
+
fs.writeFileSync(planPath, planContent);
|
|
875
|
+
clearAllCaches();
|
|
876
|
+
|
|
877
|
+
// Render T01 to sync it, but leave T02 out of sync
|
|
878
|
+
// Actually, the plan was written with T01 already checked.
|
|
879
|
+
// The stale detection should find T02 as stale.
|
|
880
|
+
const stale = detectStaleRenders(tmpDir);
|
|
881
|
+
|
|
882
|
+
assert.ok(stale.length > 0, 'detectStaleRenders should find stale entries');
|
|
883
|
+
const t02Stale = stale.find(s => s.reason.includes('T02'));
|
|
884
|
+
assert.ok(!!t02Stale, 'should detect T02 as stale (done in DB, unchecked in plan)');
|
|
885
|
+
assert.ok(t02Stale!.reason.includes('done in DB but unchecked'), 'reason should explain the mismatch');
|
|
886
|
+
|
|
887
|
+
// T01 should NOT be stale — it's checked and done
|
|
888
|
+
const t01Stale = stale.find(s => s.reason.includes('T01'));
|
|
889
|
+
assert.deepStrictEqual(t01Stale, undefined, 'T01 should not be stale (done and checked)');
|
|
890
|
+
} finally {
|
|
891
|
+
closeDatabase();
|
|
892
|
+
cleanupDir(tmpDir);
|
|
893
|
+
}
|
|
894
|
+
});
|
|
895
|
+
|
|
896
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
897
|
+
// Stale Repair — Plan Checkbox
|
|
898
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
899
|
+
|
|
900
|
+
test('── markdown-renderer: repairStaleRenders fixes plan and second detect returns empty ──', async () => {
|
|
901
|
+
const tmpDir = makeTmpDir();
|
|
902
|
+
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
903
|
+
openDatabase(dbPath);
|
|
904
|
+
clearAllCaches();
|
|
905
|
+
|
|
906
|
+
try {
|
|
907
|
+
scaffoldDirs(tmpDir, 'M001', ['S01']);
|
|
908
|
+
|
|
909
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
910
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'pending' });
|
|
911
|
+
|
|
912
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'First task', status: 'done' });
|
|
913
|
+
insertTask({ id: 'T02', sliceId: 'S01', milestoneId: 'M001', title: 'Second task', status: 'done' });
|
|
914
|
+
|
|
915
|
+
// Write plan with both tasks unchecked (both are stale since DB says done)
|
|
916
|
+
const planContent = makePlanContent('S01', [
|
|
917
|
+
{ id: 'T01', title: 'First task', done: false },
|
|
918
|
+
{ id: 'T02', title: 'Second task', done: false },
|
|
919
|
+
]);
|
|
920
|
+
const planPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-PLAN.md');
|
|
921
|
+
fs.writeFileSync(planPath, planContent);
|
|
922
|
+
clearAllCaches();
|
|
923
|
+
|
|
924
|
+
// Verify stale before repair
|
|
925
|
+
const staleBefore = detectStaleRenders(tmpDir);
|
|
926
|
+
assert.ok(staleBefore.length > 0, 'should have stale entries before repair');
|
|
927
|
+
|
|
928
|
+
// Repair
|
|
929
|
+
const repaired = await repairStaleRenders(tmpDir);
|
|
930
|
+
assert.ok(repaired > 0, 'repairStaleRenders should repair at least 1 file');
|
|
931
|
+
|
|
932
|
+
// After repair, detect again — should be empty
|
|
933
|
+
clearAllCaches();
|
|
934
|
+
const staleAfter = detectStaleRenders(tmpDir);
|
|
935
|
+
assert.deepStrictEqual(staleAfter.length, 0, 'detectStaleRenders should return empty after repair');
|
|
936
|
+
|
|
937
|
+
// Verify the plan file was actually updated
|
|
938
|
+
const repairedContent = fs.readFileSync(planPath, 'utf-8');
|
|
939
|
+
assert.ok(repairedContent.includes('[x] **T01:'), 'T01 should be checked after repair');
|
|
940
|
+
assert.ok(repairedContent.includes('[x] **T02:'), 'T02 should be checked after repair');
|
|
941
|
+
} finally {
|
|
942
|
+
closeDatabase();
|
|
943
|
+
cleanupDir(tmpDir);
|
|
944
|
+
}
|
|
945
|
+
});
|
|
946
|
+
|
|
947
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
948
|
+
// Stale Detection — Roadmap Checkbox Mismatch
|
|
949
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
950
|
+
|
|
951
|
+
test('── markdown-renderer: detectStaleRenders finds roadmap checkbox mismatch ──', () => {
|
|
952
|
+
const tmpDir = makeTmpDir();
|
|
953
|
+
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
954
|
+
openDatabase(dbPath);
|
|
955
|
+
clearAllCaches();
|
|
956
|
+
|
|
957
|
+
try {
|
|
958
|
+
scaffoldDirs(tmpDir, 'M001', ['S01', 'S02']);
|
|
959
|
+
|
|
960
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
961
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Core', status: 'complete' });
|
|
962
|
+
insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Render', status: 'pending' });
|
|
963
|
+
|
|
964
|
+
// Write roadmap with both slices unchecked (S01 is stale — complete in DB but unchecked)
|
|
965
|
+
const roadmapContent = makeRoadmapContent([
|
|
966
|
+
{ id: 'S01', title: 'Core', done: false },
|
|
967
|
+
{ id: 'S02', title: 'Render', done: false },
|
|
968
|
+
]);
|
|
969
|
+
const roadmapPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'M001-ROADMAP.md');
|
|
970
|
+
fs.writeFileSync(roadmapPath, roadmapContent);
|
|
971
|
+
clearAllCaches();
|
|
972
|
+
|
|
973
|
+
const stale = detectStaleRenders(tmpDir);
|
|
974
|
+
const s01Stale = stale.find(s => s.reason.includes('S01'));
|
|
975
|
+
assert.ok(!!s01Stale, 'should detect S01 as stale (complete in DB, unchecked in roadmap)');
|
|
976
|
+
|
|
977
|
+
const s02Stale = stale.find(s => s.reason.includes('S02'));
|
|
978
|
+
assert.deepStrictEqual(s02Stale, undefined, 'S02 should not be stale (pending and unchecked — matches)');
|
|
979
|
+
} finally {
|
|
980
|
+
closeDatabase();
|
|
981
|
+
cleanupDir(tmpDir);
|
|
982
|
+
}
|
|
983
|
+
});
|
|
984
|
+
|
|
985
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
986
|
+
// Stale Detection — Missing Task Summary
|
|
987
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
988
|
+
|
|
989
|
+
test('── markdown-renderer: detectStaleRenders finds missing task summary ──', () => {
|
|
990
|
+
const tmpDir = makeTmpDir();
|
|
991
|
+
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
992
|
+
openDatabase(dbPath);
|
|
993
|
+
clearAllCaches();
|
|
994
|
+
|
|
995
|
+
try {
|
|
996
|
+
scaffoldDirs(tmpDir, 'M001', ['S01']);
|
|
997
|
+
|
|
998
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
999
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'pending' });
|
|
1000
|
+
|
|
1001
|
+
// Task is done with full_summary_md, but no SUMMARY.md on disk
|
|
1002
|
+
const summaryContent = makeTaskSummaryContent('T01');
|
|
1003
|
+
insertTask({
|
|
1004
|
+
id: 'T01',
|
|
1005
|
+
sliceId: 'S01',
|
|
1006
|
+
milestoneId: 'M001',
|
|
1007
|
+
title: 'Task',
|
|
1008
|
+
status: 'done',
|
|
1009
|
+
fullSummaryMd: summaryContent,
|
|
1010
|
+
});
|
|
1011
|
+
|
|
1012
|
+
// Also write a plan so plan detection doesn't trigger (T01 is done but not checked)
|
|
1013
|
+
// We need a plan file so task plan detection works — but we specifically want to test
|
|
1014
|
+
// the missing summary case, so write plan with T01 checked
|
|
1015
|
+
const planContent = makePlanContent('S01', [
|
|
1016
|
+
{ id: 'T01', title: 'Task', done: true },
|
|
1017
|
+
]);
|
|
1018
|
+
const planPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-PLAN.md');
|
|
1019
|
+
fs.writeFileSync(planPath, planContent);
|
|
1020
|
+
clearAllCaches();
|
|
1021
|
+
|
|
1022
|
+
const stale = detectStaleRenders(tmpDir);
|
|
1023
|
+
const summaryStale = stale.find(s => s.reason.includes('SUMMARY.md missing'));
|
|
1024
|
+
assert.ok(!!summaryStale, 'should detect missing T01-SUMMARY.md');
|
|
1025
|
+
assert.ok(summaryStale!.reason.includes('T01'), 'reason should mention T01');
|
|
1026
|
+
} finally {
|
|
1027
|
+
closeDatabase();
|
|
1028
|
+
cleanupDir(tmpDir);
|
|
1029
|
+
}
|
|
1030
|
+
});
|
|
1031
|
+
|
|
1032
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1033
|
+
// Stale Repair — Missing Task Summary
|
|
1034
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1035
|
+
|
|
1036
|
+
test('── markdown-renderer: repairStaleRenders writes missing task summary ──', async () => {
|
|
1037
|
+
const tmpDir = makeTmpDir();
|
|
1038
|
+
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
1039
|
+
openDatabase(dbPath);
|
|
1040
|
+
clearAllCaches();
|
|
1041
|
+
|
|
1042
|
+
try {
|
|
1043
|
+
scaffoldDirs(tmpDir, 'M001', ['S01']);
|
|
1044
|
+
|
|
1045
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
1046
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'pending' });
|
|
1047
|
+
|
|
1048
|
+
const summaryContent = makeTaskSummaryContent('T01');
|
|
1049
|
+
insertTask({
|
|
1050
|
+
id: 'T01',
|
|
1051
|
+
sliceId: 'S01',
|
|
1052
|
+
milestoneId: 'M001',
|
|
1053
|
+
title: 'Task',
|
|
1054
|
+
status: 'done',
|
|
1055
|
+
fullSummaryMd: summaryContent,
|
|
1056
|
+
});
|
|
1057
|
+
|
|
1058
|
+
// Write plan with T01 checked so plan detection doesn't trigger
|
|
1059
|
+
const planContent = makePlanContent('S01', [
|
|
1060
|
+
{ id: 'T01', title: 'Task', done: true },
|
|
1061
|
+
]);
|
|
1062
|
+
const planPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-PLAN.md');
|
|
1063
|
+
fs.writeFileSync(planPath, planContent);
|
|
1064
|
+
clearAllCaches();
|
|
1065
|
+
|
|
1066
|
+
// Repair
|
|
1067
|
+
const repaired = await repairStaleRenders(tmpDir);
|
|
1068
|
+
assert.ok(repaired > 0, 'should repair missing summary');
|
|
1069
|
+
|
|
1070
|
+
// Verify file written
|
|
1071
|
+
const summaryPath = path.join(
|
|
1072
|
+
tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'tasks', 'T01-SUMMARY.md',
|
|
1073
|
+
);
|
|
1074
|
+
assert.ok(fs.existsSync(summaryPath), 'T01-SUMMARY.md should exist after repair');
|
|
1075
|
+
|
|
1076
|
+
// Second detect should be empty
|
|
1077
|
+
clearAllCaches();
|
|
1078
|
+
const staleAfter = detectStaleRenders(tmpDir);
|
|
1079
|
+
const summaryStale = staleAfter.find(s => s.reason.includes('SUMMARY.md missing') && s.reason.includes('T01'));
|
|
1080
|
+
assert.deepStrictEqual(summaryStale, undefined, 'missing summary should be fixed after repair');
|
|
1081
|
+
} finally {
|
|
1082
|
+
closeDatabase();
|
|
1083
|
+
cleanupDir(tmpDir);
|
|
1084
|
+
}
|
|
1085
|
+
});
|
|
1086
|
+
|
|
1087
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1088
|
+
// Stale Repair — Idempotency
|
|
1089
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1090
|
+
|
|
1091
|
+
test('── markdown-renderer: repairStaleRenders idempotency — fully synced returns 0 ──', async () => {
|
|
1092
|
+
const tmpDir = makeTmpDir();
|
|
1093
|
+
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
1094
|
+
openDatabase(dbPath);
|
|
1095
|
+
clearAllCaches();
|
|
1096
|
+
|
|
1097
|
+
try {
|
|
1098
|
+
scaffoldDirs(tmpDir, 'M001', ['S01']);
|
|
1099
|
+
|
|
1100
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
1101
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'pending' });
|
|
1102
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'Task', status: 'done' });
|
|
1103
|
+
|
|
1104
|
+
// Write plan with T01 checked — matches DB
|
|
1105
|
+
const planContent = makePlanContent('S01', [
|
|
1106
|
+
{ id: 'T01', title: 'Task', done: true },
|
|
1107
|
+
]);
|
|
1108
|
+
const planPath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-PLAN.md');
|
|
1109
|
+
fs.writeFileSync(planPath, planContent);
|
|
1110
|
+
clearAllCaches();
|
|
1111
|
+
|
|
1112
|
+
// No stale entries when everything is in sync (no summary to check since no fullSummaryMd)
|
|
1113
|
+
const repaired = await repairStaleRenders(tmpDir);
|
|
1114
|
+
assert.deepStrictEqual(repaired, 0, 'repairStaleRenders should return 0 on fully synced project');
|
|
1115
|
+
} finally {
|
|
1116
|
+
closeDatabase();
|
|
1117
|
+
cleanupDir(tmpDir);
|
|
1118
|
+
}
|
|
1119
|
+
});
|
|
1120
|
+
|
|
1121
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1122
|
+
// Stale Detection — Missing Slice Summary + UAT
|
|
1123
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1124
|
+
|
|
1125
|
+
test('── markdown-renderer: detectStaleRenders finds missing slice summary and UAT ──', () => {
|
|
1126
|
+
const tmpDir = makeTmpDir();
|
|
1127
|
+
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
1128
|
+
openDatabase(dbPath);
|
|
1129
|
+
clearAllCaches();
|
|
1130
|
+
|
|
1131
|
+
try {
|
|
1132
|
+
scaffoldDirs(tmpDir, 'M001', ['S01']);
|
|
1133
|
+
|
|
1134
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
1135
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'pending' });
|
|
1136
|
+
|
|
1137
|
+
// Update slice to complete with content via raw adapter
|
|
1138
|
+
const adapter = _getAdapter()!;
|
|
1139
|
+
adapter.prepare(
|
|
1140
|
+
`UPDATE slices SET status = 'complete', full_summary_md = :sm, full_uat_md = :um WHERE milestone_id = 'M001' AND id = 'S01'`,
|
|
1141
|
+
).run({
|
|
1142
|
+
':sm': '---\nid: S01\nparent: M001\nmilestone: M001\n---\n\n# S01: Summary\n\nDone.\n',
|
|
1143
|
+
':um': '# S01 UAT\n\nAll pass.\n',
|
|
1144
|
+
});
|
|
1145
|
+
|
|
1146
|
+
clearAllCaches();
|
|
1147
|
+
|
|
1148
|
+
const stale = detectStaleRenders(tmpDir);
|
|
1149
|
+
const summaryStale = stale.find(s => s.reason.includes('SUMMARY.md missing') && s.reason.includes('S01'));
|
|
1150
|
+
const uatStale = stale.find(s => s.reason.includes('UAT.md missing') && s.reason.includes('S01'));
|
|
1151
|
+
|
|
1152
|
+
assert.ok(!!summaryStale, 'should detect missing S01-SUMMARY.md');
|
|
1153
|
+
assert.ok(!!uatStale, 'should detect missing S01-UAT.md');
|
|
1154
|
+
} finally {
|
|
1155
|
+
closeDatabase();
|
|
1156
|
+
cleanupDir(tmpDir);
|
|
1157
|
+
}
|
|
1158
|
+
});
|
|
1159
|
+
|
|
1160
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1161
|
+
|