@opengsd/gsd-pi 1.8.1 → 1.9.0-dev.5c1a3ff2
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 +10 -8
- 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 +18 -49
- 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/artifact-verification.js +3 -4
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +16 -10
- 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 +125 -18
- package/dist/resources/extensions/gsd/auto-recovery.js +10 -5
- package/dist/resources/extensions/gsd/auto-worktree-cleanup.js +4 -0
- package/dist/resources/extensions/gsd/auto-worktree-teardown.js +1 -1
- package/dist/resources/extensions/gsd/auto.js +5 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +80 -4
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +31 -5
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +10 -3
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +88 -50
- 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/constants.js +12 -13
- package/dist/resources/extensions/gsd/context-masker.js +97 -21
- 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 +26 -4
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +3 -6
- package/dist/resources/extensions/gsd/flat-phase-migration.js +156 -21
- package/dist/resources/extensions/gsd/git-service.js +19 -7
- package/dist/resources/extensions/gsd/gsd-db.js +187 -45
- package/dist/resources/extensions/gsd/guided-flow.js +38 -13
- package/dist/resources/extensions/gsd/markdown-renderer.js +17 -13
- package/dist/resources/extensions/gsd/md-importer.js +107 -20
- 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 +12 -17
- package/dist/resources/extensions/gsd/migrate/presentation.js +64 -0
- package/dist/resources/extensions/gsd/model-cost-table.js +2 -0
- package/dist/resources/extensions/gsd/model-router.js +11 -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 +55 -11
- package/dist/resources/extensions/gsd/phase-anchor.js +7 -2
- package/dist/resources/extensions/gsd/planning-subagent-policy.js +37 -0
- package/dist/resources/extensions/gsd/planning-subagent-registry.js +25 -0
- package/dist/resources/extensions/gsd/post-execution-checks.js +4 -4
- package/dist/resources/extensions/gsd/preferences-types.js +5 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +48 -1
- package/dist/resources/extensions/gsd/preferences.js +17 -0
- package/dist/resources/extensions/gsd/prompts/refine-slice.md +2 -2
- package/dist/resources/extensions/gsd/provider-payload-policy.js +16 -5
- package/dist/resources/extensions/gsd/safety/destructive-guard.js +1 -1
- 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/skill-activation.js +17 -3
- 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/stop-notice.js +15 -1
- package/dist/resources/extensions/gsd/sync-lock.js +13 -7
- package/dist/resources/extensions/gsd/templates/PREFERENCES.md +5 -0
- package/dist/resources/extensions/gsd/tools/complete-task.js +94 -6
- 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/reopen-milestone.js +14 -5
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +15 -5
- package/dist/resources/extensions/gsd/tools/reopen-task.js +16 -10
- 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-context-composer.js +28 -5
- 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/workflow-projections.js +2 -3
- package/dist/resources/extensions/gsd/workspace.js +2 -1
- 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 +8 -8
- 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 +8 -8
- 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/dist/compaction/compaction.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/compaction/compaction.js +36 -4
- package/packages/gsd-agent-core/dist/compaction/compaction.js.map +1 -1
- package/packages/gsd-agent-core/dist/sdk.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/sdk.js +1 -0
- package/packages/gsd-agent-core/dist/sdk.js.map +1 -1
- 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 +15 -2
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +5 -5
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +4 -3
- 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/dist/agent.d.ts +5 -1
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +2 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +119 -0
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +107 -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 +25 -1
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.js +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/capability-patches.js +1 -1
- package/packages/pi-coding-agent/dist/core/capability-patches.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +11 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +8 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +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 +18 -49
- 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/artifact-verification.ts +3 -8
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +16 -9
- 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 +142 -16
- package/src/resources/extensions/gsd/auto-recovery.ts +10 -8
- package/src/resources/extensions/gsd/auto-worktree-cleanup.ts +4 -0
- package/src/resources/extensions/gsd/auto-worktree-teardown.ts +1 -1
- package/src/resources/extensions/gsd/auto.ts +5 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +92 -4
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +39 -10
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +11 -3
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +87 -53
- 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/constants.ts +14 -13
- package/src/resources/extensions/gsd/context-masker.ts +98 -2
- 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 +26 -4
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +3 -5
- package/src/resources/extensions/gsd/flat-phase-migration.ts +160 -19
- package/src/resources/extensions/gsd/git-service.ts +19 -6
- package/src/resources/extensions/gsd/gsd-db.ts +255 -52
- package/src/resources/extensions/gsd/guided-flow.ts +48 -12
- package/src/resources/extensions/gsd/markdown-renderer.ts +25 -21
- package/src/resources/extensions/gsd/md-importer.ts +114 -16
- 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 +12 -20
- package/src/resources/extensions/gsd/migrate/presentation.ts +83 -0
- package/src/resources/extensions/gsd/model-cost-table.ts +2 -0
- package/src/resources/extensions/gsd/model-router.ts +12 -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 +62 -11
- package/src/resources/extensions/gsd/phase-anchor.ts +8 -2
- package/src/resources/extensions/gsd/planning-subagent-policy.ts +58 -0
- package/src/resources/extensions/gsd/planning-subagent-registry.ts +31 -0
- package/src/resources/extensions/gsd/post-execution-checks.ts +4 -4
- package/src/resources/extensions/gsd/preferences-types.ts +16 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +62 -0
- package/src/resources/extensions/gsd/preferences.ts +25 -0
- package/src/resources/extensions/gsd/prompts/refine-slice.md +2 -2
- package/src/resources/extensions/gsd/provider-payload-policy.ts +18 -4
- package/src/resources/extensions/gsd/safety/destructive-guard.ts +1 -1
- 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/skill-activation.ts +17 -3
- 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/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/templates/PREFERENCES.md +5 -0
- package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +7 -7
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/before-provider-context-management.test.ts +55 -0
- 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 +362 -1
- package/src/resources/extensions/gsd/tests/context-masker.test.ts +204 -8
- package/src/resources/extensions/gsd/tests/db-engine-migrate-guards.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/db-tools-alias-suppression.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/destructive-confirmation.test.ts +33 -0
- package/src/resources/extensions/gsd/tests/destructive-guard.test.ts +14 -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/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/integration/paths.test.ts +22 -4
- package/src/resources/extensions/gsd/tests/integration-branch-meta-location.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +5 -5
- 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/milestone-leases.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +9 -1
- package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
- 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/parallel-skill-prompt-integration.test.ts +7 -5
- 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 +29 -0
- package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +84 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/reactive-executor.test.ts +39 -0
- 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/service-tier.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/single-writer-v3-tool-surface.test.ts +33 -7
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +20 -16
- package/src/resources/extensions/gsd/tests/skip-slice-cascades-tasks.test.ts +21 -0
- 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/system-context-message-routing.test.ts +13 -7
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +73 -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/workflow-projections.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/write-gate-seam.test.ts +52 -0
- package/src/resources/extensions/gsd/tools/complete-task.ts +140 -5
- 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/reopen-milestone.ts +24 -5
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +25 -5
- package/src/resources/extensions/gsd/tools/reopen-task.ts +23 -9
- 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-context-composer.ts +34 -8
- 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/workflow-projections.ts +2 -3
- package/src/resources/extensions/gsd/workspace.ts +2 -1
- 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/{UaXRrOYxBpG9QBINKhZkg → 6YWV7VAPg-jqjfPswrKZb}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{UaXRrOYxBpG9QBINKhZkg → 6YWV7VAPg-jqjfPswrKZb}/_ssgManifest.js +0 -0
|
@@ -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
|
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: #027 regression — external-edit drift repair must scope status
|
|
3
|
+
// authority to the drifted milestone(s) so a stale checkbox in an unrelated
|
|
4
|
+
// projection can't revert a reopened slice/milestone in the DB.
|
|
5
|
+
|
|
6
|
+
import test from "node:test";
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
closeDatabase,
|
|
14
|
+
getSliceTasks,
|
|
15
|
+
getSlice,
|
|
16
|
+
openDatabase,
|
|
17
|
+
updateTaskStatus,
|
|
18
|
+
updateSliceStatus,
|
|
19
|
+
} from "../gsd-db.ts";
|
|
20
|
+
import { migrateHierarchyToDb, milestoneIdsFromEntities } from "../md-importer.ts";
|
|
21
|
+
import { externalMarkdownEditHandler } from "../state-reconciliation/drift/external-markdown-edit.ts";
|
|
22
|
+
import {
|
|
23
|
+
computeProjectionSha,
|
|
24
|
+
writeCompatMarker,
|
|
25
|
+
} from "../compat/compat-marker.ts";
|
|
26
|
+
import type { DriftContext } from "../state-reconciliation/types.ts";
|
|
27
|
+
import type { GSDState } from "../types.ts";
|
|
28
|
+
|
|
29
|
+
const stubState = { phase: "idle" } as unknown as GSDState;
|
|
30
|
+
|
|
31
|
+
function makeBase(): string {
|
|
32
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-scoped-import-"));
|
|
33
|
+
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
34
|
+
return base;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function cleanup(base: string): void {
|
|
38
|
+
try {
|
|
39
|
+
closeDatabase();
|
|
40
|
+
} catch {
|
|
41
|
+
/* noop */
|
|
42
|
+
}
|
|
43
|
+
rmSync(base, { recursive: true, force: true });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const roadmapRel = (mid: string): string => join("milestones", mid, `${mid}-ROADMAP.md`);
|
|
47
|
+
|
|
48
|
+
/** Render a single-slice roadmap whose one slice is checked (`done`). */
|
|
49
|
+
function roadmapContent(mid: string, sliceTitle: string, done: boolean): string {
|
|
50
|
+
return [
|
|
51
|
+
`# ${mid}: ${mid} Milestone`,
|
|
52
|
+
"",
|
|
53
|
+
`**Vision:** ${mid} vision`,
|
|
54
|
+
"",
|
|
55
|
+
"## Slices",
|
|
56
|
+
`- [${done ? "x" : " "}] **S01: ${sliceTitle}** \`risk:low\` \`depends:[]\``,
|
|
57
|
+
"",
|
|
58
|
+
].join("\n");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function writeRoadmap(base: string, mid: string, sliceTitle: string, done: boolean): string {
|
|
62
|
+
const dir = join(base, ".gsd", "milestones", mid);
|
|
63
|
+
mkdirSync(dir, { recursive: true });
|
|
64
|
+
const content = roadmapContent(mid, sliceTitle, done);
|
|
65
|
+
writeFileSync(join(dir, `${mid}-ROADMAP.md`), content, "utf-8");
|
|
66
|
+
return content;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function planContent(taskDone: boolean): string {
|
|
70
|
+
return [
|
|
71
|
+
"# S01: Slice Plan",
|
|
72
|
+
"",
|
|
73
|
+
"**Goal:** Exercise scoped task status imports.",
|
|
74
|
+
"",
|
|
75
|
+
"## Tasks",
|
|
76
|
+
"",
|
|
77
|
+
`- [${taskDone ? "x" : " "}] **T01: Scoped Task** \`est:10m\``,
|
|
78
|
+
" Task body.",
|
|
79
|
+
"",
|
|
80
|
+
].join("\n");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function writePlan(base: string, mid: string, taskDone: boolean): void {
|
|
84
|
+
const dir = join(base, ".gsd", "milestones", mid, "slices", "S01");
|
|
85
|
+
mkdirSync(dir, { recursive: true });
|
|
86
|
+
writeFileSync(join(dir, "S01-PLAN.md"), planContent(taskDone), "utf-8");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function taskStatus(mid: string): string | undefined {
|
|
90
|
+
return getSliceTasks(mid, "S01").find((task) => task.id === "T01")?.status;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Build two legacy-layout milestones (M001, M002) each with one checked slice,
|
|
95
|
+
* import them so both slices land `complete`, then reopen M002/S01 to `pending`
|
|
96
|
+
* while leaving M002's roadmap checkbox checked (a stale projection).
|
|
97
|
+
*/
|
|
98
|
+
function seedTwoMilestonesWithReopenedB(base: string): void {
|
|
99
|
+
writeRoadmap(base, "M001", "Alpha Slice", true);
|
|
100
|
+
writeRoadmap(base, "M002", "Beta Slice", true);
|
|
101
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
102
|
+
migrateHierarchyToDb(base);
|
|
103
|
+
assert.equal(getSlice("M001", "S01")?.status, "complete");
|
|
104
|
+
assert.equal(getSlice("M002", "S01")?.status, "complete");
|
|
105
|
+
updateSliceStatus("M002", "S01", "pending");
|
|
106
|
+
assert.equal(getSlice("M002", "S01")?.status, "pending", "precondition: B/S01 reopened");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Same as seedTwoMilestonesWithReopenedB, but with checked task boxes too. The
|
|
111
|
+
* reopened DB task is the authority for out-of-scope milestones.
|
|
112
|
+
*/
|
|
113
|
+
function seedTwoMilestonesWithReopenedBTask(base: string): void {
|
|
114
|
+
writeRoadmap(base, "M001", "Alpha Slice", true);
|
|
115
|
+
writePlan(base, "M001", true);
|
|
116
|
+
writeRoadmap(base, "M002", "Beta Slice", true);
|
|
117
|
+
writePlan(base, "M002", true);
|
|
118
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
119
|
+
migrateHierarchyToDb(base);
|
|
120
|
+
assert.equal(getSlice("M002", "S01")?.status, "complete");
|
|
121
|
+
assert.equal(taskStatus("M002"), "complete");
|
|
122
|
+
updateSliceStatus("M002", "S01", "pending");
|
|
123
|
+
updateTaskStatus("M002", "S01", "T01", "pending");
|
|
124
|
+
assert.equal(getSlice("M002", "S01")?.status, "pending", "precondition: B/S01 reopened");
|
|
125
|
+
assert.equal(taskStatus("M002"), "pending", "precondition: B/S01/T01 reopened");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
test("milestoneIdsFromEntities derives milestone ids from DB entity ids (layout-independent)", () => {
|
|
129
|
+
assert.deepEqual(
|
|
130
|
+
[...milestoneIdsFromEntities(["M001", "M001/S01", "M001/S01/T01"])],
|
|
131
|
+
["M001"],
|
|
132
|
+
);
|
|
133
|
+
assert.deepEqual(
|
|
134
|
+
[...milestoneIdsFromEntities(["M001/S01", "M002/S03/T02"])].sort(),
|
|
135
|
+
["M001", "M002"],
|
|
136
|
+
);
|
|
137
|
+
// Entity-less / blank entries yield an empty set → repair preserves DB status.
|
|
138
|
+
assert.equal(milestoneIdsFromEntities([]).size, 0);
|
|
139
|
+
assert.equal(milestoneIdsFromEntities([""]).size, 0);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("scoped import preserves a reopened out-of-scope slice (the #027 bug, direct)", (t) => {
|
|
143
|
+
const base = makeBase();
|
|
144
|
+
t.after(() => cleanup(base));
|
|
145
|
+
seedTwoMilestonesWithReopenedB(base);
|
|
146
|
+
|
|
147
|
+
// Only M001 drifted; M002 keeps DB status authority.
|
|
148
|
+
migrateHierarchyToDb(base, { statusAuthoritativeMilestones: new Set(["M001"]) });
|
|
149
|
+
|
|
150
|
+
assert.equal(
|
|
151
|
+
getSlice("M002", "S01")?.status,
|
|
152
|
+
"pending",
|
|
153
|
+
"reopened out-of-scope slice must NOT be reverted to complete by a stale checkbox",
|
|
154
|
+
);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("scoped import preserves reopened tasks in an out-of-scope slice", (t) => {
|
|
158
|
+
const base = makeBase();
|
|
159
|
+
t.after(() => cleanup(base));
|
|
160
|
+
seedTwoMilestonesWithReopenedBTask(base);
|
|
161
|
+
|
|
162
|
+
// Only M001 drifted; M002's stale checked roadmap and plan boxes must not
|
|
163
|
+
// re-complete either its reopened slice or its reopened task.
|
|
164
|
+
migrateHierarchyToDb(base, { statusAuthoritativeMilestones: new Set(["M001"]) });
|
|
165
|
+
|
|
166
|
+
assert.equal(
|
|
167
|
+
getSlice("M002", "S01")?.status,
|
|
168
|
+
"pending",
|
|
169
|
+
"reopened out-of-scope slice must stay pending",
|
|
170
|
+
);
|
|
171
|
+
assert.equal(
|
|
172
|
+
taskStatus("M002"),
|
|
173
|
+
"pending",
|
|
174
|
+
"reopened out-of-scope task must NOT be reverted to complete by a stale plan checkbox",
|
|
175
|
+
);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("scoped import keeps markdown authority for the in-scope (drifted) milestone", (t) => {
|
|
179
|
+
const base = makeBase();
|
|
180
|
+
t.after(() => cleanup(base));
|
|
181
|
+
seedTwoMilestonesWithReopenedB(base);
|
|
182
|
+
|
|
183
|
+
// Sanctioned interop: M002's own roadmap was externally edited — the checked
|
|
184
|
+
// box IS authoritative for the drifted milestone.
|
|
185
|
+
migrateHierarchyToDb(base, { statusAuthoritativeMilestones: new Set(["M002"]) });
|
|
186
|
+
|
|
187
|
+
assert.equal(
|
|
188
|
+
getSlice("M002", "S01")?.status,
|
|
189
|
+
"complete",
|
|
190
|
+
"the drifted milestone's checkbox must close its slice (markdown authority)",
|
|
191
|
+
);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("no-opts import is unchanged: markdown checkbox wins (pins the default)", (t) => {
|
|
195
|
+
const base = makeBase();
|
|
196
|
+
t.after(() => cleanup(base));
|
|
197
|
+
seedTwoMilestonesWithReopenedB(base);
|
|
198
|
+
|
|
199
|
+
// No scope → full markdown authority, exactly as before #027. Initial-migration
|
|
200
|
+
// and gsd-core full-import semantics must not silently change.
|
|
201
|
+
migrateHierarchyToDb(base);
|
|
202
|
+
|
|
203
|
+
assert.equal(
|
|
204
|
+
getSlice("M002", "S01")?.status,
|
|
205
|
+
"complete",
|
|
206
|
+
"without a scope, the stale checkbox closes the slice — today's behavior",
|
|
207
|
+
);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test("scoped import still imports NEW out-of-scope content with its parsed status", (t) => {
|
|
211
|
+
const base = makeBase();
|
|
212
|
+
t.after(() => cleanup(base));
|
|
213
|
+
// Both milestones are on disk but neither is in the DB yet. M002 is out of the
|
|
214
|
+
// authority scope, but a row that does not exist yet takes the parsed status
|
|
215
|
+
// regardless of scope (new content is new content).
|
|
216
|
+
writeRoadmap(base, "M001", "Alpha Slice", true);
|
|
217
|
+
writeRoadmap(base, "M002", "Beta Slice", true);
|
|
218
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
219
|
+
|
|
220
|
+
migrateHierarchyToDb(base, { statusAuthoritativeMilestones: new Set(["M001"]) });
|
|
221
|
+
|
|
222
|
+
assert.equal(getSlice("M002", "S01")?.status, "complete", "new out-of-scope slice imports as parsed");
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("external-markdown-edit repair scopes authority to the drifted milestone (end-to-end)", async (t) => {
|
|
226
|
+
const base = makeBase();
|
|
227
|
+
t.after(() => cleanup(base));
|
|
228
|
+
seedTwoMilestonesWithReopenedB(base);
|
|
229
|
+
|
|
230
|
+
// Externally edit M001's roadmap (change the slice title) so its sha drifts;
|
|
231
|
+
// leave M002's roadmap byte-identical to its marker baseline (not drifted).
|
|
232
|
+
writeRoadmap(base, "M001", "Alpha Slice EDITED", true);
|
|
233
|
+
const m002Content = roadmapContent("M002", "Beta Slice", true);
|
|
234
|
+
|
|
235
|
+
writeCompatMarker(base, {
|
|
236
|
+
schema: 2,
|
|
237
|
+
lastWriter: "gsd-pi",
|
|
238
|
+
lastProjectedAt: "2026-07-07T00:00:00.000Z",
|
|
239
|
+
projections: {
|
|
240
|
+
[roadmapRel("M001")]: { sha: "stale000000000000", entities: ["M001"] },
|
|
241
|
+
[roadmapRel("M002")]: { sha: computeProjectionSha(m002Content), entities: ["M002"] },
|
|
242
|
+
},
|
|
243
|
+
piVersion: "1.8.1",
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const ctx: DriftContext = { basePath: base, state: stubState };
|
|
247
|
+
const drift = await externalMarkdownEditHandler.detect(stubState, ctx);
|
|
248
|
+
assert.equal(drift.length, 1, "only M001's roadmap should be detected as drifted");
|
|
249
|
+
assert.deepEqual(drift[0].entities, ["M001"]);
|
|
250
|
+
|
|
251
|
+
await externalMarkdownEditHandler.repair(drift[0], ctx);
|
|
252
|
+
|
|
253
|
+
// B was NOT the drifted file — its reopened slice must survive the repair.
|
|
254
|
+
assert.equal(
|
|
255
|
+
getSlice("M002", "S01")?.status,
|
|
256
|
+
"pending",
|
|
257
|
+
"repair of an unrelated file must not revert B/S01",
|
|
258
|
+
);
|
|
259
|
+
// A WAS drifted — its edit is imported (title updated, status stays complete).
|
|
260
|
+
assert.equal(getSlice("M001", "S01")?.title, "Alpha Slice EDITED", "A's external edit is imported");
|
|
261
|
+
assert.equal(getSlice("M001", "S01")?.status, "complete");
|
|
262
|
+
});
|
|
@@ -57,6 +57,19 @@ test("needsFlatPhaseMigration returns false when no .gsd/milestones/", () => {
|
|
|
57
57
|
assert.equal(needsFlatPhaseMigration(base), false);
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
+
test("needsFlatPhaseMigration ignores legacy anchor runtime scaffolding", () => {
|
|
61
|
+
const base = mkdtempSync(join(tmpdir(), `gsd-anchor-nomig-${randomUUID()}`));
|
|
62
|
+
mkdirSync(join(base, ".gsd", "phases"), { recursive: true });
|
|
63
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001", "anchors"), { recursive: true });
|
|
64
|
+
writeFileSync(
|
|
65
|
+
join(base, ".gsd", "milestones", "M001", "anchors", "research-slice.json"),
|
|
66
|
+
"{}",
|
|
67
|
+
"utf-8",
|
|
68
|
+
);
|
|
69
|
+
tmpDirs.push(base);
|
|
70
|
+
assert.equal(needsFlatPhaseMigration(base), false);
|
|
71
|
+
});
|
|
72
|
+
|
|
60
73
|
test("migrateToFlatPhase moves content from milestones/ to phases/", async () => {
|
|
61
74
|
const base = makeTmp();
|
|
62
75
|
await migrateToFlatPhase(base);
|
|
@@ -109,6 +122,59 @@ test("migrateToFlatPhase creates a backup", async () => {
|
|
|
109
122
|
assert.ok(backups.length >= 1, "at least one migrate-* backup dir should exist");
|
|
110
123
|
});
|
|
111
124
|
|
|
125
|
+
test("migrateToFlatPhase backs up existing phases projection before clearing it", async () => {
|
|
126
|
+
const base = makeTmp();
|
|
127
|
+
const reviewPath = join(base, ".gsd", "phases", "01-foundation", "PLAN-REVIEW.md");
|
|
128
|
+
mkdirSync(join(base, ".gsd", "phases", "01-foundation"), { recursive: true });
|
|
129
|
+
writeFileSync(reviewPath, "# Plan Review\n\nHand-authored review.", "utf-8");
|
|
130
|
+
|
|
131
|
+
await migrateToFlatPhase(base);
|
|
132
|
+
|
|
133
|
+
const backupRoot = join(base, ".gsd-backups");
|
|
134
|
+
const backups = readdirSync(backupRoot).filter(d => d.startsWith("migrate-"));
|
|
135
|
+
assert.equal(backups.length, 1, "one migrate backup should exist");
|
|
136
|
+
const backedUpReview = join(backupRoot, backups[0]!, "__phases", "01-foundation", "PLAN-REVIEW.md");
|
|
137
|
+
assert.equal(
|
|
138
|
+
readFileSync(backedUpReview, "utf-8"),
|
|
139
|
+
"# Plan Review\n\nHand-authored review.",
|
|
140
|
+
"pre-existing phases/ files should have a recovery copy",
|
|
141
|
+
);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("failed migration restores pre-existing phases projection from backup", async (t) => {
|
|
145
|
+
const base = makeTmp();
|
|
146
|
+
const phasesPath = join(base, ".gsd", "phases");
|
|
147
|
+
const reviewPath = join(phasesPath, "01-foundation", "PLAN-REVIEW.md");
|
|
148
|
+
mkdirSync(join(phasesPath, "01-foundation"), { recursive: true });
|
|
149
|
+
writeFileSync(reviewPath, "# Plan Review\n\nHand-authored review.", "utf-8");
|
|
150
|
+
|
|
151
|
+
let sabotagedClear = false;
|
|
152
|
+
const restoreFsOps = _setFlatPhaseMigrationFsOpsForTest({
|
|
153
|
+
rmSync(target, opts) {
|
|
154
|
+
rmSync(target, opts as never);
|
|
155
|
+
if (target === phasesPath && !sabotagedClear) {
|
|
156
|
+
sabotagedClear = true;
|
|
157
|
+
writeFileSync(phasesPath, "not a directory", "utf-8");
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
t.after(restoreFsOps);
|
|
162
|
+
|
|
163
|
+
await assert.rejects(migrateToFlatPhase(base), /flat-phase migration render failed/);
|
|
164
|
+
|
|
165
|
+
assert.equal(sabotagedClear, true, "test should force a post-clear render failure");
|
|
166
|
+
assert.equal(
|
|
167
|
+
readFileSync(reviewPath, "utf-8"),
|
|
168
|
+
"# Plan Review\n\nHand-authored review.",
|
|
169
|
+
"rollback should restore the pre-existing phases/ files",
|
|
170
|
+
);
|
|
171
|
+
const backupRoot = join(base, ".gsd-backups");
|
|
172
|
+
const leaked = existsSync(backupRoot)
|
|
173
|
+
? readdirSync(backupRoot).filter((d) => d.startsWith("migrate-"))
|
|
174
|
+
: [];
|
|
175
|
+
assert.equal(leaked.length, 0, "rollback should still clean the backup it created");
|
|
176
|
+
});
|
|
177
|
+
|
|
112
178
|
test("failed migration rolls back without leaking a .gsd-backups/migrate-* dir", async (t) => {
|
|
113
179
|
const base = makeTmp();
|
|
114
180
|
const milestonesPath = join(base, ".gsd", "milestones");
|
|
@@ -136,6 +202,33 @@ test("failed migration rolls back without leaking a .gsd-backups/migrate-* dir",
|
|
|
136
202
|
assert.equal(leaked.length, 0, "rollback must delete the migrate-* backup it created");
|
|
137
203
|
});
|
|
138
204
|
|
|
205
|
+
test("resumed migration stores phases backup in retained migrate snapshot", async () => {
|
|
206
|
+
const base = makeTmp();
|
|
207
|
+
const milestonesPath = join(base, ".gsd", "milestones");
|
|
208
|
+
const migratingPath = join(base, ".gsd", "milestones.migrating");
|
|
209
|
+
const reviewPath = join(base, ".gsd", "phases", "01-foundation", "PLAN-REVIEW.md");
|
|
210
|
+
mkdirSync(join(base, ".gsd", "phases", "01-foundation"), { recursive: true });
|
|
211
|
+
writeFileSync(reviewPath, "# Plan Review\n\nResume recovery copy.", "utf-8");
|
|
212
|
+
renameSync(milestonesPath, migratingPath);
|
|
213
|
+
|
|
214
|
+
await migrateToFlatPhase(base);
|
|
215
|
+
|
|
216
|
+
assert.equal(existsSync(migratingPath), false, "resume staging dir should be removed after success");
|
|
217
|
+
const backupRoot = join(base, ".gsd-backups");
|
|
218
|
+
const backups = readdirSync(backupRoot).filter((d) => d.startsWith("migrate-"));
|
|
219
|
+
assert.equal(backups.length, 1, "resumed migration should create one retained migrate backup");
|
|
220
|
+
assert.equal(
|
|
221
|
+
readFileSync(join(backupRoot, backups[0]!, "__phases", "01-foundation", "PLAN-REVIEW.md"), "utf-8"),
|
|
222
|
+
"# Plan Review\n\nResume recovery copy.",
|
|
223
|
+
"resume should retain the phases snapshot under .gsd-backups",
|
|
224
|
+
);
|
|
225
|
+
assert.equal(
|
|
226
|
+
existsSync(join(migratingPath, "__phases")),
|
|
227
|
+
false,
|
|
228
|
+
"resume should not store the phases snapshot inside the disposable migrating tree",
|
|
229
|
+
);
|
|
230
|
+
});
|
|
231
|
+
|
|
139
232
|
test("migrateToFlatPhase preserves milestone/slice/task counts in DB", async () => {
|
|
140
233
|
const base = makeTmp();
|
|
141
234
|
const msBefore = getAllMilestones().length;
|
|
@@ -190,6 +283,42 @@ test("migrateToFlatPhase prunes legacy milestones artifact rows after flat rende
|
|
|
190
283
|
);
|
|
191
284
|
});
|
|
192
285
|
|
|
286
|
+
test("migrateToFlatPhase prunes stale flat-phase artifact rows that renderAll intentionally skips", async () => {
|
|
287
|
+
const base = makeTmp();
|
|
288
|
+
insertArtifact({
|
|
289
|
+
path: "phases/01-foundation/T01-PLAN.md",
|
|
290
|
+
artifact_type: "PLAN",
|
|
291
|
+
milestone_id: "M001",
|
|
292
|
+
slice_id: "S01",
|
|
293
|
+
task_id: "T01",
|
|
294
|
+
full_content: "# Legacy standalone task plan\n",
|
|
295
|
+
});
|
|
296
|
+
insertArtifact({
|
|
297
|
+
path: "phases/01-foundation/01-01-RESEARCH.md",
|
|
298
|
+
artifact_type: "RESEARCH",
|
|
299
|
+
milestone_id: "M001",
|
|
300
|
+
slice_id: "S01",
|
|
301
|
+
task_id: null,
|
|
302
|
+
full_content: "",
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
await migrateToFlatPhase(base);
|
|
306
|
+
|
|
307
|
+
const rows = _getAdapter()!
|
|
308
|
+
.prepare("SELECT path FROM artifacts ORDER BY path")
|
|
309
|
+
.all() as Array<{ path: string }>;
|
|
310
|
+
assert.equal(
|
|
311
|
+
rows.some((row) => row.path === "phases/01-foundation/T01-PLAN.md"),
|
|
312
|
+
false,
|
|
313
|
+
"standalone task PLAN rows should be pruned when the flat renderer does not recreate the file",
|
|
314
|
+
);
|
|
315
|
+
assert.equal(
|
|
316
|
+
rows.some((row) => row.path === "phases/01-foundation/01-01-RESEARCH.md"),
|
|
317
|
+
false,
|
|
318
|
+
"empty-content artifact rows should be pruned when the flat renderer skips the file",
|
|
319
|
+
);
|
|
320
|
+
});
|
|
321
|
+
|
|
193
322
|
test("migrateToFlatPhase is idempotent (second run is a no-op)", async () => {
|
|
194
323
|
const base = makeTmp();
|
|
195
324
|
await migrateToFlatPhase(base);
|
|
@@ -199,6 +328,79 @@ test("migrateToFlatPhase is idempotent (second run is a no-op)", async () => {
|
|
|
199
328
|
assert.equal(backups.length, 1, "should only have one backup");
|
|
200
329
|
});
|
|
201
330
|
|
|
331
|
+
test("re-fired migration reuses the existing backup instead of leaking a new migrate-* dir (#1292)", async () => {
|
|
332
|
+
const base = makeTmp();
|
|
333
|
+
await migrateToFlatPhase(base);
|
|
334
|
+
const backupRoot = join(base, ".gsd-backups");
|
|
335
|
+
const firstBackups = readdirSync(backupRoot).filter((d) => d.startsWith("migrate-"));
|
|
336
|
+
assert.equal(firstBackups.length, 1, "first migration snapshots exactly one backup");
|
|
337
|
+
|
|
338
|
+
// Simulate the re-fire: the legacy .gsd/milestones/ layout reappears (e.g. a
|
|
339
|
+
// marker-key mismatch re-imports the whole tree). The DB rows still exist, so
|
|
340
|
+
// the migration gate proceeds again — it must not snapshot a second backup.
|
|
341
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks", "T01"), { recursive: true });
|
|
342
|
+
assert.equal(needsFlatPhaseMigration(base), true, "reappeared legacy layout re-triggers migration");
|
|
343
|
+
|
|
344
|
+
await migrateToFlatPhase(base);
|
|
345
|
+
|
|
346
|
+
const afterBackups = readdirSync(backupRoot).filter((d) => d.startsWith("migrate-"));
|
|
347
|
+
assert.deepEqual(afterBackups, firstBackups, "re-fire must not leak a second migrate-* backup");
|
|
348
|
+
assert.equal(existsSync(join(base, ".gsd", "milestones")), false, "legacy milestones/ removed again");
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
test("rollback after a re-fired migration preserves the reused migrate-* backup", async (t) => {
|
|
352
|
+
const base = makeTmp();
|
|
353
|
+
await migrateToFlatPhase(base);
|
|
354
|
+
const backupRoot = join(base, ".gsd-backups");
|
|
355
|
+
const firstBackups = readdirSync(backupRoot).filter((d) => d.startsWith("migrate-"));
|
|
356
|
+
assert.equal(firstBackups.length, 1, "first migration snapshots exactly one backup");
|
|
357
|
+
|
|
358
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks", "T01"), { recursive: true });
|
|
359
|
+
const milestonesPath = join(base, ".gsd", "milestones");
|
|
360
|
+
const phasesPath = join(base, ".gsd", "phases");
|
|
361
|
+
|
|
362
|
+
const restoreFsOps = _setFlatPhaseMigrationFsOpsForTest({
|
|
363
|
+
rmSync(target, opts) {
|
|
364
|
+
if (target === phasesPath) {
|
|
365
|
+
throw Object.assign(new Error("simulated locked phases/"), { code: "EPERM" });
|
|
366
|
+
}
|
|
367
|
+
return rmSync(target, opts as never);
|
|
368
|
+
},
|
|
369
|
+
});
|
|
370
|
+
t.after(restoreFsOps);
|
|
371
|
+
|
|
372
|
+
await assert.rejects(migrateToFlatPhase(base), /simulated locked/);
|
|
373
|
+
|
|
374
|
+
const afterBackups = readdirSync(backupRoot).filter((d) => d.startsWith("migrate-"));
|
|
375
|
+
assert.deepEqual(afterBackups, firstBackups, "rollback must preserve the reused backup");
|
|
376
|
+
assert.ok(existsSync(milestonesPath), "legacy milestones/ should be restored on rollback");
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
test("migration ignores an empty/partial leftover backup and writes a complete one (#1292)", async () => {
|
|
380
|
+
const base = makeTmp();
|
|
381
|
+
|
|
382
|
+
// Simulate a prior first-time backup that crashed mid-cpSync: an empty
|
|
383
|
+
// migrate-<ts>/ leftover with no milestones/ content. It is the newest entry,
|
|
384
|
+
// so mtime-based selection would reuse it as the rollback copy if content were
|
|
385
|
+
// not validated — silently retaining a recovery copy missing the legacy tree.
|
|
386
|
+
const backupRoot = join(base, ".gsd-backups");
|
|
387
|
+
const emptyLeftover = join(backupRoot, "migrate-crashed");
|
|
388
|
+
mkdirSync(emptyLeftover, { recursive: true });
|
|
389
|
+
|
|
390
|
+
await migrateToFlatPhase(base);
|
|
391
|
+
|
|
392
|
+
assert.ok(existsSync(join(base, ".gsd", "phases", "01-foundation")), "flat phase should render");
|
|
393
|
+
// The empty leftover must not have been reused; a fresh, content-bearing
|
|
394
|
+
// backup that actually captured the legacy milestones/ tree must be created.
|
|
395
|
+
const contentBackups = readdirSync(backupRoot)
|
|
396
|
+
.filter((d) => d.startsWith("migrate-"))
|
|
397
|
+
.filter((d) => existsSync(join(backupRoot, d, "M001")));
|
|
398
|
+
assert.ok(
|
|
399
|
+
contentBackups.length >= 1,
|
|
400
|
+
"a complete backup containing the legacy milestones/ tree must be created, not the empty leftover",
|
|
401
|
+
);
|
|
402
|
+
});
|
|
403
|
+
|
|
202
404
|
test("migrateToFlatPhase preserves slice sidecar artifacts and skips recovery placeholder PLAN", async () => {
|
|
203
405
|
const base = makeTmp({ withTask: false });
|
|
204
406
|
const legacySliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
|
|
@@ -19,6 +19,11 @@ import {
|
|
|
19
19
|
insertMilestone,
|
|
20
20
|
insertSlice,
|
|
21
21
|
} from "../gsd-db.ts";
|
|
22
|
+
import { getDb } from "../db/engine.ts";
|
|
23
|
+
|
|
24
|
+
function gateRunCount(gateId: string): number {
|
|
25
|
+
return (getDb().prepare("SELECT count(*) AS cnt FROM gate_runs WHERE gate_id = :gid").get({ ":gid": gateId }) as { cnt: number }).cnt;
|
|
26
|
+
}
|
|
22
27
|
|
|
23
28
|
describe("quality_gates CRUD", () => {
|
|
24
29
|
let tmpDir: string;
|
|
@@ -83,6 +88,30 @@ describe("quality_gates CRUD", () => {
|
|
|
83
88
|
assert.ok(results[0].evaluated_at);
|
|
84
89
|
});
|
|
85
90
|
|
|
91
|
+
test("saveGateResult commits the verdict and the gate_runs ledger row together", () => {
|
|
92
|
+
insertGateRow({ milestoneId: "M001", sliceId: "S01", gateId: "Q3", scope: "slice" });
|
|
93
|
+
saveGateResult({
|
|
94
|
+
milestoneId: "M001", sliceId: "S01", gateId: "Q3",
|
|
95
|
+
verdict: "pass", rationale: "OK", findings: "",
|
|
96
|
+
});
|
|
97
|
+
assert.equal(getGateResults("M001", "S01")[0].status, "complete");
|
|
98
|
+
assert.equal(gateRunCount("Q3"), 1, "exactly one ledger row must accompany the verdict");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("saveGateResult rolls back the verdict UPDATE if the ledger INSERT fails", () => {
|
|
102
|
+
insertGateRow({ milestoneId: "M001", sliceId: "S01", gateId: "Q3", scope: "slice" });
|
|
103
|
+
// Force the insertGateRun step to throw after the UPDATE by removing the
|
|
104
|
+
// ledger table. Atomicity means the gate row must stay pending.
|
|
105
|
+
getDb().exec("DROP TABLE gate_runs");
|
|
106
|
+
assert.throws(() => saveGateResult({
|
|
107
|
+
milestoneId: "M001", sliceId: "S01", gateId: "Q3",
|
|
108
|
+
verdict: "pass", rationale: "OK", findings: "",
|
|
109
|
+
}));
|
|
110
|
+
const gate = getGateResults("M001", "S01")[0];
|
|
111
|
+
assert.equal(gate.status, "pending", "gate verdict UPDATE must roll back when the ledger INSERT fails");
|
|
112
|
+
assert.equal(gate.verdict ?? "", "", "verdict must not persist after rollback");
|
|
113
|
+
});
|
|
114
|
+
|
|
86
115
|
test("saveGateResult throws when the gate row does not exist", () => {
|
|
87
116
|
assert.throws(
|
|
88
117
|
() => saveGateResult({
|