@opengsd/gsd-pi 1.7.0 → 1.8.1-dev.897b3c90
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 +8 -6
- package/dist/cli.js +5 -1
- package/dist/headless-events.d.ts +2 -1
- package/dist/headless-events.js +11 -3
- package/dist/headless-milestone-readiness.d.ts +29 -0
- package/dist/headless-milestone-readiness.js +161 -0
- package/dist/headless.js +25 -2
- package/dist/print-mode-exit.d.ts +12 -0
- package/dist/print-mode-exit.js +14 -0
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/GSD-WORKFLOW.md +16 -47
- package/dist/resources/extensions/browser-tools/capture.js +2 -77
- package/dist/resources/extensions/browser-tools/screenshot-constraints.js +83 -0
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +17 -2
- package/dist/resources/extensions/claude-code-cli/turn-assembler.js +30 -2
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +2 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +74 -19
- package/dist/resources/extensions/gsd/auto-post-unit.js +4 -0
- package/dist/resources/extensions/gsd/auto-prompts.js +35 -1
- package/dist/resources/extensions/gsd/auto.js +5 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +78 -4
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +84 -26
- package/dist/resources/extensions/gsd/command-feedback.js +16 -12
- package/dist/resources/extensions/gsd/compat/compat-marker.js +19 -2
- package/dist/resources/extensions/gsd/db/command-queue.js +7 -2
- package/dist/resources/extensions/gsd/db/engine.js +51 -16
- package/dist/resources/extensions/gsd/db/writers/cascades.js +5 -1
- package/dist/resources/extensions/gsd/db/writers/status.js +13 -3
- package/dist/resources/extensions/gsd/db-base-schema.js +28 -0
- package/dist/resources/extensions/gsd/db-migration-steps.js +30 -0
- package/dist/resources/extensions/gsd/db-schema-metadata.js +4 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +4 -4
- package/dist/resources/extensions/gsd/doctor-history.js +65 -0
- package/dist/resources/extensions/gsd/doctor-state-checks.js +509 -0
- package/dist/resources/extensions/gsd/doctor-workspace-checks.js +91 -0
- package/dist/resources/extensions/gsd/doctor.js +14 -653
- package/dist/resources/extensions/gsd/flat-phase-migration.js +156 -21
- package/dist/resources/extensions/gsd/gsd-db.js +187 -45
- package/dist/resources/extensions/gsd/guided-flow.js +29 -10
- package/dist/resources/extensions/gsd/markdown-renderer.js +16 -2
- package/dist/resources/extensions/gsd/md-importer.js +109 -16
- package/dist/resources/extensions/gsd/migrate/command.js +19 -162
- package/dist/resources/extensions/gsd/migrate/execution.js +98 -0
- package/dist/resources/extensions/gsd/migrate/plan.js +49 -0
- package/dist/resources/extensions/gsd/migrate/planning-writer.js +20 -19
- package/dist/resources/extensions/gsd/migrate/presentation.js +64 -0
- package/dist/resources/extensions/gsd/migration-auto-check.js +4 -0
- package/dist/resources/extensions/gsd/parallel-eligibility.js +5 -4
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +28 -43
- package/dist/resources/extensions/gsd/paths.js +4 -1
- package/dist/resources/extensions/gsd/phase-anchor.js +7 -2
- package/dist/resources/extensions/gsd/post-execution-checks.js +4 -4
- package/dist/resources/extensions/gsd/prompts/refine-slice.md +2 -2
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +26 -1
- package/dist/resources/extensions/gsd/safety/evidence-cross-ref.js +30 -5
- package/dist/resources/extensions/gsd/state-reconciliation/drift/external-markdown-edit.js +27 -13
- package/dist/resources/extensions/gsd/state-reconciliation/drift/external-planning-edit.js +13 -3
- package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +54 -14
- package/dist/resources/extensions/gsd/state-reconciliation/drift/sketch-flag.js +48 -5
- package/dist/resources/extensions/gsd/stop-notice.js +15 -1
- package/dist/resources/extensions/gsd/sync-lock.js +13 -7
- package/dist/resources/extensions/gsd/tools/complete-task.js +82 -1
- package/dist/resources/extensions/gsd/tools/plan-task.js +13 -15
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +18 -15
- package/dist/resources/extensions/gsd/tools/replan-task.js +128 -0
- package/dist/resources/extensions/gsd/tools/rework-brief.js +74 -0
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +81 -0
- package/dist/resources/extensions/gsd/unit-registry.js +4 -0
- package/dist/resources/extensions/gsd/uok/plan-v2.js +19 -3
- package/dist/resources/extensions/gsd/worktree-state-projection.js +6 -0
- package/dist/resources/extensions/mac-tools/index.js +31 -9
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- 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/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/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/mcp-connections/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/mcp-connections/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/notifications/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 +2 -2
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +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 +1 -1
- 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 +9 -9
- package/dist/web/standalone/.next/server/chunks/2842.js +4 -4
- package/dist/web/standalone/.next/server/chunks/8357.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +3 -3
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/app/_not-found/page-a6fb1847f67f167c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/{layout-b35cbfff38aaf4cf.js → layout-4ae2d68984392bbf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-72a856634ad14c10.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{main-app-590a74400e35f685.js → main-app-90d1d8d5e5d2dc6b.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-1115d46ac61b9823.js +1 -0
- package/dist/web/standalone/.next/static/css/3b1ceb7abfb9a230.css +1 -0
- package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/package.json +0 -1
- package/dist/web/standalone/server.js +1 -1
- package/dist/web-mode.js +31 -3
- package/integrations/hermes/open_gsd_hermes/gsd_client.py +1 -1
- package/integrations/hermes/pyproject.toml +1 -1
- package/package.json +12 -10
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/dist/workflow.d.ts +14 -0
- package/packages/contracts/dist/workflow.d.ts.map +1 -1
- package/packages/contracts/dist/workflow.js +16 -0
- package/packages/contracts/dist/workflow.js.map +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/dist/cloud-config.d.ts +1 -1
- package/packages/daemon/dist/cloud-config.d.ts.map +1 -1
- package/packages/daemon/dist/cloud-config.js +30 -8
- package/packages/daemon/dist/cloud-config.js.map +1 -1
- package/packages/daemon/dist/cloud-config.test.js +69 -1
- package/packages/daemon/dist/cloud-config.test.js.map +1 -1
- package/packages/daemon/dist/local-tool-executor.d.ts.map +1 -1
- package/packages/daemon/dist/local-tool-executor.js +2 -0
- package/packages/daemon/dist/local-tool-executor.js.map +1 -1
- package/packages/daemon/package.json +6 -6
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/gsd-cloud/README.md +18 -4
- package/packages/gsd-cloud/bin/gsd-cloud.js +6 -5
- package/packages/gsd-cloud/dist/cli.d.ts +5 -0
- package/packages/gsd-cloud/dist/cli.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/cli.js +160 -0
- package/packages/gsd-cloud/dist/cli.js.map +1 -0
- package/packages/gsd-cloud/dist/cloud-config.d.ts +19 -0
- package/packages/gsd-cloud/dist/cloud-config.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/cloud-config.js +231 -0
- package/packages/gsd-cloud/dist/cloud-config.js.map +1 -0
- package/packages/gsd-cloud/dist/cloud-config.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/cloud-config.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/cloud-config.test.js +118 -0
- package/packages/gsd-cloud/dist/cloud-config.test.js.map +1 -0
- package/packages/gsd-cloud/dist/cloud-gateway-lookup.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/cloud-gateway-lookup.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/cloud-gateway-lookup.test.js +37 -0
- package/packages/gsd-cloud/dist/cloud-gateway-lookup.test.js.map +1 -0
- package/packages/gsd-cloud/dist/cloud-runtime.d.ts +35 -0
- package/packages/gsd-cloud/dist/cloud-runtime.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/cloud-runtime.js +258 -0
- package/packages/gsd-cloud/dist/cloud-runtime.js.map +1 -0
- package/packages/gsd-cloud/dist/cloud-runtime.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/cloud-runtime.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/cloud-runtime.test.js +88 -0
- package/packages/gsd-cloud/dist/cloud-runtime.test.js.map +1 -0
- package/packages/gsd-cloud/dist/cloud-token.d.ts +3 -0
- package/packages/gsd-cloud/dist/cloud-token.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/cloud-token.js +37 -0
- package/packages/gsd-cloud/dist/cloud-token.js.map +1 -0
- package/packages/gsd-cloud/dist/config.d.ts +17 -0
- package/packages/gsd-cloud/dist/config.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/config.js +146 -0
- package/packages/gsd-cloud/dist/config.js.map +1 -0
- package/packages/gsd-cloud/dist/device-flow.d.ts +24 -0
- package/packages/gsd-cloud/dist/device-flow.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/device-flow.js +128 -0
- package/packages/gsd-cloud/dist/device-flow.js.map +1 -0
- package/packages/gsd-cloud/dist/device-flow.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/device-flow.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/device-flow.test.js +155 -0
- package/packages/gsd-cloud/dist/device-flow.test.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/claude-executor.d.ts +6 -0
- package/packages/gsd-cloud/dist/executors/claude-executor.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/claude-executor.js +15 -0
- package/packages/gsd-cloud/dist/executors/claude-executor.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/codex-executor.d.ts +6 -0
- package/packages/gsd-cloud/dist/executors/codex-executor.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/codex-executor.js +15 -0
- package/packages/gsd-cloud/dist/executors/codex-executor.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/executor.d.ts +28 -0
- package/packages/gsd-cloud/dist/executors/executor.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/executor.js +11 -0
- package/packages/gsd-cloud/dist/executors/executor.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.d.ts +34 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.js +179 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.test.js +44 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.test.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/index.d.ts +17 -0
- package/packages/gsd-cloud/dist/executors/index.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/index.js +29 -0
- package/packages/gsd-cloud/dist/executors/index.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.d.ts +36 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.js +176 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.test.js +34 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.test.js.map +1 -0
- package/packages/gsd-cloud/dist/index.d.ts +7 -0
- package/packages/gsd-cloud/dist/index.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/index.js +8 -0
- package/packages/gsd-cloud/dist/index.js.map +1 -0
- package/packages/gsd-cloud/dist/inject-gateway.d.ts +16 -0
- package/packages/gsd-cloud/dist/inject-gateway.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/inject-gateway.js +29 -0
- package/packages/gsd-cloud/dist/inject-gateway.js.map +1 -0
- package/packages/gsd-cloud/dist/inject-gateway.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/inject-gateway.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/inject-gateway.test.js +41 -0
- package/packages/gsd-cloud/dist/inject-gateway.test.js.map +1 -0
- package/packages/gsd-cloud/dist/logger.d.ts +25 -0
- package/packages/gsd-cloud/dist/logger.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/logger.js +72 -0
- package/packages/gsd-cloud/dist/logger.js.map +1 -0
- package/packages/gsd-cloud/dist/types.d.ts +51 -0
- package/packages/gsd-cloud/dist/types.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/types.js +9 -0
- package/packages/gsd-cloud/dist/types.js.map +1 -0
- package/packages/gsd-cloud/package.json +7 -5
- package/packages/mcp-server/README.md +8 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +64 -2
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +4 -4
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +60 -0
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +54 -0
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/providers/amazon-bedrock.js +1 -0
- package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +19 -0
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/GSD-WORKFLOW.md +16 -47
- package/src/resources/extensions/browser-tools/capture.ts +2 -86
- package/src/resources/extensions/browser-tools/screenshot-constraints.ts +94 -0
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +18 -1
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +108 -0
- package/src/resources/extensions/claude-code-cli/turn-assembler.ts +30 -2
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +2 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +86 -19
- package/src/resources/extensions/gsd/auto-post-unit.ts +4 -0
- package/src/resources/extensions/gsd/auto-prompts.ts +38 -1
- package/src/resources/extensions/gsd/auto.ts +5 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +91 -4
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +80 -24
- package/src/resources/extensions/gsd/command-feedback.ts +19 -12
- package/src/resources/extensions/gsd/compat/compat-marker.ts +21 -4
- package/src/resources/extensions/gsd/db/command-queue.ts +7 -1
- package/src/resources/extensions/gsd/db/engine.ts +53 -17
- package/src/resources/extensions/gsd/db/writers/cascades.ts +5 -1
- package/src/resources/extensions/gsd/db/writers/status.ts +25 -8
- package/src/resources/extensions/gsd/db-base-schema.ts +30 -0
- package/src/resources/extensions/gsd/db-migration-steps.ts +32 -0
- package/src/resources/extensions/gsd/db-schema-metadata.ts +4 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +4 -4
- package/src/resources/extensions/gsd/doctor-history.ts +81 -0
- package/src/resources/extensions/gsd/doctor-state-checks.ts +527 -0
- package/src/resources/extensions/gsd/doctor-workspace-checks.ts +97 -0
- package/src/resources/extensions/gsd/doctor.ts +14 -673
- package/src/resources/extensions/gsd/flat-phase-migration.ts +160 -19
- package/src/resources/extensions/gsd/gsd-db.ts +255 -52
- package/src/resources/extensions/gsd/guided-flow.ts +39 -10
- package/src/resources/extensions/gsd/markdown-renderer.ts +23 -7
- package/src/resources/extensions/gsd/md-importer.ts +116 -13
- package/src/resources/extensions/gsd/migrate/command.ts +21 -227
- package/src/resources/extensions/gsd/migrate/execution.ts +136 -0
- package/src/resources/extensions/gsd/migrate/plan.ts +101 -0
- package/src/resources/extensions/gsd/migrate/planning-writer.ts +21 -23
- package/src/resources/extensions/gsd/migrate/presentation.ts +83 -0
- package/src/resources/extensions/gsd/migration-auto-check.ts +3 -0
- package/src/resources/extensions/gsd/parallel-eligibility.ts +5 -4
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +37 -40
- package/src/resources/extensions/gsd/paths.ts +4 -1
- package/src/resources/extensions/gsd/phase-anchor.ts +8 -2
- package/src/resources/extensions/gsd/post-execution-checks.ts +4 -4
- package/src/resources/extensions/gsd/prompts/refine-slice.md +2 -2
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +28 -1
- package/src/resources/extensions/gsd/safety/evidence-cross-ref.ts +31 -5
- package/src/resources/extensions/gsd/state-reconciliation/drift/external-markdown-edit.ts +30 -13
- package/src/resources/extensions/gsd/state-reconciliation/drift/external-planning-edit.ts +16 -3
- package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +67 -20
- package/src/resources/extensions/gsd/state-reconciliation/drift/sketch-flag.ts +47 -5
- package/src/resources/extensions/gsd/state-reconciliation/types.ts +2 -2
- package/src/resources/extensions/gsd/stop-notice.ts +18 -1
- package/src/resources/extensions/gsd/sync-lock.ts +14 -6
- package/src/resources/extensions/gsd/tests/closeout-git-deferral.test.ts +96 -1
- package/src/resources/extensions/gsd/tests/command-feedback.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/compat-marker.test.ts +104 -0
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +286 -0
- package/src/resources/extensions/gsd/tests/db-authority-regression.test.ts +76 -1
- package/src/resources/extensions/gsd/tests/db-engine-migrate-guards.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/discuss-routing-fixes.test.ts +123 -0
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/doctor-history-public-api.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/evidence-cross-ref.test.ts +81 -0
- package/src/resources/extensions/gsd/tests/evidence-xref-gsd-exec.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/external-edit-scoped-import.test.ts +262 -0
- package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +202 -0
- package/src/resources/extensions/gsd/tests/gate-storage.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/import-slice-milestone-completion-preserve.test.ts +116 -0
- package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/migrate-plan.test.ts +68 -0
- package/src/resources/extensions/gsd/tests/migrate-presentation.test.ts +98 -0
- package/src/resources/extensions/gsd/tests/migrate-safety-audit.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/milestone-leases.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/parallel-eligibility-file-overlap.test.ts +146 -0
- package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/phase-anchor.test.ts +32 -4
- package/src/resources/extensions/gsd/tests/plan-task.test.ts +26 -23
- package/src/resources/extensions/gsd/tests/planning-writer.test.ts +47 -1
- package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +18 -3
- package/src/resources/extensions/gsd/tests/reassess-handler.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/replan-task-handler.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/rework-brief.test.ts +197 -0
- package/src/resources/extensions/gsd/tests/skip-slice-cascades-tasks.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +177 -25
- package/src/resources/extensions/gsd/tests/stop-notice.test.ts +17 -0
- package/src/resources/extensions/gsd/tests/sync-lock.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +93 -1
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/workflow-migration-gates.test.ts +139 -0
- package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/write-gate-seam.test.ts +52 -0
- package/src/resources/extensions/gsd/tools/complete-task.ts +120 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +14 -16
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +18 -17
- package/src/resources/extensions/gsd/tools/replan-task.ts +160 -0
- package/src/resources/extensions/gsd/tools/rework-brief.ts +91 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +92 -0
- package/src/resources/extensions/gsd/types.ts +11 -0
- package/src/resources/extensions/gsd/unit-registry.ts +4 -0
- package/src/resources/extensions/gsd/uok/plan-v2.ts +20 -3
- package/src/resources/extensions/gsd/worktree-state-projection.ts +12 -0
- package/src/resources/extensions/mac-tools/index.ts +52 -9
- package/src/resources/extensions/mac-tools/tests/mac-screenshot-payload.test.cjs +70 -0
- package/dist/web/standalone/.next/static/chunks/app/_not-found/page-49f565245e1e4afe.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/page-bc4c723c6a19c6f0.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ec3eaffc9785ba48.js +0 -1
- package/dist/web/standalone/.next/static/css/18c2fdf261bfc1b7.css +0 -1
- /package/dist/web/standalone/.next/static/{enWQFRY-aLTUq3wmxG2Qz → TezjdjSHBVRRr54XjAvte}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{enWQFRY-aLTUq3wmxG2Qz → TezjdjSHBVRRr54XjAvte}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Regression for #1291 — a full markdown re-import must NOT rewrite
|
|
3
|
+
// the completed_at of an already-complete slice or milestone with the current
|
|
4
|
+
// import time. The task path was fixed for this (#1222/#1228) via
|
|
5
|
+
// preserveCompletionMetadata; the slice and milestone backfills in
|
|
6
|
+
// migrateHierarchyToDb had no such guard, so every re-import re-stamped every
|
|
7
|
+
// complete slice/milestone. The fix threads preserveCompletion through
|
|
8
|
+
// updateSliceStatus/updateMilestoneStatus so an existing completed_at survives.
|
|
9
|
+
|
|
10
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { tmpdir } from 'node:os';
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
openDatabase,
|
|
16
|
+
closeDatabase,
|
|
17
|
+
getAllMilestones,
|
|
18
|
+
getMilestoneSlices,
|
|
19
|
+
updateSliceStatus,
|
|
20
|
+
updateMilestoneStatus,
|
|
21
|
+
} from '../gsd-db.ts';
|
|
22
|
+
import { migrateHierarchyToDb } from '../md-importer.ts';
|
|
23
|
+
import { describe, test, afterEach } from 'node:test';
|
|
24
|
+
import assert from 'node:assert/strict';
|
|
25
|
+
|
|
26
|
+
const tmpDirs: string[] = [];
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
closeDatabase();
|
|
29
|
+
for (const d of tmpDirs) { try { rmSync(d, { recursive: true, force: true }); } catch { /* */ } }
|
|
30
|
+
tmpDirs.length = 0;
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
function createFixtureBase(): string {
|
|
34
|
+
const base = mkdtempSync(join(tmpdir(), 'gsd-preserve-completion-'));
|
|
35
|
+
tmpDirs.push(base);
|
|
36
|
+
return base;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function writeFile(base: string, relativePath: string, content: string): void {
|
|
40
|
+
const full = join(base, '.gsd', relativePath);
|
|
41
|
+
mkdirSync(join(full, '..'), { recursive: true });
|
|
42
|
+
writeFileSync(full, content);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// All slices [x] → milestone imports complete, slices import complete.
|
|
46
|
+
const ROADMAP_ALL_DONE = `# M001: Finished Milestone
|
|
47
|
+
|
|
48
|
+
**Vision:** Done work.
|
|
49
|
+
|
|
50
|
+
## Slices
|
|
51
|
+
|
|
52
|
+
- [x] **S01: First Slice** \`risk:low\` \`depends:[]\`
|
|
53
|
+
> After this: Done.
|
|
54
|
+
|
|
55
|
+
- [x] **S02: Second Slice** \`risk:medium\` \`depends:[S01]\`
|
|
56
|
+
> After this: Also done.
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
const REAL_S01 = '2026-07-05T19:52:04.672Z';
|
|
60
|
+
const REAL_S02 = '2026-07-05T20:36:51.313Z';
|
|
61
|
+
const REAL_M001 = '2026-07-05T21:00:00.000Z';
|
|
62
|
+
|
|
63
|
+
describe('migrateHierarchyToDb: re-import preserves existing completion timestamps (#1291)', () => {
|
|
64
|
+
test('a re-import does not rewrite already-complete slice completed_at', () => {
|
|
65
|
+
const base = createFixtureBase();
|
|
66
|
+
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_ALL_DONE);
|
|
67
|
+
|
|
68
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
69
|
+
migrateHierarchyToDb(base);
|
|
70
|
+
|
|
71
|
+
// Simulate the durable completion timestamps the real run wrote hours apart.
|
|
72
|
+
updateSliceStatus('M001', 'S01', 'complete', REAL_S01);
|
|
73
|
+
updateSliceStatus('M001', 'S02', 'complete', REAL_S02);
|
|
74
|
+
|
|
75
|
+
// A second full re-import (the class of event that bit the reporter).
|
|
76
|
+
migrateHierarchyToDb(base);
|
|
77
|
+
|
|
78
|
+
const slices = getMilestoneSlices('M001');
|
|
79
|
+
const s01 = slices.find((s) => s.id === 'S01')!;
|
|
80
|
+
const s02 = slices.find((s) => s.id === 'S02')!;
|
|
81
|
+
assert.equal(s01.completed_at, REAL_S01, 'S01 completed_at must survive re-import unchanged');
|
|
82
|
+
assert.equal(s02.completed_at, REAL_S02, 'S02 completed_at must survive re-import unchanged');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('a re-import does not rewrite an already-complete milestone completed_at', () => {
|
|
86
|
+
const base = createFixtureBase();
|
|
87
|
+
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_ALL_DONE);
|
|
88
|
+
|
|
89
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
90
|
+
migrateHierarchyToDb(base);
|
|
91
|
+
|
|
92
|
+
updateMilestoneStatus('M001', 'complete', REAL_M001);
|
|
93
|
+
|
|
94
|
+
migrateHierarchyToDb(base);
|
|
95
|
+
|
|
96
|
+
const m001 = getAllMilestones().find((m) => m.id === 'M001')!;
|
|
97
|
+
assert.equal(m001.completed_at, REAL_M001, 'M001 completed_at must survive re-import unchanged');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('an initial import of a complete slice still backfills a completed_at when the row has none', () => {
|
|
101
|
+
// The guard preserves an existing timestamp but must not suppress the
|
|
102
|
+
// legitimate first-time backfill for a slice imported as complete.
|
|
103
|
+
const base = createFixtureBase();
|
|
104
|
+
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_ALL_DONE);
|
|
105
|
+
|
|
106
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
107
|
+
migrateHierarchyToDb(base);
|
|
108
|
+
|
|
109
|
+
const slices = getMilestoneSlices('M001');
|
|
110
|
+
for (const s of slices) {
|
|
111
|
+
assert.ok(s.completed_at, `complete slice ${s.id} should receive a backfilled completed_at on first import`);
|
|
112
|
+
}
|
|
113
|
+
const m001 = getAllMilestones().find((m) => m.id === 'M001')!;
|
|
114
|
+
assert.ok(m001.completed_at, 'complete milestone should receive a backfilled completed_at on first import');
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -427,3 +427,56 @@ test('migrate-hier: demo text extracted', () => {
|
|
|
427
427
|
}
|
|
428
428
|
});
|
|
429
429
|
|
|
430
|
+
// ─── Test (i): Sketch slice stub tasks are not seeded (#1286) ─────────
|
|
431
|
+
|
|
432
|
+
test('migrate-hier: sketch slice stub PLAN tasks are not imported', () => {
|
|
433
|
+
const base = createFixtureBase();
|
|
434
|
+
try {
|
|
435
|
+
// S01 is a sketch (badge before risk, per ADR-011 render); its PLAN is a
|
|
436
|
+
// stub with a placeholder <tasks> block. S02 is a real, refined slice.
|
|
437
|
+
const roadmap = `# M001: Test Milestone
|
|
438
|
+
|
|
439
|
+
**Vision:** Sketch slices must not seed DB tasks.
|
|
440
|
+
|
|
441
|
+
## Slices
|
|
442
|
+
|
|
443
|
+
- [ ] **S01: Sketch Slice** \`[sketch]\` \`risk:low\` \`depends:[]\`
|
|
444
|
+
> After this: Sketch decomposed.
|
|
445
|
+
|
|
446
|
+
- [ ] **S02: Real Slice** \`risk:low\` \`depends:[]\`
|
|
447
|
+
> After this: Real work done.
|
|
448
|
+
`;
|
|
449
|
+
const sketchPlan = `# S01: Sketch Slice
|
|
450
|
+
|
|
451
|
+
**Goal:** Placeholder.
|
|
452
|
+
|
|
453
|
+
## Tasks
|
|
454
|
+
|
|
455
|
+
- [ ] **T01: Plan 01**
|
|
456
|
+
Stub placeholder task.
|
|
457
|
+
`;
|
|
458
|
+
writeFile(base, 'milestones/M001/M001-ROADMAP.md', roadmap);
|
|
459
|
+
writeFile(base, 'milestones/M001/slices/S01/S01-PLAN.md', sketchPlan);
|
|
460
|
+
writeFile(base, 'milestones/M001/slices/S02/S02-PLAN.md', PLAN_S02_1_TASK);
|
|
461
|
+
|
|
462
|
+
openDatabase(':memory:');
|
|
463
|
+
const counts = migrateHierarchyToDb(base);
|
|
464
|
+
|
|
465
|
+
const slices = getMilestoneSlices('M001');
|
|
466
|
+
assert.deepStrictEqual(slices.length, 2, 'sketch: 2 slices inserted');
|
|
467
|
+
assert.deepStrictEqual(slices[0]!.is_sketch, 1, 'sketch: S01 preserves is_sketch flag');
|
|
468
|
+
|
|
469
|
+
const s01Tasks = getSliceTasks('M001', 'S01');
|
|
470
|
+
assert.deepStrictEqual(s01Tasks.length, 0, 'sketch: no stub tasks seeded for sketch slice S01');
|
|
471
|
+
|
|
472
|
+
const s02Tasks = getSliceTasks('M001', 'S02');
|
|
473
|
+
assert.deepStrictEqual(s02Tasks.length, 1, 'sketch: real slice S02 still imports its task');
|
|
474
|
+
assert.deepStrictEqual(counts.tasks, 1, 'sketch: only the non-sketch slice contributes tasks');
|
|
475
|
+
|
|
476
|
+
closeDatabase();
|
|
477
|
+
} finally {
|
|
478
|
+
closeDatabase();
|
|
479
|
+
cleanup(base);
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
|
|
7
|
+
import { createMigrationPlan, splitValidationIssues } from "../migrate/plan.ts";
|
|
8
|
+
import type { ValidationResult } from "../migrate/types.ts";
|
|
9
|
+
|
|
10
|
+
function makeBase(prefix: string): string {
|
|
11
|
+
return mkdtempSync(join(tmpdir(), prefix));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function write(path: string, content: string): void {
|
|
15
|
+
mkdirSync(join(path, ".."), { recursive: true });
|
|
16
|
+
writeFileSync(path, content);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
test("splitValidationIssues separates warnings from fatals without dropping issue order", () => {
|
|
20
|
+
const validation: ValidationResult = {
|
|
21
|
+
valid: false,
|
|
22
|
+
issues: [
|
|
23
|
+
{ file: "ROADMAP.md", severity: "warning", message: "missing roadmap" },
|
|
24
|
+
{ file: ".planning", severity: "fatal", message: "missing directory" },
|
|
25
|
+
{ file: "PROJECT.md", severity: "warning", message: "missing project" },
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
assert.deepEqual(splitValidationIssues(validation), {
|
|
30
|
+
warnings: [
|
|
31
|
+
{ file: "ROADMAP.md", severity: "warning", message: "missing roadmap" },
|
|
32
|
+
{ file: "PROJECT.md", severity: "warning", message: "missing project" },
|
|
33
|
+
],
|
|
34
|
+
fatals: [
|
|
35
|
+
{ file: ".planning", severity: "fatal", message: "missing directory" },
|
|
36
|
+
],
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("createMigrationPlan reports a missing .planning source without parsing", async () => {
|
|
41
|
+
const base = makeBase("gsd-migrate-plan-missing-");
|
|
42
|
+
try {
|
|
43
|
+
const result = await createMigrationPlan(base);
|
|
44
|
+
|
|
45
|
+
assert.equal(result.status, "missing-source");
|
|
46
|
+
assert.equal(result.sourcePath, join(base, ".planning"));
|
|
47
|
+
assert.equal(result.targetRoot, base);
|
|
48
|
+
} finally {
|
|
49
|
+
rmSync(base, { recursive: true, force: true });
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("createMigrationPlan reports zero-slice migrations as blocked after validation", async () => {
|
|
54
|
+
const base = makeBase("gsd-migrate-plan-blocked-");
|
|
55
|
+
try {
|
|
56
|
+
mkdirSync(join(base, ".planning"), { recursive: true });
|
|
57
|
+
write(join(base, ".planning", "PROJECT.md"), "# Legacy Project\n");
|
|
58
|
+
|
|
59
|
+
const result = await createMigrationPlan(base);
|
|
60
|
+
|
|
61
|
+
assert.equal(result.status, "blocked");
|
|
62
|
+
assert.match(result.message, /zero slices/);
|
|
63
|
+
assert.equal(result.validation.valid, true);
|
|
64
|
+
assert.ok(result.warnings.some((issue) => issue.file === "ROADMAP.md"));
|
|
65
|
+
} finally {
|
|
66
|
+
rmSync(base, { recursive: true, force: true });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
buildMigrationPreviewSummary,
|
|
9
|
+
buildReviewPrompt,
|
|
10
|
+
formatMigrationPreviewStats,
|
|
11
|
+
} from "../migrate/presentation.ts";
|
|
12
|
+
import { gsdRoot } from "../paths.ts";
|
|
13
|
+
import type { MigrationPreview } from "../migrate/writer.ts";
|
|
14
|
+
|
|
15
|
+
function previewFixture(overrides: Partial<MigrationPreview> = {}): MigrationPreview {
|
|
16
|
+
return {
|
|
17
|
+
decisions: { total: 2 },
|
|
18
|
+
migrationInputs: {
|
|
19
|
+
milestonePhaseDirs: 3,
|
|
20
|
+
decisions: 2,
|
|
21
|
+
seeds: 1,
|
|
22
|
+
},
|
|
23
|
+
milestoneCount: 1,
|
|
24
|
+
totalSlices: 2,
|
|
25
|
+
totalTasks: 5,
|
|
26
|
+
doneSlices: 1,
|
|
27
|
+
doneTasks: 3,
|
|
28
|
+
sliceCompletionPct: 50,
|
|
29
|
+
taskCompletionPct: 60,
|
|
30
|
+
requirements: {
|
|
31
|
+
active: 1,
|
|
32
|
+
validated: 2,
|
|
33
|
+
deferred: 1,
|
|
34
|
+
outOfScope: 1,
|
|
35
|
+
total: 5,
|
|
36
|
+
},
|
|
37
|
+
...overrides,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
test("buildMigrationPreviewSummary includes counts, legacy inputs, and existing-target warning", () => {
|
|
42
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-migrate-presentation-"));
|
|
43
|
+
try {
|
|
44
|
+
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
45
|
+
|
|
46
|
+
assert.deepEqual(buildMigrationPreviewSummary(previewFixture(), base), [
|
|
47
|
+
"Decisions: 2",
|
|
48
|
+
"Milestones: 1",
|
|
49
|
+
"Slices: 2 (1 done — 50%)",
|
|
50
|
+
"Tasks: 5 (3 done — 60%)",
|
|
51
|
+
"Requirements: 5 (2 validated, 1 active, 1 deferred, 1 out of scope)",
|
|
52
|
+
"Legacy inputs: 3 milestone phase dir(s), 2 decision file(s), 1 seed file(s)",
|
|
53
|
+
"",
|
|
54
|
+
`⚠ A .gsd directory already exists at ${gsdRoot(base)}.`,
|
|
55
|
+
"It will be backed up, deleted, and rewritten fresh before DB import.",
|
|
56
|
+
]);
|
|
57
|
+
} finally {
|
|
58
|
+
rmSync(base, { recursive: true, force: true });
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("formatMigrationPreviewStats uses bullet formatting for review prompts", () => {
|
|
63
|
+
assert.equal(
|
|
64
|
+
formatMigrationPreviewStats(previewFixture({ requirements: { active: 0, validated: 0, deferred: 0, outOfScope: 0, total: 0 } })),
|
|
65
|
+
[
|
|
66
|
+
"- Decisions: 2",
|
|
67
|
+
"- Milestones: 1",
|
|
68
|
+
"- Slices: 2 (1 done — 50%)",
|
|
69
|
+
"- Tasks: 5 (3 done — 60%)",
|
|
70
|
+
"- Legacy inputs: 3 milestone phase dir(s), 2 decision file(s), 1 seed file(s)",
|
|
71
|
+
].join("\n"),
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("buildReviewPrompt interpolates source, target, and preview stats", () => {
|
|
76
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-migrate-prompt-"));
|
|
77
|
+
try {
|
|
78
|
+
const promptsDir = join(base, "prompts");
|
|
79
|
+
mkdirSync(promptsDir, { recursive: true });
|
|
80
|
+
writeFileSync(
|
|
81
|
+
join(promptsDir, "review-migration.md"),
|
|
82
|
+
"Source={{sourcePath}}\nTarget={{gsdPath}}\nStats:\n{{previewStats}}\n",
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const prompt = buildReviewPrompt({
|
|
86
|
+
sourcePath: "/legacy/.planning",
|
|
87
|
+
gsdPath: "/project/.gsd",
|
|
88
|
+
preview: previewFixture(),
|
|
89
|
+
templatePath: join(promptsDir, "review-migration.md"),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
assert.match(prompt, /Source=\/legacy\/\.planning/);
|
|
93
|
+
assert.match(prompt, /Target=\/project\/\.gsd/);
|
|
94
|
+
assert.match(prompt, /- Requirements: 5 \(2 validated, 1 active, 1 deferred, 1 out of scope\)/);
|
|
95
|
+
} finally {
|
|
96
|
+
rmSync(base, { recursive: true, force: true });
|
|
97
|
+
}
|
|
98
|
+
});
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
archiveLegacyPlanningDirectory,
|
|
20
20
|
verifyMigrationProjection,
|
|
21
21
|
} from "../migrate/audit.ts";
|
|
22
|
-
import { assertMigrationDbReadiness, executeMigrationWrite, importWrittenMigrationToDb } from "../migrate/
|
|
22
|
+
import { assertMigrationDbReadiness, executeMigrationWrite, importWrittenMigrationToDb } from "../migrate/execution.ts";
|
|
23
23
|
import { writeGSDDirectory } from "../migrate/writer.ts";
|
|
24
24
|
import { closeDatabase, getArtifact } from "../gsd-db.ts";
|
|
25
25
|
import type { GSDProject } from "../migrate/types.ts";
|
|
@@ -420,6 +420,59 @@ test("migration auto-check still reports real drift with scratch dirs excluded f
|
|
|
420
420
|
}
|
|
421
421
|
});
|
|
422
422
|
|
|
423
|
+
test("migration auto-check excludes sketch slice stub tasks from the markdown scan (#1286)", () => {
|
|
424
|
+
const base = makeBase();
|
|
425
|
+
try {
|
|
426
|
+
// S01 is a sketch (`[sketch]` badge): its PLAN is a stub carrying a
|
|
427
|
+
// placeholder <tasks> block until refinement. migrateHierarchyToDb skips
|
|
428
|
+
// seeding those stub tasks, so the markdown scan must skip them too — else
|
|
429
|
+
// the stub inflates the markdown count and reports false drift against a DB
|
|
430
|
+
// that (correctly) holds zero tasks for the sketch slice.
|
|
431
|
+
const roadmap = `# M001: Test Milestone
|
|
432
|
+
|
|
433
|
+
**Vision:** Sketch slices must not inflate markdown task counts.
|
|
434
|
+
|
|
435
|
+
## Slices
|
|
436
|
+
|
|
437
|
+
- [ ] **S01: Sketch Slice** \`[sketch]\` \`risk:low\` \`depends:[]\`
|
|
438
|
+
> After this: Sketch decomposed.
|
|
439
|
+
|
|
440
|
+
- [ ] **S02: Real Slice** \`risk:low\` \`depends:[]\`
|
|
441
|
+
> After this: Real work done.
|
|
442
|
+
`;
|
|
443
|
+
const sketchPlan = `# S01: Sketch Slice
|
|
444
|
+
|
|
445
|
+
**Goal:** Placeholder.
|
|
446
|
+
|
|
447
|
+
## Tasks
|
|
448
|
+
|
|
449
|
+
- [ ] **T01: Plan 01**
|
|
450
|
+
Stub placeholder task.
|
|
451
|
+
`;
|
|
452
|
+
const realPlan = `# S02: Real Slice
|
|
453
|
+
|
|
454
|
+
**Goal:** Real work.
|
|
455
|
+
|
|
456
|
+
## Tasks
|
|
457
|
+
|
|
458
|
+
- [ ] **T01: Real Task**
|
|
459
|
+
Real work.
|
|
460
|
+
`;
|
|
461
|
+
const milestoneDir = join(base, ".gsd", "milestones", "M001");
|
|
462
|
+
mkdirSync(join(milestoneDir, "slices", "S01"), { recursive: true });
|
|
463
|
+
mkdirSync(join(milestoneDir, "slices", "S02"), { recursive: true });
|
|
464
|
+
writeFileSync(join(milestoneDir, "M001-ROADMAP.md"), roadmap);
|
|
465
|
+
writeFileSync(join(milestoneDir, "slices", "S01", "S01-PLAN.md"), sketchPlan);
|
|
466
|
+
writeFileSync(join(milestoneDir, "slices", "S02", "S02-PLAN.md"), realPlan);
|
|
467
|
+
|
|
468
|
+
// Both slices count, but only the refined slice contributes a task; the
|
|
469
|
+
// sketch stub task is excluded, matching migrateHierarchyToDb.
|
|
470
|
+
assert.deepEqual(countMarkdownHierarchy(base), { milestones: 1, slices: 2, tasks: 1 });
|
|
471
|
+
} finally {
|
|
472
|
+
cleanup(base);
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
|
|
423
476
|
test("migration auto-check still compares a roadmapless milestone that HAS a DB row", async () => {
|
|
424
477
|
const base = makeBase();
|
|
425
478
|
try {
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
releaseMilestoneLease,
|
|
19
19
|
refreshMilestoneLease,
|
|
20
20
|
getMilestoneLease,
|
|
21
|
+
forceReleaseLeasesForWorker,
|
|
21
22
|
} from "../db/milestone-leases.ts";
|
|
22
23
|
|
|
23
24
|
function makeBase(): string {
|
|
@@ -163,6 +164,60 @@ test("refreshMilestoneLease only succeeds with the matching fencing token", (t)
|
|
|
163
164
|
assert.equal(refreshMilestoneLease(w1, "M001", claim.token - 1), false);
|
|
164
165
|
});
|
|
165
166
|
|
|
167
|
+
test("forceReleaseLeasesForWorker frees only the dead worker's leases, not a live peer's", (t) => {
|
|
168
|
+
const base = makeBase();
|
|
169
|
+
t.after(() => cleanup(base));
|
|
170
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
171
|
+
insertMilestone({ id: "M001", title: "One", status: "active" });
|
|
172
|
+
insertMilestone({ id: "M002", title: "Two", status: "active" });
|
|
173
|
+
|
|
174
|
+
// Distinct project roots → distinct processes (not re-entrant peers).
|
|
175
|
+
const wA = registerAutoWorker({ projectRootRealpath: base });
|
|
176
|
+
const wB = registerAutoWorker({ projectRootRealpath: join(base, "peer") });
|
|
177
|
+
assert.equal(claimMilestoneLease(wA, "M001").ok, true);
|
|
178
|
+
assert.equal(claimMilestoneLease(wB, "M002").ok, true);
|
|
179
|
+
|
|
180
|
+
const freed = forceReleaseLeasesForWorker(wA);
|
|
181
|
+
assert.equal(freed, 1, "exactly A's one held lease is released");
|
|
182
|
+
|
|
183
|
+
assert.equal(getMilestoneLease("M001")!.status, "released", "A's M001 lease is released");
|
|
184
|
+
const m2 = getMilestoneLease("M002")!;
|
|
185
|
+
assert.equal(m2.status, "held", "B's M002 lease must stay held (no over-release)");
|
|
186
|
+
assert.equal(m2.worker_id, wB);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("after force release a fresh claim on the freed milestone gets a larger fencing token", (t) => {
|
|
190
|
+
const base = makeBase();
|
|
191
|
+
t.after(() => cleanup(base));
|
|
192
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
193
|
+
insertMilestone({ id: "M001", title: "One", status: "active" });
|
|
194
|
+
|
|
195
|
+
const wA = registerAutoWorker({ projectRootRealpath: base });
|
|
196
|
+
const wB = registerAutoWorker({ projectRootRealpath: join(base, "peer") });
|
|
197
|
+
const first = claimMilestoneLease(wA, "M001");
|
|
198
|
+
assert.equal(first.ok, true);
|
|
199
|
+
const releasedToken = first.ok ? first.token : 0;
|
|
200
|
+
|
|
201
|
+
assert.equal(forceReleaseLeasesForWorker(wA), 1);
|
|
202
|
+
|
|
203
|
+
const takeover = claimMilestoneLease(wB, "M001");
|
|
204
|
+
assert.equal(takeover.ok, true);
|
|
205
|
+
if (takeover.ok) {
|
|
206
|
+
assert.ok(takeover.token > releasedToken, "takeover token must exceed the released lease's token");
|
|
207
|
+
}
|
|
208
|
+
assert.equal(getMilestoneLease("M001")!.worker_id, wB);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("forceReleaseLeasesForWorker returns 0 for a worker holding no leases", (t) => {
|
|
212
|
+
const base = makeBase();
|
|
213
|
+
t.after(() => cleanup(base));
|
|
214
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
215
|
+
insertMilestone({ id: "M001", title: "One", status: "active" });
|
|
216
|
+
|
|
217
|
+
const wA = registerAutoWorker({ projectRootRealpath: base });
|
|
218
|
+
assert.equal(forceReleaseLeasesForWorker(wA), 0);
|
|
219
|
+
});
|
|
220
|
+
|
|
166
221
|
test("claimMilestoneLease rethrows foreign-key failures instead of treating them as lease contention", (t) => {
|
|
167
222
|
const base = makeBase();
|
|
168
223
|
t.after(() => cleanup(base));
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression tests for batched touched-file collection in parallel eligibility.
|
|
3
|
+
*
|
|
4
|
+
* `collectTouchedFiles` used to run one `getSliceTasks` query per slice (N+1);
|
|
5
|
+
* it now issues a single `getTasksBySliceIds` batch and dedups paths into a
|
|
6
|
+
* Set. These tests pin the observable contract of that path through the public
|
|
7
|
+
* `analyzeParallelEligibility` API:
|
|
8
|
+
* 1. files are collected from EVERY slice of a milestone, not just the first
|
|
9
|
+
* (a file present only in a non-first slice must still drive overlap), and
|
|
10
|
+
* 2. a path repeated across a milestone's slices is deduped (Set semantics),
|
|
11
|
+
* so it appears once in the cross-milestone overlap.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
15
|
+
import assert from "node:assert/strict";
|
|
16
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import { tmpdir } from "node:os";
|
|
19
|
+
|
|
20
|
+
import { analyzeParallelEligibility } from "../parallel-eligibility.ts";
|
|
21
|
+
import { invalidateStateCache } from "../state.ts";
|
|
22
|
+
import {
|
|
23
|
+
openDatabase,
|
|
24
|
+
closeDatabase,
|
|
25
|
+
insertMilestone,
|
|
26
|
+
insertSlice,
|
|
27
|
+
insertTask,
|
|
28
|
+
} from "../gsd-db.ts";
|
|
29
|
+
|
|
30
|
+
// ─── Fixture Helpers ───────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
function createFixtureBase(): string {
|
|
33
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-parallel-elig-overlap-"));
|
|
34
|
+
mkdirSync(join(base, ".gsd", "milestones"), { recursive: true });
|
|
35
|
+
return base;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function writeMilestoneFile(base: string, milestoneId: string, filename: string, content: string): void {
|
|
39
|
+
const filePath = join(base, ".gsd", "milestones", milestoneId, filename);
|
|
40
|
+
mkdirSync(join(filePath, ".."), { recursive: true });
|
|
41
|
+
writeFileSync(filePath, content);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Write the on-disk planning artifacts that make a milestone eligible
|
|
46
|
+
* (CONTEXT + ROADMAP listing its slices + a PLAN per slice). Mirrors the
|
|
47
|
+
* proven-eligible setup used by parallel-eligibility-ghost.test.ts.
|
|
48
|
+
*/
|
|
49
|
+
function writeEligibleMilestoneFiles(base: string, milestoneId: string, sliceIds: string[]): void {
|
|
50
|
+
writeMilestoneFile(base, milestoneId, `${milestoneId}-CONTEXT.md`, `# ${milestoneId}: Fixture\n\nBatch fixture.`);
|
|
51
|
+
const roadmapSlices = sliceIds
|
|
52
|
+
.map((sid) => `- [ ] **${sid}: Slice ${sid}** \`risk:low\` \`depends:[]\`\n > Slice ${sid}.`)
|
|
53
|
+
.join("\n");
|
|
54
|
+
writeMilestoneFile(
|
|
55
|
+
base,
|
|
56
|
+
milestoneId,
|
|
57
|
+
`${milestoneId}-ROADMAP.md`,
|
|
58
|
+
`# ${milestoneId}: Fixture\n\n## Slices\n\n${roadmapSlices}\n`,
|
|
59
|
+
);
|
|
60
|
+
for (const sid of sliceIds) {
|
|
61
|
+
writeMilestoneFile(
|
|
62
|
+
base,
|
|
63
|
+
milestoneId,
|
|
64
|
+
`slices/${sid}/${sid}-PLAN.md`,
|
|
65
|
+
`# ${sid}: Slice ${sid}\n\n**Goal:** Do ${sid}.\n**Demo:** Done.\n\n## Tasks\n\n- [ ] **T01: Task** \`est:10m\`\n Do it.\n`,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function cleanup(base: string): void {
|
|
71
|
+
rmSync(base, { recursive: true, force: true });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ─── Tests ─────────────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
describe("parallel-eligibility: batched touched-file collection", () => {
|
|
77
|
+
let base: string;
|
|
78
|
+
|
|
79
|
+
beforeEach(() => {
|
|
80
|
+
base = createFixtureBase();
|
|
81
|
+
openDatabase(":memory:");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
afterEach(() => {
|
|
85
|
+
closeDatabase();
|
|
86
|
+
cleanup(base);
|
|
87
|
+
invalidateStateCache();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("collects files from every slice (batch) and dedups repeated paths", async () => {
|
|
91
|
+
// M001 spans two slices. `src/shared.ts` appears in BOTH slices (dedup
|
|
92
|
+
// target); `src/beta.ts` appears ONLY in the second slice S02 (proves the
|
|
93
|
+
// batch covers non-first slices). `src/alpha.ts` is unique to M001.
|
|
94
|
+
writeEligibleMilestoneFiles(base, "M001", ["S01", "S02"]);
|
|
95
|
+
insertMilestone({ id: "M001", title: "M001: Fixture", status: "active" });
|
|
96
|
+
insertSlice({ id: "S01", milestoneId: "M001", title: "Slice S01", status: "active", risk: "low", depends: [], sequence: 1 });
|
|
97
|
+
insertSlice({ id: "S02", milestoneId: "M001", title: "Slice S02", status: "active", risk: "low", depends: [], sequence: 2 });
|
|
98
|
+
insertTask({
|
|
99
|
+
id: "T01", sliceId: "S01", milestoneId: "M001", title: "S01 task", status: "pending",
|
|
100
|
+
planning: { files: ["src/alpha.ts", "src/shared.ts"] },
|
|
101
|
+
});
|
|
102
|
+
insertTask({
|
|
103
|
+
id: "T01", sliceId: "S02", milestoneId: "M001", title: "S02 task", status: "pending",
|
|
104
|
+
planning: { files: ["src/shared.ts", "src/beta.ts"] },
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// M002 overlaps M001 on both `src/shared.ts` and `src/beta.ts`.
|
|
108
|
+
writeEligibleMilestoneFiles(base, "M002", ["S01"]);
|
|
109
|
+
insertMilestone({ id: "M002", title: "M002: Fixture", status: "active" });
|
|
110
|
+
insertSlice({ id: "S01", milestoneId: "M002", title: "Slice S01", status: "active", risk: "low", depends: [], sequence: 1 });
|
|
111
|
+
insertTask({
|
|
112
|
+
id: "T01", sliceId: "S01", milestoneId: "M002", title: "S01 task", status: "pending",
|
|
113
|
+
planning: { files: ["src/shared.ts", "src/beta.ts"] },
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
invalidateStateCache();
|
|
117
|
+
const result = await analyzeParallelEligibility(base);
|
|
118
|
+
|
|
119
|
+
// Both milestones must be eligible so Rule 3 (file overlap) runs on them.
|
|
120
|
+
assert.ok(result.eligible.find((e) => e.milestoneId === "M001"), "M001 should be eligible");
|
|
121
|
+
assert.ok(result.eligible.find((e) => e.milestoneId === "M002"), "M002 should be eligible");
|
|
122
|
+
|
|
123
|
+
const overlap = result.fileOverlaps.find(
|
|
124
|
+
(o) =>
|
|
125
|
+
(o.mid1 === "M001" && o.mid2 === "M002") ||
|
|
126
|
+
(o.mid1 === "M002" && o.mid2 === "M001"),
|
|
127
|
+
);
|
|
128
|
+
assert.ok(overlap, "M001 and M002 must be reported as overlapping");
|
|
129
|
+
|
|
130
|
+
// `src/beta.ts` lives only in M001's SECOND slice — its presence proves the
|
|
131
|
+
// batched query collected tasks from every slice, not just the first.
|
|
132
|
+
assert.ok(
|
|
133
|
+
overlap!.files.includes("src/beta.ts"),
|
|
134
|
+
"overlap must include src/beta.ts (only present in M001's non-first slice S02)",
|
|
135
|
+
);
|
|
136
|
+
assert.ok(overlap!.files.includes("src/shared.ts"), "overlap must include src/shared.ts");
|
|
137
|
+
|
|
138
|
+
// `src/shared.ts` appears in both of M001's slices but must be deduped to a
|
|
139
|
+
// single entry (Set collection), so the sorted overlap is exactly these two.
|
|
140
|
+
assert.deepEqual(
|
|
141
|
+
[...overlap!.files].sort(),
|
|
142
|
+
["src/beta.ts", "src/shared.ts"],
|
|
143
|
+
"overlap files must be deduped and limited to the shared paths",
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
});
|