@opengsd/gsd-pi 1.8.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 -3
- 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 +5 -5
- 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 +5 -5
- 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 +5 -1
- package/packages/daemon/dist/cloud-config.js.map +1 -1
- package/packages/daemon/dist/cloud-config.test.js +51 -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/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 +3 -3
- 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/{seHIUVbR1mm7LfG83nWJO → TezjdjSHBVRRr54XjAvte}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{seHIUVbR1mm7LfG83nWJO → TezjdjSHBVRRr54XjAvte}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// GSD Extension - Migration guard tests (plan 026).
|
|
2
|
+
// Covers the newer-DB refusal in migrateSchema, whole-chain rollback atomicity
|
|
3
|
+
// via the _setMigrationFaultForTest seam, the legacy-data guard in initSchema,
|
|
4
|
+
// and getCurrentSchemaVersion's empty-table NULL handling.
|
|
5
|
+
|
|
6
|
+
import { describe, test } from 'node:test';
|
|
7
|
+
import assert from 'node:assert/strict';
|
|
8
|
+
import * as fs from 'node:fs';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
import * as os from 'node:os';
|
|
11
|
+
import { createRequire } from 'node:module';
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
openDatabase,
|
|
15
|
+
closeDatabase,
|
|
16
|
+
isDbAvailable,
|
|
17
|
+
SCHEMA_VERSION,
|
|
18
|
+
_setMigrationFaultForTest,
|
|
19
|
+
} from '../gsd-db.ts';
|
|
20
|
+
import { createDbAdapter } from '../db-adapter.ts';
|
|
21
|
+
import { getCurrentSchemaVersion } from '../db-schema-metadata.ts';
|
|
22
|
+
|
|
23
|
+
const _require = createRequire(import.meta.url);
|
|
24
|
+
|
|
25
|
+
function tempDbPath(): string {
|
|
26
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-migrate-guards-'));
|
|
27
|
+
return path.join(dir, 'gsd.db');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function cleanup(dbPath: string): void {
|
|
31
|
+
closeDatabase();
|
|
32
|
+
fs.rmSync(path.dirname(dbPath), { recursive: true, force: true });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface RawDb {
|
|
36
|
+
exec(sql: string): void;
|
|
37
|
+
prepare(sql: string): { get(...args: unknown[]): Record<string, unknown> | undefined; all(...args: unknown[]): Array<Record<string, unknown>> };
|
|
38
|
+
close(): void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function openRawSqliteForTest(dbPath: string): RawDb {
|
|
42
|
+
try {
|
|
43
|
+
const mod = _require('node:sqlite') as { DatabaseSync: new (path: string) => RawDb };
|
|
44
|
+
return new mod.DatabaseSync(dbPath);
|
|
45
|
+
} catch {
|
|
46
|
+
type SqliteCtor = new (path: string) => RawDb;
|
|
47
|
+
const mod = _require('better-sqlite3') as SqliteCtor | { default: SqliteCtor };
|
|
48
|
+
const DatabaseCtor: SqliteCtor = typeof mod === 'function' ? mod : mod.default;
|
|
49
|
+
return new DatabaseCtor(dbPath);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function rawColumnNames(raw: RawDb, table: string): string[] {
|
|
54
|
+
return raw.prepare(`PRAGMA table_info(${table})`).all().map((r) => r['name'] as string);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function rawHasTable(raw: RawDb, table: string): boolean {
|
|
58
|
+
const row = raw.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?").get(table);
|
|
59
|
+
return Boolean(row);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function rawMaxVersion(raw: RawDb): number | null {
|
|
63
|
+
const row = raw.prepare('SELECT MAX(version) as v FROM schema_version').get();
|
|
64
|
+
return (row?.['v'] as number | null) ?? null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
describe('db-engine migrate guards', () => {
|
|
68
|
+
test('getCurrentSchemaVersion returns 0 on an empty schema_version table', () => {
|
|
69
|
+
const sqlite = _require('node:sqlite') as { DatabaseSync: new (path: string) => unknown };
|
|
70
|
+
const adapter = createDbAdapter(new sqlite.DatabaseSync(':memory:'));
|
|
71
|
+
try {
|
|
72
|
+
adapter.exec('CREATE TABLE schema_version (version INTEGER NOT NULL, applied_at TEXT NOT NULL)');
|
|
73
|
+
assert.equal(getCurrentSchemaVersion(adapter), 0);
|
|
74
|
+
} finally {
|
|
75
|
+
adapter.close();
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('refuses to open a DB whose schema is newer than this binary', () => {
|
|
80
|
+
const dbPath = tempDbPath();
|
|
81
|
+
try {
|
|
82
|
+
assert.ok(openDatabase(dbPath), 'fresh open should succeed');
|
|
83
|
+
closeDatabase();
|
|
84
|
+
|
|
85
|
+
const raw = openRawSqliteForTest(dbPath);
|
|
86
|
+
raw.exec(
|
|
87
|
+
`INSERT INTO schema_version (version, applied_at) VALUES (${SCHEMA_VERSION + 1}, '2026-01-01T00:00:00Z')`,
|
|
88
|
+
);
|
|
89
|
+
raw.close();
|
|
90
|
+
|
|
91
|
+
assert.throws(() => openDatabase(dbPath), /newer than/);
|
|
92
|
+
assert.equal(isDbAvailable(), false, 'no connection may stay open on refusal');
|
|
93
|
+
} finally {
|
|
94
|
+
cleanup(dbPath);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('a mid-migration failure rolls back to the starting schema version', () => {
|
|
99
|
+
const dbPath = tempDbPath();
|
|
100
|
+
try {
|
|
101
|
+
assert.ok(openDatabase(dbPath), 'fresh open should succeed');
|
|
102
|
+
closeDatabase();
|
|
103
|
+
|
|
104
|
+
// Rewind the DB to v(SCHEMA_VERSION-1): stamp the prior version and drop
|
|
105
|
+
// the objects the last migration adds, so the re-run has real work to redo.
|
|
106
|
+
const startVersion = SCHEMA_VERSION - 2;
|
|
107
|
+
const raw = openRawSqliteForTest(dbPath);
|
|
108
|
+
raw.exec('DELETE FROM schema_version');
|
|
109
|
+
raw.exec(`INSERT INTO schema_version (version, applied_at) VALUES (${startVersion}, '2026-01-01T00:00:00Z')`);
|
|
110
|
+
raw.exec('ALTER TABLE slices DROP COLUMN target_repositories');
|
|
111
|
+
raw.exec('ALTER TABLE tasks DROP COLUMN target_repositories');
|
|
112
|
+
raw.close();
|
|
113
|
+
|
|
114
|
+
_setMigrationFaultForTest(true);
|
|
115
|
+
try {
|
|
116
|
+
assert.throws(() => openDatabase(dbPath), /migration fault injected/);
|
|
117
|
+
} finally {
|
|
118
|
+
_setMigrationFaultForTest(false);
|
|
119
|
+
}
|
|
120
|
+
assert.equal(isDbAvailable(), false, 'no connection may stay open on migration failure');
|
|
121
|
+
|
|
122
|
+
const inspect = openRawSqliteForTest(dbPath);
|
|
123
|
+
try {
|
|
124
|
+
assert.equal(rawMaxVersion(inspect), startVersion, 'version must roll back to the starting version');
|
|
125
|
+
assert.ok(
|
|
126
|
+
!rawColumnNames(inspect, 'slices').includes('target_repositories'),
|
|
127
|
+
'column added by the failed migration must be rolled back',
|
|
128
|
+
);
|
|
129
|
+
} finally {
|
|
130
|
+
inspect.close();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// The DB is intact — a clean re-open migrates to the current version.
|
|
134
|
+
assert.ok(openDatabase(dbPath), 're-open without the fault should succeed');
|
|
135
|
+
closeDatabase();
|
|
136
|
+
const after = openRawSqliteForTest(dbPath);
|
|
137
|
+
try {
|
|
138
|
+
assert.equal(rawMaxVersion(after), SCHEMA_VERSION);
|
|
139
|
+
assert.ok(rawColumnNames(after, 'slices').includes('target_repositories'));
|
|
140
|
+
assert.equal(rawHasTable(after, 'rework_briefs'), true);
|
|
141
|
+
assert.equal(rawHasTable(after, 'rework_brief_findings'), true);
|
|
142
|
+
} finally {
|
|
143
|
+
after.close();
|
|
144
|
+
}
|
|
145
|
+
} finally {
|
|
146
|
+
cleanup(dbPath);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('a DB with data but no version rows migrates from v1 instead of being stamped current', () => {
|
|
151
|
+
const dbPath = tempDbPath();
|
|
152
|
+
try {
|
|
153
|
+
assert.ok(openDatabase(dbPath), 'fresh open should succeed');
|
|
154
|
+
closeDatabase();
|
|
155
|
+
|
|
156
|
+
// Simulate a legacy DB: user data present, version table empty, and a
|
|
157
|
+
// migration-added column missing so we can observe the chain actually run.
|
|
158
|
+
const raw = openRawSqliteForTest(dbPath);
|
|
159
|
+
raw.exec("INSERT INTO milestones (id, title) VALUES ('M001', 'Legacy milestone')");
|
|
160
|
+
raw.exec('DELETE FROM schema_version');
|
|
161
|
+
raw.exec('ALTER TABLE milestones DROP COLUMN vision');
|
|
162
|
+
raw.close();
|
|
163
|
+
|
|
164
|
+
assert.ok(openDatabase(dbPath), 'legacy DB open should succeed');
|
|
165
|
+
closeDatabase();
|
|
166
|
+
|
|
167
|
+
const inspect = openRawSqliteForTest(dbPath);
|
|
168
|
+
try {
|
|
169
|
+
const versions = inspect.prepare('SELECT version FROM schema_version ORDER BY version').all()
|
|
170
|
+
.map((r) => r['version'] as number);
|
|
171
|
+
assert.equal(versions[0], 1, 'baseline v1 must be recorded, not a fresh-install stamp');
|
|
172
|
+
assert.equal(versions[versions.length - 1], SCHEMA_VERSION, 'migration chain must reach the current version');
|
|
173
|
+
assert.ok(
|
|
174
|
+
rawColumnNames(inspect, 'milestones').includes('vision'),
|
|
175
|
+
'migration-added column must be restored by the chain',
|
|
176
|
+
);
|
|
177
|
+
const row = inspect.prepare('SELECT count(*) as cnt FROM milestones').get();
|
|
178
|
+
assert.equal(row?.['cnt'], 1, 'existing data must survive');
|
|
179
|
+
} finally {
|
|
180
|
+
inspect.close();
|
|
181
|
+
}
|
|
182
|
+
} finally {
|
|
183
|
+
cleanup(dbPath);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
});
|
|
@@ -7,6 +7,8 @@ import { join } from 'node:path';
|
|
|
7
7
|
import { tmpdir } from 'node:os';
|
|
8
8
|
|
|
9
9
|
import { deriveState, invalidateStateCache, _deriveStateImpl, deriveStateFromDb, isGhostMilestone } from '../state.ts';
|
|
10
|
+
import { readCachedDeriveState, writeCachedDeriveState } from '../state/derive/cache.ts';
|
|
11
|
+
import type { GSDState } from '../types.ts';
|
|
10
12
|
import {
|
|
11
13
|
openDatabase,
|
|
12
14
|
closeDatabase,
|
|
@@ -582,6 +584,33 @@ describe('derive-state-db', async () => {
|
|
|
582
584
|
}
|
|
583
585
|
});
|
|
584
586
|
|
|
587
|
+
// ─── Test 7b: lock flip must not serve a basePath-keyed cache hit ─────
|
|
588
|
+
// The derive cache is keyed on basePath only, but deriveStateFromDb filters
|
|
589
|
+
// by GSD_MILESTONE_LOCK. auto.ts's capture/restoreMilestoneLockEnv call
|
|
590
|
+
// invalidateStateCache() on every flip; this guards that the cache is
|
|
591
|
+
// genuinely lock-blind (so that invalidate call is load-bearing, not
|
|
592
|
+
// redundant) and that invalidation defeats the stale read.
|
|
593
|
+
test('derive-state-db: a milestone-lock flip needs explicit cache invalidation', () => {
|
|
594
|
+
const prevLock = process.env.GSD_MILESTONE_LOCK;
|
|
595
|
+
try {
|
|
596
|
+
const base = '/tmp/gsd-lock-flip-fixture';
|
|
597
|
+
const stateA = { phase: 'planning' } as unknown as GSDState;
|
|
598
|
+
process.env.GSD_MILESTONE_LOCK = 'M001';
|
|
599
|
+
writeCachedDeriveState(base, stateA);
|
|
600
|
+
|
|
601
|
+
// Lock flips but basePath is unchanged → the cache still hits.
|
|
602
|
+
process.env.GSD_MILESTONE_LOCK = 'M002';
|
|
603
|
+
assert.strictEqual(readCachedDeriveState(base), stateA, 'basePath-only cache ignores the lock flip');
|
|
604
|
+
|
|
605
|
+
// What the lock-env functions do on flip:
|
|
606
|
+
invalidateStateCache();
|
|
607
|
+
assert.strictEqual(readCachedDeriveState(base), null, 'invalidation defeats the stale-lock read');
|
|
608
|
+
} finally {
|
|
609
|
+
if (prevLock === undefined) delete process.env.GSD_MILESTONE_LOCK;
|
|
610
|
+
else process.env.GSD_MILESTONE_LOCK = prevLock;
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
|
|
585
614
|
// ═════════════════════════════════════════════════════════════════════════
|
|
586
615
|
// New: deriveStateFromDb() cross-validation tests
|
|
587
616
|
// ═════════════════════════════════════════════════════════════════════════
|
|
@@ -80,6 +80,43 @@ function makeDiscussCtx(notifications: Array<{ message: string; level?: string }
|
|
|
80
80
|
};
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
async function runDiscussTargetFixture(
|
|
84
|
+
target: string,
|
|
85
|
+
milestones: Array<{ id: string; title?: string; status?: string }>,
|
|
86
|
+
writeArtifacts?: (base: string) => void,
|
|
87
|
+
) {
|
|
88
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-discuss-target-"));
|
|
89
|
+
const notifications: Array<{ message: string; level?: string }> = [];
|
|
90
|
+
const harness = makeDiscussPi();
|
|
91
|
+
try {
|
|
92
|
+
mkdirSync(join(base, ".gsd", "milestones"), { recursive: true });
|
|
93
|
+
writeArtifacts?.(base);
|
|
94
|
+
const dbPath = join(base, ".gsd", "gsd.db");
|
|
95
|
+
assert.equal(openDatabase(dbPath), true);
|
|
96
|
+
for (const milestone of milestones) {
|
|
97
|
+
insertMilestone(milestone);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
await showDiscuss(
|
|
101
|
+
makeDiscussCtx(notifications) as any,
|
|
102
|
+
harness.pi as any,
|
|
103
|
+
base,
|
|
104
|
+
{ target },
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
notifications: [...notifications],
|
|
109
|
+
sent: [...harness.sent],
|
|
110
|
+
};
|
|
111
|
+
} finally {
|
|
112
|
+
harness.restore();
|
|
113
|
+
if (isDbAvailable()) closeDatabase();
|
|
114
|
+
invalidateStateCache();
|
|
115
|
+
clearGuidedUnitContext();
|
|
116
|
+
rmSync(base, { recursive: true, force: true });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
83
120
|
describe("discuss target normalization", () => {
|
|
84
121
|
test("canonicalizes milestone and slice casing", () => {
|
|
85
122
|
assert.equal(normalizeDiscussTarget("m014"), "M014");
|
|
@@ -90,6 +127,92 @@ describe("discuss target normalization", () => {
|
|
|
90
127
|
});
|
|
91
128
|
});
|
|
92
129
|
|
|
130
|
+
describe("showDiscuss targeted milestone guardrails (#1320)", () => {
|
|
131
|
+
test("missing, complete, and parked milestone targets get actionable messages", async () => {
|
|
132
|
+
const cases = [
|
|
133
|
+
{
|
|
134
|
+
target: "M006",
|
|
135
|
+
milestones: [{ id: "M005", title: "Current", status: "active" }],
|
|
136
|
+
level: "warning",
|
|
137
|
+
message: /Milestone M006 was not found in the roadmap\. Use \/gsd new-milestone to add it, or \/gsd status to see available milestones\./,
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
target: "M006/S01",
|
|
141
|
+
milestones: [{ id: "M005", title: "Current", status: "active" }],
|
|
142
|
+
level: "warning",
|
|
143
|
+
message: /Milestone M006 was not found in the roadmap\. Use \/gsd new-milestone to add it, or \/gsd status to see available milestones\./,
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
target: "M006",
|
|
147
|
+
milestones: [{ id: "M006", title: "Done", status: "complete" }],
|
|
148
|
+
level: "info",
|
|
149
|
+
message: /Milestone M006 is already complete\./,
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
target: "M006/S01",
|
|
153
|
+
milestones: [{ id: "M006", title: "Done", status: "complete" }],
|
|
154
|
+
level: "info",
|
|
155
|
+
message: /Milestone M006 is already complete\./,
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
target: "M006",
|
|
159
|
+
milestones: [{ id: "M006", title: "Deferred", status: "parked" }],
|
|
160
|
+
level: "warning",
|
|
161
|
+
message: /Milestone M006 is parked\. Run \/gsd unpark M006 to reactivate\./,
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
target: "M006/S01",
|
|
165
|
+
milestones: [{ id: "M006", title: "Deferred", status: "parked" }],
|
|
166
|
+
level: "warning",
|
|
167
|
+
message: /Milestone M006 is parked\. Run \/gsd unpark M006 to reactivate\./,
|
|
168
|
+
},
|
|
169
|
+
];
|
|
170
|
+
|
|
171
|
+
for (const testCase of cases) {
|
|
172
|
+
const result = await runDiscussTargetFixture(testCase.target, testCase.milestones);
|
|
173
|
+
assert.equal(result.sent.length, 0, `${testCase.target} must not dispatch`);
|
|
174
|
+
assert.equal(result.notifications.length, 1, `${testCase.target} must emit one notification`);
|
|
175
|
+
assert.equal(result.notifications[0]?.level, testCase.level);
|
|
176
|
+
assert.match(result.notifications[0]?.message ?? "", testCase.message);
|
|
177
|
+
assert.doesNotMatch(result.notifications[0]?.message ?? "", /not discussable/i);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("bare milestone targets match unique-suffix milestone IDs", async () => {
|
|
182
|
+
const result = await runDiscussTargetFixture("M006", [
|
|
183
|
+
{ id: "M006-abc123", title: "Unique milestone", status: "active" },
|
|
184
|
+
]);
|
|
185
|
+
|
|
186
|
+
assert.equal(result.notifications.length, 0);
|
|
187
|
+
assert.equal(result.sent.length, 1, "bare ID must dispatch the suffixed milestone");
|
|
188
|
+
assert.match(String(result.sent[0]?.content), /M006-abc123|Unique milestone|guided-discuss-milestone/i);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("bare slice targets match unique-suffix milestone IDs", async () => {
|
|
192
|
+
const result = await runDiscussTargetFixture(
|
|
193
|
+
"M006/S01",
|
|
194
|
+
[{ id: "M006-abc123", title: "Unique milestone", status: "active" }],
|
|
195
|
+
(base) => {
|
|
196
|
+
mkdirSync(join(base, ".gsd", "milestones", "M006-abc123"), { recursive: true });
|
|
197
|
+
writeFileSync(
|
|
198
|
+
join(base, ".gsd", "milestones", "M006-abc123", "M006-abc123-ROADMAP.md"),
|
|
199
|
+
`# M006-abc123 Roadmap
|
|
200
|
+
|
|
201
|
+
## Slices
|
|
202
|
+
- [ ] **S01: Unique slice** \`risk:medium\` \`depends:[]\`
|
|
203
|
+
> After this: the suffixed milestone slice can be discussed
|
|
204
|
+
`,
|
|
205
|
+
"utf-8",
|
|
206
|
+
);
|
|
207
|
+
},
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
assert.equal(result.notifications.length, 0);
|
|
211
|
+
assert.equal(result.sent.length, 1, "bare ID slice target must dispatch through the suffixed milestone");
|
|
212
|
+
assert.match(String(result.sent[0]?.content), /M006-abc123|S01|Unique slice|guided-discuss-slice/i);
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
93
216
|
describe("loadDiscussNormSlices roadmap fallback (#2892)", () => {
|
|
94
217
|
test("falls back to ROADMAP when DB has no slice rows", async () => {
|
|
95
218
|
const base = mkdtempSync(join(tmpdir(), "gsd-discuss-slices-"));
|
|
@@ -330,6 +330,56 @@ test("dispatch: session milestone mismatch stops before missing-task-plan recove
|
|
|
330
330
|
assert.match(result.reason, /context mid "M001" does not match session\.currentMilestoneId "M002"/);
|
|
331
331
|
});
|
|
332
332
|
|
|
333
|
+
test("dispatch: bare context milestone maps to suffixed active session milestone", async (t) => {
|
|
334
|
+
const tmp = mkdtempSync(join(tmpdir(), "gsd-suffixed-session-milestone-"));
|
|
335
|
+
t.after(() => rmSync(tmp, { recursive: true, force: true }));
|
|
336
|
+
|
|
337
|
+
const worktreeRoot = join(tmp, ".gsd", "worktrees", "M003-vaz73w");
|
|
338
|
+
scaffoldLegacyMilestoneContext(worktreeRoot, "M003-vaz73w");
|
|
339
|
+
scaffoldLegacySlicePlan(worktreeRoot, "M003-vaz73w", "S01");
|
|
340
|
+
scaffoldLegacyTaskPlan(worktreeRoot, "M003-vaz73w", "S01", "T02");
|
|
341
|
+
|
|
342
|
+
const ctx = makeContextFor(tmp, "M003", "S01", "T02", {
|
|
343
|
+
basePath: worktreeRoot,
|
|
344
|
+
originalBasePath: tmp,
|
|
345
|
+
currentMilestoneId: "M003-vaz73w",
|
|
346
|
+
});
|
|
347
|
+
const result = await resolveDispatch(ctx);
|
|
348
|
+
|
|
349
|
+
assert.equal(result.action, "dispatch");
|
|
350
|
+
assert.ok(result.action === "dispatch" && result.unitType === "execute-task",
|
|
351
|
+
`unitType should be execute-task, got: ${result.action === "dispatch" ? result.unitType : "(stop)"}`);
|
|
352
|
+
assert.ok(result.action === "dispatch" && result.unitId === "M003-vaz73w/S01/T02",
|
|
353
|
+
`unitId should use suffixed milestone, got: ${result.action === "dispatch" ? result.unitId : "(stop)"}`);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
test("dispatch: suffixed context milestone keeps suffix when scoped alias is bare", async (t) => {
|
|
357
|
+
// Inverse of the case above: dispatch context carries the suffixed id but the
|
|
358
|
+
// scoped alias (session.currentMilestoneId) is bare. resolveEffectiveDispatchMilestoneId
|
|
359
|
+
// must keep the suffixed id so slice/task artifacts resolve under the suffixed
|
|
360
|
+
// worktree layout (milestones/M003-xxxxxx/) rather than the bare milestones/M003/.
|
|
361
|
+
const tmp = mkdtempSync(join(tmpdir(), "gsd-suffixed-context-bare-scope-"));
|
|
362
|
+
t.after(() => rmSync(tmp, { recursive: true, force: true }));
|
|
363
|
+
|
|
364
|
+
const worktreeRoot = join(tmp, ".gsd", "worktrees", "M003-vaz73w");
|
|
365
|
+
scaffoldLegacyMilestoneContext(worktreeRoot, "M003-vaz73w");
|
|
366
|
+
scaffoldLegacySlicePlan(worktreeRoot, "M003-vaz73w", "S01");
|
|
367
|
+
scaffoldLegacyTaskPlan(worktreeRoot, "M003-vaz73w", "S01", "T02");
|
|
368
|
+
|
|
369
|
+
const ctx = makeContextFor(tmp, "M003-vaz73w", "S01", "T02", {
|
|
370
|
+
basePath: worktreeRoot,
|
|
371
|
+
originalBasePath: tmp,
|
|
372
|
+
currentMilestoneId: "M003",
|
|
373
|
+
});
|
|
374
|
+
const result = await resolveDispatch(ctx);
|
|
375
|
+
|
|
376
|
+
assert.equal(result.action, "dispatch");
|
|
377
|
+
assert.ok(result.action === "dispatch" && result.unitType === "execute-task",
|
|
378
|
+
`unitType should be execute-task, got: ${result.action === "dispatch" ? result.unitType : "(stop)"}`);
|
|
379
|
+
assert.ok(result.action === "dispatch" && result.unitId === "M003-vaz73w/S01/T02",
|
|
380
|
+
`unitId should keep suffixed milestone, got: ${result.action === "dispatch" ? result.unitId : "(stop)"}`);
|
|
381
|
+
});
|
|
382
|
+
|
|
333
383
|
test("dispatch: worktree path mismatch stops before planning a different milestone", async (t) => {
|
|
334
384
|
const tmp = mkdtempSync(join(tmpdir(), "gsd-worktree-path-milestone-mismatch-"));
|
|
335
385
|
t.after(() => rmSync(tmp, { recursive: true, force: true }));
|
|
@@ -146,6 +146,55 @@ test("dispatch-rule-coverage: pre-planning, no CONTEXT → discuss-milestone", a
|
|
|
146
146
|
);
|
|
147
147
|
});
|
|
148
148
|
|
|
149
|
+
test("dispatch-rule-coverage: pre-planning worktree sees project-root CONTEXT", async (t) => {
|
|
150
|
+
const tmp = mkdtempSync(join(tmpdir(), "gsd-disp-cov-wt-context-"));
|
|
151
|
+
t.after(() => rmSync(tmp, { recursive: true, force: true }));
|
|
152
|
+
|
|
153
|
+
const projectPhaseDir = join(tmp, ".gsd", "phases", "01-foundation");
|
|
154
|
+
mkdirSync(projectPhaseDir, { recursive: true });
|
|
155
|
+
writeFileSync(join(projectPhaseDir, "01-CONTEXT.md"), "# Context\n");
|
|
156
|
+
|
|
157
|
+
const worktree = join(tmp, ".gsd", "worktrees", "M001");
|
|
158
|
+
mkdirSync(join(worktree, ".gsd", "milestones", "M001"), { recursive: true });
|
|
159
|
+
writeFileSync(
|
|
160
|
+
join(worktree, ".gsd", "milestones", "M001", "M001-META.json"),
|
|
161
|
+
'{"branch":"milestone/M001"}',
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const rule = DISPATCH_RULES.find(
|
|
165
|
+
(candidate) => candidate.name === "pre-planning (no context) → discuss-milestone",
|
|
166
|
+
);
|
|
167
|
+
assert.ok(rule, "pre-planning missing-context rule should exist");
|
|
168
|
+
|
|
169
|
+
const result = await rule.match(makeCtx(worktree, makeState({ phase: "pre-planning" })));
|
|
170
|
+
assert.equal(result, null, "project-root CONTEXT must prevent a second discuss-milestone dispatch");
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("dispatch-rule-coverage: pre-planning worktree sees worktree-only CONTEXT", async (t) => {
|
|
174
|
+
// Mirror image of the project-root test: discuss-milestone mirrors CONTEXT into
|
|
175
|
+
// the worktree's own .gsd, and it may not yet exist at the project root (the
|
|
176
|
+
// worktree→root sync deliberately does not flow markdown projections back).
|
|
177
|
+
// A project-root-only check would miss it and re-fire discuss-milestone.
|
|
178
|
+
const tmp = mkdtempSync(join(tmpdir(), "gsd-disp-cov-wt-only-context-"));
|
|
179
|
+
t.after(() => rmSync(tmp, { recursive: true, force: true }));
|
|
180
|
+
|
|
181
|
+
// No CONTEXT at the project root — only the milestone dir exists there.
|
|
182
|
+
mkdirSync(join(tmp, ".gsd", "phases"), { recursive: true });
|
|
183
|
+
|
|
184
|
+
const worktree = join(tmp, ".gsd", "worktrees", "M001");
|
|
185
|
+
const worktreePhaseDir = join(worktree, ".gsd", "phases", "01-foundation");
|
|
186
|
+
mkdirSync(worktreePhaseDir, { recursive: true });
|
|
187
|
+
writeFileSync(join(worktreePhaseDir, "01-CONTEXT.md"), "# WT Context\n");
|
|
188
|
+
|
|
189
|
+
const rule = DISPATCH_RULES.find(
|
|
190
|
+
(candidate) => candidate.name === "pre-planning (no context) → discuss-milestone",
|
|
191
|
+
);
|
|
192
|
+
assert.ok(rule, "pre-planning missing-context rule should exist");
|
|
193
|
+
|
|
194
|
+
const result = await rule.match(makeCtx(worktree, makeState({ phase: "pre-planning" })));
|
|
195
|
+
assert.equal(result, null, "worktree-only CONTEXT must prevent a second discuss-milestone dispatch");
|
|
196
|
+
});
|
|
197
|
+
|
|
149
198
|
test("dispatch-rule-coverage: pre-planning, has CONTEXT, no RESEARCH → research-milestone", async (t) => {
|
|
150
199
|
const tmp = mkdtempSync(join(tmpdir(), "gsd-disp-cov-res-"));
|
|
151
200
|
t.after(() => rmSync(tmp, { recursive: true, force: true }));
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
|
|
7
|
+
import { readDoctorHistory, runGSDDoctor, type DoctorHistoryEntry } from "../doctor.ts";
|
|
8
|
+
|
|
9
|
+
test("doctor.ts preserves readDoctorHistory public API and records doctor runs", async (t) => {
|
|
10
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-doctor-history-api-"));
|
|
11
|
+
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
12
|
+
|
|
13
|
+
t.after(() => rmSync(base, { recursive: true, force: true }));
|
|
14
|
+
|
|
15
|
+
const report = await runGSDDoctor(base, { fix: false });
|
|
16
|
+
const history: DoctorHistoryEntry[] = await readDoctorHistory(base, 1);
|
|
17
|
+
|
|
18
|
+
assert.equal(history.length, 1);
|
|
19
|
+
assert.equal(history[0].ok, report.ok);
|
|
20
|
+
assert.equal(history[0].errors, report.issues.filter(issue => issue.severity === "error").length);
|
|
21
|
+
assert.equal(history[0].warnings, report.issues.filter(issue => issue.severity === "warning").length);
|
|
22
|
+
assert.equal(history[0].fixes, 0);
|
|
23
|
+
assert.ok(Array.isArray(history[0].codes));
|
|
24
|
+
assert.equal(typeof history[0].summary, "string");
|
|
25
|
+
});
|
|
@@ -54,6 +54,87 @@ test("passing retry evidence is not invalidated by an earlier failed run of the
|
|
|
54
54
|
assert.deepEqual(mismatches, []);
|
|
55
55
|
});
|
|
56
56
|
|
|
57
|
+
test("newer script-wrapped pass is not shadowed by a stale exact failing run", () => {
|
|
58
|
+
const command = "npm test";
|
|
59
|
+
const mismatches = crossReferenceEvidence(
|
|
60
|
+
[{ command, exitCode: 0, verdict: "passed after retry" }],
|
|
61
|
+
[
|
|
62
|
+
{
|
|
63
|
+
kind: "bash",
|
|
64
|
+
toolCallId: "call-1",
|
|
65
|
+
command,
|
|
66
|
+
exitCode: 1,
|
|
67
|
+
outputSnippet: "failed",
|
|
68
|
+
timestamp: 1,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
kind: "bash",
|
|
72
|
+
toolCallId: "call-2",
|
|
73
|
+
command: `cd /work && ${command}`,
|
|
74
|
+
exitCode: 0,
|
|
75
|
+
outputSnippet: "passed",
|
|
76
|
+
timestamp: 2,
|
|
77
|
+
},
|
|
78
|
+
] as EvidenceEntry[],
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
assert.deepEqual(mismatches, []);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("later containing script does not override an exact successful run", () => {
|
|
85
|
+
const command = "npm test";
|
|
86
|
+
const mismatches = crossReferenceEvidence(
|
|
87
|
+
[{ command, exitCode: 0, verdict: "passed" }],
|
|
88
|
+
[
|
|
89
|
+
{
|
|
90
|
+
kind: "bash",
|
|
91
|
+
toolCallId: "call-1",
|
|
92
|
+
command,
|
|
93
|
+
exitCode: 0,
|
|
94
|
+
outputSnippet: "passed",
|
|
95
|
+
timestamp: 1,
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
kind: "bash",
|
|
99
|
+
toolCallId: "call-2",
|
|
100
|
+
command: `cd /work && ${command} && npm run lint`,
|
|
101
|
+
exitCode: 1,
|
|
102
|
+
outputSnippet: "lint failed",
|
|
103
|
+
timestamp: 2,
|
|
104
|
+
},
|
|
105
|
+
] as EvidenceEntry[],
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
assert.deepEqual(mismatches, []);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("same-timestamp retry evidence prefers the later recorded run", () => {
|
|
112
|
+
const command = "npm test";
|
|
113
|
+
const mismatches = crossReferenceEvidence(
|
|
114
|
+
[{ command, exitCode: 0, verdict: "passed after retry" }],
|
|
115
|
+
[
|
|
116
|
+
{
|
|
117
|
+
kind: "bash",
|
|
118
|
+
toolCallId: "call-1",
|
|
119
|
+
command,
|
|
120
|
+
exitCode: 1,
|
|
121
|
+
outputSnippet: "failed",
|
|
122
|
+
timestamp: 1,
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
kind: "bash",
|
|
126
|
+
toolCallId: "call-2",
|
|
127
|
+
command,
|
|
128
|
+
exitCode: 0,
|
|
129
|
+
outputSnippet: "passed",
|
|
130
|
+
timestamp: 1,
|
|
131
|
+
},
|
|
132
|
+
] as EvidenceEntry[],
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
assert.deepEqual(mismatches, []);
|
|
136
|
+
});
|
|
137
|
+
|
|
57
138
|
test("stale verification evidence batches are ignored when a newer completion batch exists", () => {
|
|
58
139
|
const command = "node todo.js add 'Task A' && node todo.js add 'Task B' && node todo.js done 1";
|
|
59
140
|
const resetCommand = `rm -f "$HOME/.config/todo/data.json" && ${command}`;
|
|
@@ -60,6 +60,28 @@ test("evidence-xref: verification run through gsd_exec script matches the claime
|
|
|
60
60
|
assert.deepEqual(mismatches, [], "gsd_exec-executed verification must not be flagged as missing");
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
+
test("evidence-xref: gsd_exec runtime-purpose label matches the recorded sandbox run", () => {
|
|
64
|
+
resetEvidence();
|
|
65
|
+
|
|
66
|
+
recordToolCall("tc-exec-label", "gsd_exec", {
|
|
67
|
+
runtime: "node",
|
|
68
|
+
code: "const plan = 'T02-PLAN'; if (!plan.includes('T02')) process.exit(1);",
|
|
69
|
+
purpose: "validate T02-PLAN contains canonical Cargo command, state seam, d",
|
|
70
|
+
});
|
|
71
|
+
recordToolResult("tc-exec-label", "gsd_exec", gsdExecResult(0), false);
|
|
72
|
+
|
|
73
|
+
const mismatches = crossReferenceEvidence(
|
|
74
|
+
[{
|
|
75
|
+
command: "gsd_exec node: validate T02-PLAN contains canonical Cargo command, state seam, d",
|
|
76
|
+
exitCode: 0,
|
|
77
|
+
verdict: "passed",
|
|
78
|
+
}],
|
|
79
|
+
getEvidence(),
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
assert.deepEqual(mismatches, [], "gsd_exec purpose labels must match sandbox evidence");
|
|
83
|
+
});
|
|
84
|
+
|
|
63
85
|
test("evidence-xref: multi-line gsd_exec script matches claims for each embedded command", () => {
|
|
64
86
|
resetEvidence();
|
|
65
87
|
|