@opengsd/gsd-pi 1.8.1 → 1.9.0
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 +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/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 +102 -14
- 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/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/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 +8 -8
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- 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_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 +2 -2
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- 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 +2 -2
- 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/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
- 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 +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +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/8357.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- 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/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-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/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 +109 -11
- 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/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/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-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/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-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/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/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/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/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/css/18c2fdf261bfc1b7.css +0 -1
- /package/dist/web/standalone/.next/static/{UaXRrOYxBpG9QBINKhZkg → BE5XoklToI6lZjRzpXih8}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{UaXRrOYxBpG9QBINKhZkg → BE5XoklToI6lZjRzpXih8}/_ssgManifest.js +0 -0
|
@@ -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 }));
|
|
@@ -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
|
+
});
|