@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
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression tests for batched touched-file collection in parallel eligibility.
|
|
3
|
+
*
|
|
4
|
+
* `collectTouchedFiles` used to run one `getSliceTasks` query per slice (N+1);
|
|
5
|
+
* it now issues a single `getTasksBySliceIds` batch and dedups paths into a
|
|
6
|
+
* Set. These tests pin the observable contract of that path through the public
|
|
7
|
+
* `analyzeParallelEligibility` API:
|
|
8
|
+
* 1. files are collected from EVERY slice of a milestone, not just the first
|
|
9
|
+
* (a file present only in a non-first slice must still drive overlap), and
|
|
10
|
+
* 2. a path repeated across a milestone's slices is deduped (Set semantics),
|
|
11
|
+
* so it appears once in the cross-milestone overlap.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
15
|
+
import assert from "node:assert/strict";
|
|
16
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import { tmpdir } from "node:os";
|
|
19
|
+
|
|
20
|
+
import { analyzeParallelEligibility } from "../parallel-eligibility.ts";
|
|
21
|
+
import { invalidateStateCache } from "../state.ts";
|
|
22
|
+
import {
|
|
23
|
+
openDatabase,
|
|
24
|
+
closeDatabase,
|
|
25
|
+
insertMilestone,
|
|
26
|
+
insertSlice,
|
|
27
|
+
insertTask,
|
|
28
|
+
} from "../gsd-db.ts";
|
|
29
|
+
|
|
30
|
+
// ─── Fixture Helpers ───────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
function createFixtureBase(): string {
|
|
33
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-parallel-elig-overlap-"));
|
|
34
|
+
mkdirSync(join(base, ".gsd", "milestones"), { recursive: true });
|
|
35
|
+
return base;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function writeMilestoneFile(base: string, milestoneId: string, filename: string, content: string): void {
|
|
39
|
+
const filePath = join(base, ".gsd", "milestones", milestoneId, filename);
|
|
40
|
+
mkdirSync(join(filePath, ".."), { recursive: true });
|
|
41
|
+
writeFileSync(filePath, content);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Write the on-disk planning artifacts that make a milestone eligible
|
|
46
|
+
* (CONTEXT + ROADMAP listing its slices + a PLAN per slice). Mirrors the
|
|
47
|
+
* proven-eligible setup used by parallel-eligibility-ghost.test.ts.
|
|
48
|
+
*/
|
|
49
|
+
function writeEligibleMilestoneFiles(base: string, milestoneId: string, sliceIds: string[]): void {
|
|
50
|
+
writeMilestoneFile(base, milestoneId, `${milestoneId}-CONTEXT.md`, `# ${milestoneId}: Fixture\n\nBatch fixture.`);
|
|
51
|
+
const roadmapSlices = sliceIds
|
|
52
|
+
.map((sid) => `- [ ] **${sid}: Slice ${sid}** \`risk:low\` \`depends:[]\`\n > Slice ${sid}.`)
|
|
53
|
+
.join("\n");
|
|
54
|
+
writeMilestoneFile(
|
|
55
|
+
base,
|
|
56
|
+
milestoneId,
|
|
57
|
+
`${milestoneId}-ROADMAP.md`,
|
|
58
|
+
`# ${milestoneId}: Fixture\n\n## Slices\n\n${roadmapSlices}\n`,
|
|
59
|
+
);
|
|
60
|
+
for (const sid of sliceIds) {
|
|
61
|
+
writeMilestoneFile(
|
|
62
|
+
base,
|
|
63
|
+
milestoneId,
|
|
64
|
+
`slices/${sid}/${sid}-PLAN.md`,
|
|
65
|
+
`# ${sid}: Slice ${sid}\n\n**Goal:** Do ${sid}.\n**Demo:** Done.\n\n## Tasks\n\n- [ ] **T01: Task** \`est:10m\`\n Do it.\n`,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function cleanup(base: string): void {
|
|
71
|
+
rmSync(base, { recursive: true, force: true });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ─── Tests ─────────────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
describe("parallel-eligibility: batched touched-file collection", () => {
|
|
77
|
+
let base: string;
|
|
78
|
+
|
|
79
|
+
beforeEach(() => {
|
|
80
|
+
base = createFixtureBase();
|
|
81
|
+
openDatabase(":memory:");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
afterEach(() => {
|
|
85
|
+
closeDatabase();
|
|
86
|
+
cleanup(base);
|
|
87
|
+
invalidateStateCache();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("collects files from every slice (batch) and dedups repeated paths", async () => {
|
|
91
|
+
// M001 spans two slices. `src/shared.ts` appears in BOTH slices (dedup
|
|
92
|
+
// target); `src/beta.ts` appears ONLY in the second slice S02 (proves the
|
|
93
|
+
// batch covers non-first slices). `src/alpha.ts` is unique to M001.
|
|
94
|
+
writeEligibleMilestoneFiles(base, "M001", ["S01", "S02"]);
|
|
95
|
+
insertMilestone({ id: "M001", title: "M001: Fixture", status: "active" });
|
|
96
|
+
insertSlice({ id: "S01", milestoneId: "M001", title: "Slice S01", status: "active", risk: "low", depends: [], sequence: 1 });
|
|
97
|
+
insertSlice({ id: "S02", milestoneId: "M001", title: "Slice S02", status: "active", risk: "low", depends: [], sequence: 2 });
|
|
98
|
+
insertTask({
|
|
99
|
+
id: "T01", sliceId: "S01", milestoneId: "M001", title: "S01 task", status: "pending",
|
|
100
|
+
planning: { files: ["src/alpha.ts", "src/shared.ts"] },
|
|
101
|
+
});
|
|
102
|
+
insertTask({
|
|
103
|
+
id: "T01", sliceId: "S02", milestoneId: "M001", title: "S02 task", status: "pending",
|
|
104
|
+
planning: { files: ["src/shared.ts", "src/beta.ts"] },
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// M002 overlaps M001 on both `src/shared.ts` and `src/beta.ts`.
|
|
108
|
+
writeEligibleMilestoneFiles(base, "M002", ["S01"]);
|
|
109
|
+
insertMilestone({ id: "M002", title: "M002: Fixture", status: "active" });
|
|
110
|
+
insertSlice({ id: "S01", milestoneId: "M002", title: "Slice S01", status: "active", risk: "low", depends: [], sequence: 1 });
|
|
111
|
+
insertTask({
|
|
112
|
+
id: "T01", sliceId: "S01", milestoneId: "M002", title: "S01 task", status: "pending",
|
|
113
|
+
planning: { files: ["src/shared.ts", "src/beta.ts"] },
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
invalidateStateCache();
|
|
117
|
+
const result = await analyzeParallelEligibility(base);
|
|
118
|
+
|
|
119
|
+
// Both milestones must be eligible so Rule 3 (file overlap) runs on them.
|
|
120
|
+
assert.ok(result.eligible.find((e) => e.milestoneId === "M001"), "M001 should be eligible");
|
|
121
|
+
assert.ok(result.eligible.find((e) => e.milestoneId === "M002"), "M002 should be eligible");
|
|
122
|
+
|
|
123
|
+
const overlap = result.fileOverlaps.find(
|
|
124
|
+
(o) =>
|
|
125
|
+
(o.mid1 === "M001" && o.mid2 === "M002") ||
|
|
126
|
+
(o.mid1 === "M002" && o.mid2 === "M001"),
|
|
127
|
+
);
|
|
128
|
+
assert.ok(overlap, "M001 and M002 must be reported as overlapping");
|
|
129
|
+
|
|
130
|
+
// `src/beta.ts` lives only in M001's SECOND slice — its presence proves the
|
|
131
|
+
// batched query collected tasks from every slice, not just the first.
|
|
132
|
+
assert.ok(
|
|
133
|
+
overlap!.files.includes("src/beta.ts"),
|
|
134
|
+
"overlap must include src/beta.ts (only present in M001's non-first slice S02)",
|
|
135
|
+
);
|
|
136
|
+
assert.ok(overlap!.files.includes("src/shared.ts"), "overlap must include src/shared.ts");
|
|
137
|
+
|
|
138
|
+
// `src/shared.ts` appears in both of M001's slices but must be deduped to a
|
|
139
|
+
// single entry (Set collection), so the sorted overlap is exactly these two.
|
|
140
|
+
assert.deepEqual(
|
|
141
|
+
[...overlap!.files].sort(),
|
|
142
|
+
["src/beta.ts", "src/shared.ts"],
|
|
143
|
+
"overlap files must be deduped and limited to the shared paths",
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
@@ -108,6 +108,65 @@ describe("parallel-monitor-overlay", () => {
|
|
|
108
108
|
}
|
|
109
109
|
});
|
|
110
110
|
|
|
111
|
+
it("merged worker query renders slice progress and completions across multiple slices", async () => {
|
|
112
|
+
// Regression guard for the single-open merge: queryWorkerProgress now
|
|
113
|
+
// returns { slices, completions } from ONE connection per worker per tick.
|
|
114
|
+
// A worker spanning two slices, each with a completed task, must still
|
|
115
|
+
// surface every slice's progress AND both completions (previously two
|
|
116
|
+
// separate DB opens produced these).
|
|
117
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-parallel-overlay-multi-"));
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
mkdirSync(join(base, ".gsd", "parallel"), { recursive: true });
|
|
121
|
+
assert.equal(openDatabase(join(base, ".gsd", "gsd.db")), true);
|
|
122
|
+
insertMilestone({ id: "M001", title: "Multi-slice Fixture" });
|
|
123
|
+
insertSlice({ milestoneId: "M001", id: "S01", title: "First", status: "pending", sequence: 1 });
|
|
124
|
+
insertSlice({ milestoneId: "M001", id: "S02", title: "Second", status: "pending", sequence: 2 });
|
|
125
|
+
insertTask({ milestoneId: "M001", sliceId: "S01", id: "T01", status: "complete", oneLiner: "alpha done", sequence: 1 });
|
|
126
|
+
insertTask({ milestoneId: "M001", sliceId: "S01", id: "T02", status: "pending", sequence: 2 });
|
|
127
|
+
insertTask({ milestoneId: "M001", sliceId: "S02", id: "T01", status: "complete", oneLiner: "beta done", sequence: 1 });
|
|
128
|
+
insertTask({ milestoneId: "M001", sliceId: "S02", id: "T02", status: "pending", sequence: 2 });
|
|
129
|
+
closeDatabase();
|
|
130
|
+
|
|
131
|
+
writeFileSync(
|
|
132
|
+
join(base, ".gsd", "parallel", "M001.status.json"),
|
|
133
|
+
JSON.stringify({
|
|
134
|
+
milestoneId: "M001",
|
|
135
|
+
pid: process.pid,
|
|
136
|
+
state: "running",
|
|
137
|
+
cost: 0,
|
|
138
|
+
lastHeartbeat: Date.now(),
|
|
139
|
+
startedAt: Date.now() - 30_000,
|
|
140
|
+
worktreePath: join(base, ".gsd-worktrees", "M001"),
|
|
141
|
+
}),
|
|
142
|
+
"utf-8",
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const mod = await import("../parallel-monitor-overlay.js");
|
|
146
|
+
const mockTui = { requestRender: () => {} };
|
|
147
|
+
const mockTheme = {
|
|
148
|
+
fg: (_color: string, text: string) => text,
|
|
149
|
+
bold: (text: string) => text,
|
|
150
|
+
};
|
|
151
|
+
const overlay = new mod.ParallelMonitorOverlay(mockTui, mockTheme as any, () => {}, base);
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
const joined = overlay.render(120).join("\n");
|
|
155
|
+
// Both slices' progress must come from the single merged query.
|
|
156
|
+
assert.match(joined, /S01:1\/2/, "S01 progress should render from the merged query");
|
|
157
|
+
assert.match(joined, /S02:1\/2/, "S02 progress should render from the merged query");
|
|
158
|
+
// Completions for tasks in both slices ride along on the same DB open.
|
|
159
|
+
assert.match(joined, /S01\/T01: alpha done/, "S01 completion should render");
|
|
160
|
+
assert.match(joined, /S02\/T01: beta done/, "S02 completion should render");
|
|
161
|
+
} finally {
|
|
162
|
+
overlay.dispose();
|
|
163
|
+
}
|
|
164
|
+
} finally {
|
|
165
|
+
try { closeDatabase(); } catch { /* already closed */ }
|
|
166
|
+
rmSync(base, { recursive: true, force: true });
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
111
170
|
it("progressBar generates correct width", async () => {
|
|
112
171
|
// Dynamic import to test the module loads cleanly
|
|
113
172
|
const mod = await import("../parallel-monitor-overlay.js");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import test from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
-
import { mkdtempSync, mkdirSync, rmSync, existsSync } from "node:fs";
|
|
3
|
+
import { mkdtempSync, mkdirSync, rmSync, existsSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
|
|
@@ -9,11 +9,11 @@ import type { PhaseAnchor } from "../phase-anchor.js";
|
|
|
9
9
|
|
|
10
10
|
function makeTempBase(): string {
|
|
11
11
|
const tmp = mkdtempSync(join(tmpdir(), "gsd-anchor-test-"));
|
|
12
|
-
mkdirSync(join(tmp, ".gsd"
|
|
12
|
+
mkdirSync(join(tmp, ".gsd"), { recursive: true });
|
|
13
13
|
return tmp;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
test("writePhaseAnchor creates anchor file
|
|
16
|
+
test("writePhaseAnchor creates anchor file under runtime state", () => {
|
|
17
17
|
const base = makeTempBase();
|
|
18
18
|
try {
|
|
19
19
|
const anchor: PhaseAnchor = {
|
|
@@ -26,7 +26,35 @@ test("writePhaseAnchor creates anchor file in correct location", () => {
|
|
|
26
26
|
nextSteps: ["Plan the implementation slices"],
|
|
27
27
|
};
|
|
28
28
|
writePhaseAnchor(base, "M001", anchor);
|
|
29
|
-
assert.ok(existsSync(join(base, ".gsd", "
|
|
29
|
+
assert.ok(existsSync(join(base, ".gsd", "runtime", "anchors", "M001", "discuss.json")));
|
|
30
|
+
assert.equal(existsSync(join(base, ".gsd", "milestones", "M001", "anchors", "discuss.json")), false);
|
|
31
|
+
} finally {
|
|
32
|
+
rmSync(base, { recursive: true, force: true });
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("readPhaseAnchor falls back to legacy anchor location", () => {
|
|
37
|
+
const base = makeTempBase();
|
|
38
|
+
try {
|
|
39
|
+
const legacyDir = join(base, ".gsd", "milestones", "M001", "anchors");
|
|
40
|
+
mkdirSync(legacyDir, { recursive: true });
|
|
41
|
+
writeFileSync(
|
|
42
|
+
join(legacyDir, "research-slice.json"),
|
|
43
|
+
JSON.stringify({
|
|
44
|
+
phase: "research-slice",
|
|
45
|
+
milestoneId: "M001",
|
|
46
|
+
generatedAt: "2026-07-07T00:00:00.000Z",
|
|
47
|
+
intent: "Keep reading old anchors during upgrade.",
|
|
48
|
+
decisions: [],
|
|
49
|
+
blockers: [],
|
|
50
|
+
nextSteps: [],
|
|
51
|
+
}),
|
|
52
|
+
"utf-8",
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const read = readPhaseAnchor(base, "M001", "research-slice");
|
|
56
|
+
assert.ok(read);
|
|
57
|
+
assert.equal(read.intent, "Keep reading old anchors during upgrade.");
|
|
30
58
|
} finally {
|
|
31
59
|
rmSync(base, { recursive: true, force: true });
|
|
32
60
|
}
|
|
@@ -6,7 +6,7 @@ import { tmpdir } from 'node:os';
|
|
|
6
6
|
|
|
7
7
|
import { openDatabase, closeDatabase, insertMilestone, insertSlice, insertTask, getSlice, getTask, getSliceTasks, getGateResults } from '../gsd-db.ts';
|
|
8
8
|
import { handlePlanTask } from '../tools/plan-task.ts';
|
|
9
|
-
import {
|
|
9
|
+
import { parsePlan } from '../parsers-legacy.ts';
|
|
10
10
|
|
|
11
11
|
function makeTmpBase(): string {
|
|
12
12
|
const base = mkdtempSync(join(tmpdir(), 'gsd-plan-task-'));
|
|
@@ -60,7 +60,7 @@ function validParams() {
|
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
test('handlePlanTask writes planning state and renders
|
|
63
|
+
test('handlePlanTask writes planning state and renders the flat-phase slice plan', async () => {
|
|
64
64
|
const base = makeTmpBase();
|
|
65
65
|
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
66
66
|
|
|
@@ -75,11 +75,13 @@ test('handlePlanTask writes planning state and renders task plan', async () => {
|
|
|
75
75
|
assert.equal(task?.description, 'Implement the DB-backed task planning handler.');
|
|
76
76
|
assert.equal(task?.estimate, '30m');
|
|
77
77
|
|
|
78
|
-
const
|
|
79
|
-
assert.
|
|
80
|
-
|
|
81
|
-
assert.equal(
|
|
82
|
-
|
|
78
|
+
const slicePlanPath = join(base, '.gsd', 'phases', '01-test', '01-02-PLAN.md');
|
|
79
|
+
assert.equal(result.taskPlanPath, slicePlanPath);
|
|
80
|
+
assert.ok(existsSync(slicePlanPath), 'slice plan should be rendered to disk');
|
|
81
|
+
assert.equal(existsSync(join(base, '.gsd', 'phases', '01-test', 'T02-PLAN.md')), false);
|
|
82
|
+
const slicePlan = parsePlan(readFileSync(slicePlanPath, 'utf-8'));
|
|
83
|
+
assert.equal(slicePlan.tasks[0]?.id, 'T02');
|
|
84
|
+
assert.equal(slicePlan.tasks[0]?.files?.[0], 'src/resources/extensions/gsd/tools/plan-task.ts');
|
|
83
85
|
} finally {
|
|
84
86
|
cleanup(base);
|
|
85
87
|
}
|
|
@@ -215,17 +217,17 @@ test('handlePlanTask rejects missing parent slice', async () => {
|
|
|
215
217
|
}
|
|
216
218
|
});
|
|
217
219
|
|
|
218
|
-
test('handlePlanTask surfaces render failures without changing parse-visible
|
|
220
|
+
test('handlePlanTask surfaces render failures without changing parse-visible slice plan state', async () => {
|
|
219
221
|
const base = makeTmpBase();
|
|
220
222
|
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
221
223
|
|
|
222
224
|
try {
|
|
223
225
|
seedParent();
|
|
224
226
|
insertTask({ id: 'T02', sliceId: 'S02', milestoneId: 'M001', title: 'Cached task', status: 'pending' });
|
|
225
|
-
const
|
|
226
|
-
writeFileSync(
|
|
227
|
-
rmSync(
|
|
228
|
-
mkdirSync(
|
|
227
|
+
const slicePlanPath = join(base, '.gsd', 'phases', '01-test', '01-02-PLAN.md');
|
|
228
|
+
writeFileSync(slicePlanPath, '# S02: Cached slice\n\n<tasks>\n- [ ] **T02**: Cached task\n</tasks>\n', 'utf-8');
|
|
229
|
+
rmSync(slicePlanPath, { force: true });
|
|
230
|
+
mkdirSync(slicePlanPath, { recursive: true });
|
|
229
231
|
|
|
230
232
|
const result = await handlePlanTask(validParams(), base);
|
|
231
233
|
assert.ok('error' in result);
|
|
@@ -243,15 +245,16 @@ test('handlePlanTask keeps the sketch flag set when the flat-phase slice plan sy
|
|
|
243
245
|
insertMilestone({ id: 'M001', title: 'Milestone', status: 'active' });
|
|
244
246
|
insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Planning slice', status: 'pending', demo: 'Rendered plans exist.', isSketch: true });
|
|
245
247
|
|
|
246
|
-
// The
|
|
247
|
-
//
|
|
248
|
-
//
|
|
249
|
-
//
|
|
250
|
-
//
|
|
251
|
-
// instead of leaving the slice out of refining over a stale plan.
|
|
248
|
+
// The flat-phase slice render targets phases/01-test/01-02-PLAN.md.
|
|
249
|
+
// Pre-creating that path as a directory forces EISDIR, so the canonical
|
|
250
|
+
// slice plan never reflects the task. The sketch flag must therefore stay
|
|
251
|
+
// set — the slice keeps refining — instead of leaving the slice out of
|
|
252
|
+
// refining over a stale plan.
|
|
252
253
|
mkdirSync(join(base, '.gsd', 'phases', '01-test', '01-02-PLAN.md'), { recursive: true });
|
|
253
254
|
|
|
254
255
|
const result = await handlePlanTask(validParams(), base);
|
|
256
|
+
assert.ok('error' in result);
|
|
257
|
+
assert.match(result.error, /render failed:/);
|
|
255
258
|
assert.equal(getSlice('M001', 'S02')?.is_sketch, 1, 'sketch flag must stay set when the canonical slice plan could not sync');
|
|
256
259
|
assert.ok(getTask('M001', 'S02', 'T02'), 'the task itself is still persisted so the slice can re-sync on retry');
|
|
257
260
|
} finally {
|
|
@@ -265,8 +268,8 @@ test('handlePlanTask reruns idempotently and refreshes parse-visible state', asy
|
|
|
265
268
|
|
|
266
269
|
try {
|
|
267
270
|
seedParent();
|
|
268
|
-
const
|
|
269
|
-
writeFileSync(
|
|
271
|
+
const slicePlanPath = join(base, '.gsd', 'phases', '01-test', '01-02-PLAN.md');
|
|
272
|
+
writeFileSync(slicePlanPath, '# S02: Cached slice\n\n<tasks>\n- [ ] **T02**: Cached task\n</tasks>\n', 'utf-8');
|
|
270
273
|
|
|
271
274
|
const first = await handlePlanTask(validParams(), base);
|
|
272
275
|
assert.ok(!('error' in first));
|
|
@@ -282,9 +285,9 @@ test('handlePlanTask reruns idempotently and refreshes parse-visible state', asy
|
|
|
282
285
|
assert.equal(task?.description, 'Updated task handler description.');
|
|
283
286
|
assert.equal(task?.estimate, '1h');
|
|
284
287
|
|
|
285
|
-
const parsed =
|
|
286
|
-
assert.equal(parsed.
|
|
287
|
-
assert.match(readFileSync(
|
|
288
|
+
const parsed = parsePlan(readFileSync(slicePlanPath, 'utf-8'));
|
|
289
|
+
assert.equal(parsed.tasks[0]?.id, 'T02');
|
|
290
|
+
assert.match(readFileSync(slicePlanPath, 'utf-8'), /Updated task handler description\./);
|
|
288
291
|
} finally {
|
|
289
292
|
cleanup(base);
|
|
290
293
|
}
|
|
@@ -92,6 +92,35 @@ test("writePlanningDirectory is idempotent: writing twice produces stable conten
|
|
|
92
92
|
assert.equal(roadmap2, roadmap1);
|
|
93
93
|
});
|
|
94
94
|
|
|
95
|
+
test("writePlanningDirectory does not emit an ingestible PLAN.md for a task-less (sketch) slice", async () => {
|
|
96
|
+
// Regression for #1285: a milestone-level sketch slice has zero DB tasks.
|
|
97
|
+
// The projection must not write a placeholder NN-01-PLAN.md — the reverse
|
|
98
|
+
// transform maps one task per plan file, so a placeholder would materialize
|
|
99
|
+
// a phantom "Plan NN" task and make auto-mode skip planning.
|
|
100
|
+
const base = makeTmp();
|
|
101
|
+
insertSlice({
|
|
102
|
+
milestoneId: "M001", id: "S02", title: "Sketch slice", status: "pending",
|
|
103
|
+
risk: "medium", depends: ["S01"], demo: "designed", sequence: 2,
|
|
104
|
+
});
|
|
105
|
+
// S02 intentionally has no tasks.
|
|
106
|
+
|
|
107
|
+
await writePlanningDirectory(base, "flat-phases");
|
|
108
|
+
|
|
109
|
+
const phaseDirs = readdirSync(join(base, ".planning", "phases"), { withFileTypes: true })
|
|
110
|
+
.filter((d) => d.isDirectory())
|
|
111
|
+
.map((d) => d.name);
|
|
112
|
+
const sketchDir = phaseDirs.find((d) => /^02-/.test(d));
|
|
113
|
+
assert.ok(sketchDir, `expected a 02- phase dir for the sketch slice, got ${JSON.stringify(phaseDirs)}`);
|
|
114
|
+
|
|
115
|
+
const planFiles = readdirSync(join(base, ".planning", "phases", sketchDir!))
|
|
116
|
+
.filter((f) => f.endsWith("PLAN.md"));
|
|
117
|
+
assert.equal(planFiles.length, 0, `sketch slice should have no PLAN.md, got ${JSON.stringify(planFiles)}`);
|
|
118
|
+
|
|
119
|
+
// The slice remains discoverable via ROADMAP.md.
|
|
120
|
+
const roadmap = readFileSync(join(base, ".planning", "ROADMAP.md"), "utf-8");
|
|
121
|
+
assert.match(roadmap, /Sketch slice/);
|
|
122
|
+
});
|
|
123
|
+
|
|
95
124
|
test("writePlanningDirectory throws for unsupported layouts", async () => {
|
|
96
125
|
const base = makeTmp();
|
|
97
126
|
await assert.rejects(() => writePlanningDirectory(base, "multi-milestone"), /not yet supported/);
|
|
@@ -496,6 +496,28 @@ describe("checkImportResolution", () => {
|
|
|
496
496
|
}
|
|
497
497
|
});
|
|
498
498
|
|
|
499
|
+
test("ignores generated Convex _generated imports", () => {
|
|
500
|
+
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
501
|
+
mkdirSync(tempDir, { recursive: true });
|
|
502
|
+
mkdirSync(join(tempDir, "convex"), { recursive: true });
|
|
503
|
+
writeFileSync(
|
|
504
|
+
join(tempDir, "convex", "stagedLibraryEdits.ts"),
|
|
505
|
+
"import type { DataModel } from './_generated/dataModel';\nexport const model = {} as DataModel;"
|
|
506
|
+
);
|
|
507
|
+
|
|
508
|
+
try {
|
|
509
|
+
const task = createTask({
|
|
510
|
+
id: "T01",
|
|
511
|
+
key_files: ["convex/stagedLibraryEdits.ts"],
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
const results = checkImportResolution(task, [], tempDir);
|
|
515
|
+
assert.deepEqual(results, []);
|
|
516
|
+
} finally {
|
|
517
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
|
|
499
521
|
test("fails when import doesn't resolve", () => {
|
|
500
522
|
tempDir = join(tmpdir(), `post-exec-test-${Date.now()}`);
|
|
501
523
|
mkdirSync(tempDir, { recursive: true });
|
|
@@ -210,6 +210,56 @@ test('handleReassessRoadmap succeeds when modifying only pending slices', async
|
|
|
210
210
|
}
|
|
211
211
|
});
|
|
212
212
|
|
|
213
|
+
test('handleReassessRoadmap writes legacy-layout reassessment to S##-ASSESSMENT.md', async () => {
|
|
214
|
+
const base = mkdtempSync(join(tmpdir(), 'gsd-reassess-legacy-'));
|
|
215
|
+
mkdirSync(join(base, '.gsd', 'milestones', 'M005-3qxqd7', 'slices', 'S04'), { recursive: true });
|
|
216
|
+
mkdirSync(join(base, '.gsd', 'phases', '05-3qxqd7-legacy-milestone'), { recursive: true });
|
|
217
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
insertMilestone({ id: 'M005-3qxqd7', title: 'Legacy Milestone', status: 'active' });
|
|
221
|
+
insertSlice({
|
|
222
|
+
id: 'S04',
|
|
223
|
+
milestoneId: 'M005-3qxqd7',
|
|
224
|
+
title: 'Legacy Slice Four',
|
|
225
|
+
status: 'complete',
|
|
226
|
+
demo: 'Demo four.',
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
const result = await handleReassessRoadmap({
|
|
230
|
+
milestoneId: 'M005-3qxqd7',
|
|
231
|
+
completedSliceId: 'S04',
|
|
232
|
+
verdict: 'confirmed',
|
|
233
|
+
assessment: 'S04 completed successfully. Roadmap remains valid.',
|
|
234
|
+
sliceChanges: {
|
|
235
|
+
modified: [],
|
|
236
|
+
added: [],
|
|
237
|
+
removed: [],
|
|
238
|
+
},
|
|
239
|
+
}, base);
|
|
240
|
+
|
|
241
|
+
if ('error' in result) {
|
|
242
|
+
assert.fail(`unexpected error: ${result.error}`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const correctPath = join(base, '.gsd', 'milestones', 'M005-3qxqd7', 'slices', 'S04', 'S04-ASSESSMENT.md');
|
|
246
|
+
const wrongFlatNameInLegacyDir = join(base, '.gsd', 'milestones', 'M005-3qxqd7', 'slices', 'S04', '05-04-ASSESSMENT.md');
|
|
247
|
+
const wrongFlatPhasePath = join(base, '.gsd', 'phases', '05-3qxqd7-legacy-milestone', '05-04-ASSESSMENT.md');
|
|
248
|
+
assert.match(
|
|
249
|
+
result.assessmentPath.replace(/\\/g, '/'),
|
|
250
|
+
/\/\.gsd\/milestones\/M005-3qxqd7\/slices\/S04\/S04-ASSESSMENT\.md$/,
|
|
251
|
+
);
|
|
252
|
+
assert.ok(existsSync(correctPath), 'legacy ASSESSMENT should use S## filename inside slices/S##/');
|
|
253
|
+
assert.equal(existsSync(wrongFlatNameInLegacyDir), false, 'legacy slice dir must not receive flat-phase assessment filename');
|
|
254
|
+
assert.equal(existsSync(wrongFlatPhasePath), false, 'legacy reassessment must not be diverted to a coexisting flat-phase dir');
|
|
255
|
+
|
|
256
|
+
const assessment = getAssessment('.gsd/milestones/M005-3qxqd7/slices/S04/S04-ASSESSMENT.md');
|
|
257
|
+
assert.ok(assessment, 'assessment row should use the legacy-layout path');
|
|
258
|
+
} finally {
|
|
259
|
+
cleanup(base);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
|
|
213
263
|
test('handleReassessRoadmap cache invalidation: getMilestoneSlices reflects mutations', async () => {
|
|
214
264
|
const base = makeTmpBase();
|
|
215
265
|
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtempSync, mkdirSync, rmSync, readFileSync, existsSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
openDatabase,
|
|
9
|
+
closeDatabase,
|
|
10
|
+
insertMilestone,
|
|
11
|
+
insertSlice,
|
|
12
|
+
insertTask,
|
|
13
|
+
upsertTaskPlanning,
|
|
14
|
+
getTask,
|
|
15
|
+
getReplanHistory,
|
|
16
|
+
} from '../gsd-db.ts';
|
|
17
|
+
import { handleReplanTask } from '../tools/replan-task.ts';
|
|
18
|
+
|
|
19
|
+
function makeTmpBase(): string {
|
|
20
|
+
const base = mkdtempSync(join(tmpdir(), 'gsd-replan-task-'));
|
|
21
|
+
mkdirSync(join(base, '.gsd', 'phases', '01-test'), { recursive: true });
|
|
22
|
+
return base;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function cleanup(base: string): void {
|
|
26
|
+
try { closeDatabase(); } catch { /* noop */ }
|
|
27
|
+
try { rmSync(base, { recursive: true, force: true }); } catch { /* noop */ }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function seedTask(status = 'pending'): void {
|
|
31
|
+
insertMilestone({ id: 'M001', title: 'Test Milestone', status: 'active' });
|
|
32
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Test Slice', status: 'active', demo: 'Demo.' });
|
|
33
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'Original Task', status });
|
|
34
|
+
upsertTaskPlanning('M001', 'S01', 'T01', {
|
|
35
|
+
description: 'Original task description.',
|
|
36
|
+
estimate: '30m',
|
|
37
|
+
files: ['src/original.ts'],
|
|
38
|
+
verify: 'node --test original.test.ts',
|
|
39
|
+
inputs: ['src/original.ts'],
|
|
40
|
+
expectedOutput: ['src/original.ts'],
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function validReplanTaskParams() {
|
|
45
|
+
return {
|
|
46
|
+
milestoneId: 'M001',
|
|
47
|
+
sliceId: 'S01',
|
|
48
|
+
taskId: 'T01',
|
|
49
|
+
title: 'Replanned Task',
|
|
50
|
+
description: 'Updated task description with blocking rework scope.',
|
|
51
|
+
estimate: '45m',
|
|
52
|
+
files: ['src/replanned.ts'],
|
|
53
|
+
verify: 'node --test replanned.test.ts',
|
|
54
|
+
inputs: ['src/original.ts'],
|
|
55
|
+
expectedOutput: ['src/replanned.ts'],
|
|
56
|
+
reworkBriefRef: 'RB-001',
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
test('handleReplanTask updates one pending task plan and records a task-scoped replan history entry', async () => {
|
|
61
|
+
const base = makeTmpBase();
|
|
62
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
seedTask('pending');
|
|
66
|
+
|
|
67
|
+
const result = await handleReplanTask(validReplanTaskParams(), base);
|
|
68
|
+
assert.ok(!('error' in result), `unexpected error: ${'error' in result ? result.error : ''}`);
|
|
69
|
+
|
|
70
|
+
const task = getTask('M001', 'S01', 'T01');
|
|
71
|
+
assert.ok(task, 'task should still exist');
|
|
72
|
+
assert.equal(task.title, 'Replanned Task');
|
|
73
|
+
assert.equal(task.description, 'Updated task description with blocking rework scope.');
|
|
74
|
+
assert.equal(task.verify, 'node --test replanned.test.ts');
|
|
75
|
+
assert.deepEqual(task.files, ['src/replanned.ts']);
|
|
76
|
+
|
|
77
|
+
const history = getReplanHistory('M001', 'S01');
|
|
78
|
+
assert.ok(history.some((row) => row['task_id'] === 'T01' && String(row['summary']).includes('RB-001')));
|
|
79
|
+
|
|
80
|
+
const planPath = join(base, '.gsd', 'phases', '01-test', '01-01-PLAN.md');
|
|
81
|
+
assert.ok(existsSync(planPath), 'slice plan projection should be rendered');
|
|
82
|
+
const planContent = readFileSync(planPath, 'utf-8');
|
|
83
|
+
assert.match(planContent, /Replanned Task/);
|
|
84
|
+
assert.match(planContent, /node --test replanned\.test\.ts/);
|
|
85
|
+
} finally {
|
|
86
|
+
cleanup(base);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('handleReplanTask rejects completed tasks', async () => {
|
|
91
|
+
const base = makeTmpBase();
|
|
92
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
seedTask('complete');
|
|
96
|
+
|
|
97
|
+
const result = await handleReplanTask(validReplanTaskParams(), base);
|
|
98
|
+
assert.ok('error' in result);
|
|
99
|
+
assert.match(result.error, /cannot replan completed task T01/i);
|
|
100
|
+
} finally {
|
|
101
|
+
cleanup(base);
|
|
102
|
+
}
|
|
103
|
+
});
|