@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
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
import { cpSync, existsSync, mkdirSync, readdirSync, renameSync, rmSync, statSync } from "node:fs";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import { renderAllFromDb, renderRoadmapFromDb } from "./markdown-renderer.js";
|
|
7
|
-
import { deleteArtifactsByPathPrefix, getAllMilestones, getMilestoneSlices } from "./gsd-db.js";
|
|
7
|
+
import { deleteArtifactByPath, deleteArtifactsByPathPrefix, getAllMilestones, getArtifactsByPathPrefix, getMilestoneSlices, } from "./gsd-db.js";
|
|
8
8
|
import { migrateFromMarkdown } from "./md-importer.js";
|
|
9
9
|
import { countDbHierarchy } from "./migration-auto-check.js";
|
|
10
10
|
import { logWarning } from "./workflow-logger.js";
|
|
11
11
|
import { LAYOUT_SEGMENTS } from "./layout-policy.js";
|
|
12
|
-
import { canonicalPhaseDirName, dirIsContentBearingLegacyMilestone, milestonesDir, resolveMilestonePath, } from "./paths.js";
|
|
12
|
+
import { canonicalPhaseDirName, dirIsContentBearingLegacyMilestone, gsdProjectionRoot, milestonesDir, resolveMilestonePath, } from "./paths.js";
|
|
13
13
|
const LEGACY_MIGRATING_SEGMENT = "milestones.migrating";
|
|
14
14
|
const RETRYABLE_FS_ERROR_CODES = new Set(["EPERM", "EBUSY", "ENOTEMPTY"]);
|
|
15
15
|
const RM_RETRY_OPTIONS = { recursive: true, force: true, maxRetries: 5, retryDelay: 100 };
|
|
@@ -60,6 +60,46 @@ function expectedPhaseDirs(basePath) {
|
|
|
60
60
|
return getAllMilestones().map((milestone) => resolveMilestonePath(basePath, milestone.id) ??
|
|
61
61
|
join(milestonesDir(basePath), canonicalPhaseDirName(milestone.id, milestone.title)));
|
|
62
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Return the most-recent existing `.gsd-backups/migrate-<ts>/` snapshot, or null.
|
|
65
|
+
*
|
|
66
|
+
* The flat-phase migration can re-fire on later dispatches when the legacy
|
|
67
|
+
* `.gsd/milestones/` layout reappears (e.g. a marker-key mismatch re-triggers a
|
|
68
|
+
* whole-tree re-import — issue #1292). The DB was already reconciled from that
|
|
69
|
+
* tree before the backup step runs, so re-snapshotting an identical legacy tree
|
|
70
|
+
* on every dispatch only leaks a fresh `migrate-<ts>/` directory each time. When
|
|
71
|
+
* a prior snapshot already exists we reuse it as the rollback fallback instead
|
|
72
|
+
* of creating a duplicate, bounding the accumulation to one recovery copy.
|
|
73
|
+
*/
|
|
74
|
+
function existingMigrateBackup(basePath) {
|
|
75
|
+
const backupRoot = join(basePath, ".gsd-backups");
|
|
76
|
+
if (!existsSync(backupRoot))
|
|
77
|
+
return null;
|
|
78
|
+
try {
|
|
79
|
+
let latest = null;
|
|
80
|
+
for (const entry of readdirSync(backupRoot)) {
|
|
81
|
+
if (!entry.startsWith("migrate-"))
|
|
82
|
+
continue;
|
|
83
|
+
const dirPath = join(backupRoot, entry);
|
|
84
|
+
try {
|
|
85
|
+
const st = statSync(dirPath);
|
|
86
|
+
if (!st.isDirectory())
|
|
87
|
+
continue;
|
|
88
|
+
if (!hasLegacyMilestoneSubdirs(dirPath))
|
|
89
|
+
continue;
|
|
90
|
+
if (!latest || st.mtimeMs > latest.mtimeMs)
|
|
91
|
+
latest = { path: dirPath, mtimeMs: st.mtimeMs };
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// Non-fatal: skip unreadable entries.
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return latest?.path ?? null;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
63
103
|
function hasLegacyMilestoneSubdirs(dirPath) {
|
|
64
104
|
if (!existsSync(dirPath))
|
|
65
105
|
return false;
|
|
@@ -70,7 +110,58 @@ function hasLegacyMilestoneSubdirs(dirPath) {
|
|
|
70
110
|
return false;
|
|
71
111
|
}
|
|
72
112
|
}
|
|
73
|
-
function
|
|
113
|
+
function backupFlatProjectionIfPresent(basePath, phasesPath, backupDir) {
|
|
114
|
+
if (!existsSync(phasesPath))
|
|
115
|
+
return;
|
|
116
|
+
const phaseBackupDir = join(backupDir, "__phases");
|
|
117
|
+
try {
|
|
118
|
+
mkdirSync(backupDir, { recursive: true });
|
|
119
|
+
if (existsSync(phaseBackupDir)) {
|
|
120
|
+
removePathWithRetries(phaseBackupDir);
|
|
121
|
+
}
|
|
122
|
+
fsOps.cpSync(phasesPath, phaseBackupDir, { recursive: true, force: true });
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
logWarning("migration", `flat-phase projection backup failed: ${err.message}`);
|
|
126
|
+
throw err;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function isInsideFlatProjectionBackup(backupDir, src) {
|
|
130
|
+
const phaseBackupDir = join(backupDir, "__phases");
|
|
131
|
+
return src === phaseBackupDir || src.startsWith(`${phaseBackupDir}/`) || src.startsWith(`${phaseBackupDir}\\`);
|
|
132
|
+
}
|
|
133
|
+
function restoreFlatProjectionFromBackup(basePath, backupDir) {
|
|
134
|
+
const phaseBackupDir = join(backupDir, "__phases");
|
|
135
|
+
if (!existsSync(phaseBackupDir))
|
|
136
|
+
return;
|
|
137
|
+
const phasesPath = join(basePath, ".gsd", LAYOUT_SEGMENTS.level1);
|
|
138
|
+
try {
|
|
139
|
+
if (existsSync(phasesPath)) {
|
|
140
|
+
removePathWithRetries(phasesPath);
|
|
141
|
+
}
|
|
142
|
+
mkdirSync(join(basePath, ".gsd"), { recursive: true });
|
|
143
|
+
cpSync(phaseBackupDir, phasesPath, { recursive: true });
|
|
144
|
+
}
|
|
145
|
+
catch (restoreErr) {
|
|
146
|
+
logWarning("migration", `rollback: could not restore ${LAYOUT_SEGMENTS.level1}/ from backup: ${restoreErr.message}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function pruneStaleFlatPhaseArtifactRows(basePath) {
|
|
150
|
+
const projectionRoot = gsdProjectionRoot(basePath);
|
|
151
|
+
let pruned = 0;
|
|
152
|
+
for (const row of getArtifactsByPathPrefix(`${LAYOUT_SEGMENTS.level1}/`)) {
|
|
153
|
+
if (existsSync(join(projectionRoot, row.path)))
|
|
154
|
+
continue;
|
|
155
|
+
const staleTaskPlan = row.artifact_type.toUpperCase() === "PLAN" && Boolean(row.task_id);
|
|
156
|
+
const skippedEmptyArtifact = row.full_content.trim() === "";
|
|
157
|
+
if (!staleTaskPlan && !skippedEmptyArtifact)
|
|
158
|
+
continue;
|
|
159
|
+
deleteArtifactByPath(row.path);
|
|
160
|
+
pruned++;
|
|
161
|
+
}
|
|
162
|
+
return pruned;
|
|
163
|
+
}
|
|
164
|
+
function rollbackPartialMigration(basePath, backupDir, migratingPath, backupCreatedThisRun = true) {
|
|
74
165
|
// Remove the partially-written phases/ dir.
|
|
75
166
|
try {
|
|
76
167
|
removePathWithRetries(join(basePath, ".gsd", LAYOUT_SEGMENTS.level1));
|
|
@@ -83,7 +174,7 @@ function rollbackPartialMigration(basePath, backupDir, migratingPath) {
|
|
|
83
174
|
// preserved migrating tree (the sole surviving data source) and must never be
|
|
84
175
|
// removed here. Deleting the backup after a successful rollback keeps the gate
|
|
85
176
|
// from leaking one .gsd-backups/migrate-<ts>/ per session_start.
|
|
86
|
-
const isDisposableBackup = Boolean(backupDir) && backupDir !== migratingPath;
|
|
177
|
+
const isDisposableBackup = Boolean(backupDir) && backupDir !== migratingPath && backupCreatedThisRun;
|
|
87
178
|
const cleanupBackup = () => {
|
|
88
179
|
if (!isDisposableBackup || !existsSync(backupDir))
|
|
89
180
|
return;
|
|
@@ -99,11 +190,16 @@ function rollbackPartialMigration(basePath, backupDir, migratingPath) {
|
|
|
99
190
|
try {
|
|
100
191
|
if (migratingPath && existsSync(migratingPath) && !existsSync(milestonesPath)) {
|
|
101
192
|
movePathWithCopyDeleteFallback(migratingPath, milestonesPath);
|
|
193
|
+
restoreFlatProjectionFromBackup(basePath, backupDir);
|
|
102
194
|
cleanupBackup();
|
|
103
195
|
return;
|
|
104
196
|
}
|
|
105
197
|
if (existsSync(backupDir)) {
|
|
106
|
-
cpSync(backupDir, milestonesPath, {
|
|
198
|
+
cpSync(backupDir, milestonesPath, {
|
|
199
|
+
recursive: true,
|
|
200
|
+
filter: (src) => !isInsideFlatProjectionBackup(backupDir, src),
|
|
201
|
+
});
|
|
202
|
+
restoreFlatProjectionFromBackup(basePath, backupDir);
|
|
107
203
|
cleanupBackup();
|
|
108
204
|
}
|
|
109
205
|
if (migratingPath && existsSync(migratingPath)) {
|
|
@@ -204,17 +300,33 @@ export async function migrateToFlatPhase(basePath) {
|
|
|
204
300
|
return;
|
|
205
301
|
}
|
|
206
302
|
let backupDir = migratingPath;
|
|
303
|
+
let backupCreatedThisRun = false;
|
|
207
304
|
if (!resumingInterrupted) {
|
|
208
|
-
// 2. Backup (only reached when the DB has rows and migration will proceed)
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
305
|
+
// 2. Backup (only reached when the DB has rows and migration will proceed).
|
|
306
|
+
// migrateFromMarkdown above already reconciled the legacy tree into the DB,
|
|
307
|
+
// so its content is safely persisted. If a prior successful migration
|
|
308
|
+
// already snapshotted the legacy tree, a re-fire of this gate (issue #1292:
|
|
309
|
+
// marker-key mismatch re-importing the whole tree at dispatch boundaries)
|
|
310
|
+
// must not leak a fresh .gsd-backups/migrate-<ts>/ every dispatch. Treat it
|
|
311
|
+
// as a marker-refresh re-projection: reuse the existing snapshot as the
|
|
312
|
+
// rollback fallback instead of creating a duplicate.
|
|
313
|
+
const priorBackup = existingMigrateBackup(basePath);
|
|
314
|
+
if (priorBackup) {
|
|
315
|
+
backupDir = priorBackup;
|
|
316
|
+
logWarning("migration", `flat-phase migration re-fired; reusing existing backup ${priorBackup} instead of re-snapshotting (issue #1292)`);
|
|
214
317
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
318
|
+
else {
|
|
319
|
+
const ts = Date.now();
|
|
320
|
+
backupDir = join(basePath, ".gsd-backups", `migrate-${ts}`);
|
|
321
|
+
try {
|
|
322
|
+
mkdirSync(join(basePath, ".gsd-backups"), { recursive: true });
|
|
323
|
+
cpSync(milestonesPath, backupDir, { recursive: true });
|
|
324
|
+
backupCreatedThisRun = true;
|
|
325
|
+
}
|
|
326
|
+
catch (err) {
|
|
327
|
+
logWarning("migration", `flat-phase migration backup failed: ${err.message}`);
|
|
328
|
+
throw err;
|
|
329
|
+
}
|
|
218
330
|
}
|
|
219
331
|
if (existsSync(migratingPath)) {
|
|
220
332
|
removePathWithRetries(migratingPath);
|
|
@@ -230,14 +342,34 @@ export async function migrateToFlatPhase(basePath) {
|
|
|
230
342
|
throw err;
|
|
231
343
|
}
|
|
232
344
|
}
|
|
345
|
+
else {
|
|
346
|
+
const priorBackup = existingMigrateBackup(basePath);
|
|
347
|
+
if (priorBackup) {
|
|
348
|
+
backupDir = priorBackup;
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
const ts = Date.now();
|
|
352
|
+
backupDir = join(basePath, ".gsd-backups", `migrate-${ts}`);
|
|
353
|
+
try {
|
|
354
|
+
mkdirSync(join(basePath, ".gsd-backups"), { recursive: true });
|
|
355
|
+
cpSync(migratingPath, backupDir, { recursive: true });
|
|
356
|
+
backupCreatedThisRun = true;
|
|
357
|
+
}
|
|
358
|
+
catch (err) {
|
|
359
|
+
logWarning("migration", `flat-phase migration backup failed during resume: ${err.message}`);
|
|
360
|
+
throw err;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
233
364
|
// Clear any stale or partially-rendered flat projection before this run
|
|
234
365
|
// writes. Verification below checks the current DB render, not leftovers.
|
|
235
366
|
try {
|
|
367
|
+
backupFlatProjectionIfPresent(basePath, phasesPath, backupDir);
|
|
236
368
|
removePathWithRetries(phasesPath);
|
|
237
369
|
}
|
|
238
370
|
catch (err) {
|
|
239
371
|
logWarning("migration", `failed to clear stale phases/ before render: ${err.message}`);
|
|
240
|
-
rollbackPartialMigration(basePath, backupDir, migratingPath);
|
|
372
|
+
rollbackPartialMigration(basePath, backupDir, migratingPath, backupCreatedThisRun);
|
|
241
373
|
throw err;
|
|
242
374
|
}
|
|
243
375
|
// 4. Render flat-phase from DB
|
|
@@ -260,35 +392,38 @@ export async function migrateToFlatPhase(basePath) {
|
|
|
260
392
|
}
|
|
261
393
|
catch (err) {
|
|
262
394
|
logWarning("migration", `flat-phase render failed: ${err.message}`);
|
|
263
|
-
rollbackPartialMigration(basePath, backupDir, migratingPath);
|
|
395
|
+
rollbackPartialMigration(basePath, backupDir, migratingPath, backupCreatedThisRun);
|
|
264
396
|
throw err;
|
|
265
397
|
}
|
|
266
398
|
// 5. Verify render succeeded and flat-phase projection was written
|
|
267
399
|
if (renderResult.errors.length > 0) {
|
|
268
400
|
logWarning("migration", `flat-phase render had ${renderResult.errors.length} error(s): ${renderResult.errors.join("; ")}`);
|
|
269
|
-
rollbackPartialMigration(basePath, backupDir, migratingPath);
|
|
401
|
+
rollbackPartialMigration(basePath, backupDir, migratingPath, backupCreatedThisRun);
|
|
270
402
|
throw new Error(`flat-phase migration render failed: ${renderResult.errors.slice(0, 3).join("; ")}`);
|
|
271
403
|
}
|
|
272
404
|
const db = countDbHierarchy();
|
|
273
405
|
const missingPhaseDirs = expectedPhaseDirs(basePath).filter((phaseDir) => !existsSync(phaseDir));
|
|
274
406
|
if (db.milestones > 0 && missingPhaseDirs.length > 0) {
|
|
275
407
|
logWarning("migration", `flat-phase migration missing ${missingPhaseDirs.length} expected phase dir(s): ${missingPhaseDirs.join(", ")}`);
|
|
276
|
-
rollbackPartialMigration(basePath, backupDir, migratingPath);
|
|
408
|
+
rollbackPartialMigration(basePath, backupDir, migratingPath, backupCreatedThisRun);
|
|
277
409
|
throw new Error("flat-phase migration verification failed: missing rendered phase directories");
|
|
278
410
|
}
|
|
279
411
|
if (db.slices > 0 && renderResult.rendered === 0) {
|
|
280
412
|
logWarning("migration", "flat-phase migration verification failed: render produced no artifacts for populated DB");
|
|
281
|
-
rollbackPartialMigration(basePath, backupDir, migratingPath);
|
|
413
|
+
rollbackPartialMigration(basePath, backupDir, migratingPath, backupCreatedThisRun);
|
|
282
414
|
throw new Error("flat-phase migration verification failed: no artifacts rendered");
|
|
283
415
|
}
|
|
284
416
|
// 6. Verified — prune legacy artifact rows now that renderAllFromDb has
|
|
285
|
-
// re-inserted flat-phase rows for artifacts that still have files.
|
|
417
|
+
// re-inserted flat-phase rows for artifacts that still have files. Also
|
|
418
|
+
// prune flat-phase rows whose files the renderer intentionally no longer
|
|
419
|
+
// materializes, such as task PLAN files and empty-content artifacts.
|
|
286
420
|
try {
|
|
287
421
|
deleteArtifactsByPathPrefix("milestones/");
|
|
422
|
+
pruneStaleFlatPhaseArtifactRows(basePath);
|
|
288
423
|
}
|
|
289
424
|
catch (err) {
|
|
290
425
|
logWarning("migration", `flat-phase migration could not prune legacy artifact rows: ${err.message}`);
|
|
291
|
-
rollbackPartialMigration(basePath, backupDir, migratingPath);
|
|
426
|
+
rollbackPartialMigration(basePath, backupDir, migratingPath, backupCreatedThisRun);
|
|
292
427
|
throw err;
|
|
293
428
|
}
|
|
294
429
|
// 7. Remove the renamed legacy tree (backup already on disk).
|
|
@@ -19,7 +19,8 @@ import { createHash } from "node:crypto";
|
|
|
19
19
|
import { GSDError, GSD_STALE_STATE } from "./errors.js";
|
|
20
20
|
import { getGateIdsForTurn } from "./gate-registry.js";
|
|
21
21
|
import { logWarning } from "./workflow-logger.js";
|
|
22
|
-
import {
|
|
22
|
+
import { rowToArtifact } from "./db-milestone-artifact-rows.js";
|
|
23
|
+
import { isClosedStatus, toStatus } from "./status-guards.js";
|
|
23
24
|
// Connection ownership, lifecycle, schema/migrations and transaction
|
|
24
25
|
// primitives now live in the engine; re-export the full public surface so
|
|
25
26
|
// existing `from "./gsd-db.js"` imports keep working.
|
|
@@ -336,6 +337,11 @@ export function upsertSlicePlanning(milestoneId, sliceId, planning) {
|
|
|
336
337
|
export function insertTask(t) {
|
|
337
338
|
if (!getDbOrNull())
|
|
338
339
|
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
340
|
+
// Stamp completed_at for every terminal-complete alias (complete/done/closed),
|
|
341
|
+
// not just two literals — a task imported as "closed" is completed and must
|
|
342
|
+
// carry a timestamp. NOT for "skipped": a skipped task was never completed
|
|
343
|
+
// (cascade writers set its completed_at = NULL).
|
|
344
|
+
const isCompleteAlias = t.status != null && toStatus(t.status) === "complete";
|
|
339
345
|
getDbOrNull().prepare(`INSERT INTO tasks (
|
|
340
346
|
milestone_id, slice_id, id, title, status, one_liner, narrative,
|
|
341
347
|
verification_result, duration, completed_at, blocker_discovered,
|
|
@@ -385,7 +391,7 @@ export function insertTask(t) {
|
|
|
385
391
|
":narrative": t.narrative ?? "",
|
|
386
392
|
":verification_result": t.verificationResult ?? "",
|
|
387
393
|
":duration": t.duration ?? "",
|
|
388
|
-
":completed_at":
|
|
394
|
+
":completed_at": isCompleteAlias ? new Date().toISOString() : null,
|
|
389
395
|
":blocker_discovered": t.blockerDiscovered ? 1 : 0,
|
|
390
396
|
":deviations": t.deviations ?? "",
|
|
391
397
|
":known_issues": t.knownIssues ?? "",
|
|
@@ -401,7 +407,7 @@ export function insertTask(t) {
|
|
|
401
407
|
":observability_impact": t.planning?.observabilityImpact ?? "",
|
|
402
408
|
":full_plan_md": t.planning?.fullPlanMd ?? "",
|
|
403
409
|
":sequence": t.sequence ?? 0,
|
|
404
|
-
":preserve_completion": t.preserveCompletionMetadata &&
|
|
410
|
+
":preserve_completion": t.preserveCompletionMetadata && isCompleteAlias ? 1 : 0,
|
|
405
411
|
":target_repositories": JSON.stringify(t.planning?.targetRepositories ?? []),
|
|
406
412
|
":raw_target_repositories": t.planning && "targetRepositories" in t.planning
|
|
407
413
|
? JSON.stringify(t.planning.targetRepositories ?? [])
|
|
@@ -498,8 +504,8 @@ export function upsertTaskPlanning(milestoneId, sliceId, taskId, planning) {
|
|
|
498
504
|
":target_repositories": planning.targetRepositories ? JSON.stringify(planning.targetRepositories) : null,
|
|
499
505
|
});
|
|
500
506
|
}
|
|
501
|
-
export function updateSliceStatus(milestoneId, sliceId, status, completedAt) {
|
|
502
|
-
applyStatusTransition({ entity: "slice", milestoneId, sliceId, status, completedAt });
|
|
507
|
+
export function updateSliceStatus(milestoneId, sliceId, status, completedAt, preserveCompletion) {
|
|
508
|
+
applyStatusTransition({ entity: "slice", milestoneId, sliceId, status, completedAt, preserveCompletion });
|
|
503
509
|
}
|
|
504
510
|
export function setTaskSummaryMd(milestoneId, sliceId, taskId, md) {
|
|
505
511
|
if (!getDbOrNull())
|
|
@@ -611,8 +617,8 @@ function writeMilestoneStatus(milestoneId, status, completedAt) {
|
|
|
611
617
|
* advance planned milestones. They may not reopen a closed milestone; callers
|
|
612
618
|
* must use reopenMilestoneStatus(), which is reserved for gsd_milestone_reopen.
|
|
613
619
|
*/
|
|
614
|
-
export function updateMilestoneStatus(milestoneId, status, completedAt) {
|
|
615
|
-
applyStatusTransition({ entity: "milestone", milestoneId, status, completedAt });
|
|
620
|
+
export function updateMilestoneStatus(milestoneId, status, completedAt, preserveCompletion) {
|
|
621
|
+
applyStatusTransition({ entity: "milestone", milestoneId, status, completedAt, preserveCompletion });
|
|
616
622
|
}
|
|
617
623
|
/**
|
|
618
624
|
* Explicit closed -> active transition for gsd_milestone_reopen only.
|
|
@@ -657,6 +663,128 @@ export function insertReplanHistory(entry) {
|
|
|
657
663
|
":created_at": new Date().toISOString(),
|
|
658
664
|
});
|
|
659
665
|
}
|
|
666
|
+
function reworkBriefIdFromTask(milestoneId, sliceId, taskId) {
|
|
667
|
+
return `RB-${milestoneId}-${sliceId}-${taskId}`;
|
|
668
|
+
}
|
|
669
|
+
function normalizeReworkStatus(status) {
|
|
670
|
+
const normalized = String(status ?? "pending");
|
|
671
|
+
if (normalized === "resolved" || normalized === "deferred-with-override") {
|
|
672
|
+
return normalized;
|
|
673
|
+
}
|
|
674
|
+
return "pending";
|
|
675
|
+
}
|
|
676
|
+
function rowToReworkFinding(row) {
|
|
677
|
+
let verificationCommands = [];
|
|
678
|
+
try {
|
|
679
|
+
const parsed = JSON.parse(String(row["verification_commands"] ?? "[]"));
|
|
680
|
+
verificationCommands = Array.isArray(parsed) ? parsed.map(String) : [];
|
|
681
|
+
}
|
|
682
|
+
catch {
|
|
683
|
+
verificationCommands = [];
|
|
684
|
+
}
|
|
685
|
+
return {
|
|
686
|
+
brief_id: String(row["brief_id"] ?? ""),
|
|
687
|
+
finding_id: String(row["finding_id"] ?? ""),
|
|
688
|
+
severity: String(row["severity"] ?? "blocking") === "advisory" ? "advisory" : "blocking",
|
|
689
|
+
description: String(row["description"] ?? ""),
|
|
690
|
+
required_fix: String(row["required_fix"] ?? ""),
|
|
691
|
+
verification_commands: verificationCommands,
|
|
692
|
+
status: normalizeReworkStatus(row["status"]),
|
|
693
|
+
evidence: String(row["evidence"] ?? ""),
|
|
694
|
+
decision_ref: String(row["decision_ref"] ?? ""),
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
export function saveReworkBrief(entry) {
|
|
698
|
+
if (!getDbOrNull())
|
|
699
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
700
|
+
const briefId = entry.briefId?.trim() || reworkBriefIdFromTask(entry.milestoneId, entry.sliceId, entry.taskId);
|
|
701
|
+
const now = new Date().toISOString();
|
|
702
|
+
transaction(() => {
|
|
703
|
+
getDbOrNull().prepare(`INSERT INTO rework_briefs (id, milestone_id, slice_id, task_id, created_at, updated_at)
|
|
704
|
+
VALUES (:id, :mid, :sid, :tid, :created_at, :updated_at)
|
|
705
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
706
|
+
milestone_id = :mid,
|
|
707
|
+
slice_id = :sid,
|
|
708
|
+
task_id = :tid,
|
|
709
|
+
updated_at = :updated_at`).run({
|
|
710
|
+
":id": briefId,
|
|
711
|
+
":mid": entry.milestoneId,
|
|
712
|
+
":sid": entry.sliceId,
|
|
713
|
+
":tid": entry.taskId,
|
|
714
|
+
":created_at": now,
|
|
715
|
+
":updated_at": now,
|
|
716
|
+
});
|
|
717
|
+
getDbOrNull().prepare("DELETE FROM rework_brief_findings WHERE brief_id = :id").run({ ":id": briefId });
|
|
718
|
+
const stmt = getDbOrNull().prepare(`INSERT INTO rework_brief_findings (
|
|
719
|
+
brief_id, finding_id, severity, description, required_fix, verification_commands,
|
|
720
|
+
status, evidence, decision_ref, updated_at
|
|
721
|
+
) VALUES (
|
|
722
|
+
:brief_id, :finding_id, :severity, :description, :required_fix, :verification_commands,
|
|
723
|
+
:status, :evidence, :decision_ref, :updated_at
|
|
724
|
+
)`);
|
|
725
|
+
for (const finding of entry.findings) {
|
|
726
|
+
stmt.run({
|
|
727
|
+
":brief_id": briefId,
|
|
728
|
+
":finding_id": finding.findingId,
|
|
729
|
+
":severity": finding.severity,
|
|
730
|
+
":description": finding.description,
|
|
731
|
+
":required_fix": finding.requiredFix,
|
|
732
|
+
":verification_commands": JSON.stringify(finding.verificationCommands),
|
|
733
|
+
":status": finding.status ?? "pending",
|
|
734
|
+
":evidence": finding.evidence ?? "",
|
|
735
|
+
":decision_ref": finding.decisionRef ?? "",
|
|
736
|
+
":updated_at": now,
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
return { briefId };
|
|
741
|
+
}
|
|
742
|
+
export function getBlockingReworkFindingsForTask(milestoneId, sliceId, taskId) {
|
|
743
|
+
if (!getDbOrNull())
|
|
744
|
+
return [];
|
|
745
|
+
const rows = getDbOrNull().prepare(`SELECT f.*
|
|
746
|
+
FROM rework_brief_findings f
|
|
747
|
+
JOIN rework_briefs b ON b.id = f.brief_id
|
|
748
|
+
WHERE b.milestone_id = :mid
|
|
749
|
+
AND b.slice_id = :sid
|
|
750
|
+
AND b.task_id = :tid
|
|
751
|
+
AND f.severity = 'blocking'
|
|
752
|
+
ORDER BY b.created_at, f.finding_id`).all({ ":mid": milestoneId, ":sid": sliceId, ":tid": taskId });
|
|
753
|
+
return rows.map(rowToReworkFinding);
|
|
754
|
+
}
|
|
755
|
+
export function getUnresolvedBlockingReworkFindingsForTask(milestoneId, sliceId, taskId) {
|
|
756
|
+
return getBlockingReworkFindingsForTask(milestoneId, sliceId, taskId)
|
|
757
|
+
.filter((finding) => finding.status === "pending");
|
|
758
|
+
}
|
|
759
|
+
export function applyReworkResolutions(resolutions) {
|
|
760
|
+
if (!getDbOrNull())
|
|
761
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
762
|
+
const now = new Date().toISOString();
|
|
763
|
+
const stmt = getDbOrNull().prepare(`UPDATE rework_brief_findings
|
|
764
|
+
SET status = :status,
|
|
765
|
+
evidence = :evidence,
|
|
766
|
+
decision_ref = :decision_ref,
|
|
767
|
+
updated_at = :updated_at
|
|
768
|
+
WHERE finding_id = :finding_id
|
|
769
|
+
AND brief_id IN (
|
|
770
|
+
SELECT id FROM rework_briefs
|
|
771
|
+
WHERE milestone_id = :mid AND slice_id = :sid AND task_id = :tid
|
|
772
|
+
)`);
|
|
773
|
+
transaction(() => {
|
|
774
|
+
for (const resolution of resolutions) {
|
|
775
|
+
stmt.run({
|
|
776
|
+
":status": resolution.status,
|
|
777
|
+
":evidence": resolution.evidence,
|
|
778
|
+
":decision_ref": resolution.decisionRef ?? "",
|
|
779
|
+
":updated_at": now,
|
|
780
|
+
":finding_id": resolution.findingId,
|
|
781
|
+
":mid": resolution.milestoneId,
|
|
782
|
+
":sid": resolution.sliceId,
|
|
783
|
+
":tid": resolution.taskId,
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
});
|
|
787
|
+
}
|
|
660
788
|
export function insertAssessment(entry) {
|
|
661
789
|
if (!getDbOrNull())
|
|
662
790
|
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
@@ -767,44 +895,51 @@ export function saveGateResult(g) {
|
|
|
767
895
|
if (!getDbOrNull())
|
|
768
896
|
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
769
897
|
const evaluatedAt = new Date().toISOString();
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
: "
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
898
|
+
// Atomic: the gate verdict UPDATE and the gate_runs ledger INSERT must commit
|
|
899
|
+
// together. As two autocommits, a crash between them records a completed gate
|
|
900
|
+
// with no ledger row (the row Recovery Classification reads). transaction()
|
|
901
|
+
// is re-entrant, so callers already inside a transaction are safe. The
|
|
902
|
+
// throw-on-zero-changes stays inside so a missing gate row rolls back cleanly.
|
|
903
|
+
transaction(() => {
|
|
904
|
+
const result = getDbOrNull().prepare(`UPDATE quality_gates
|
|
905
|
+
SET status = 'complete', verdict = :verdict, rationale = :rationale,
|
|
906
|
+
findings = :findings, evaluated_at = :evaluated_at
|
|
907
|
+
WHERE milestone_id = :mid AND slice_id = :sid AND gate_id = :gid
|
|
908
|
+
AND (task_id = :tid OR (:tid = '' AND task_id IS NULL))`).run({
|
|
909
|
+
":mid": g.milestoneId,
|
|
910
|
+
":sid": g.sliceId,
|
|
911
|
+
":gid": g.gateId,
|
|
912
|
+
":tid": g.taskId ?? "",
|
|
913
|
+
":verdict": g.verdict,
|
|
914
|
+
":rationale": g.rationale,
|
|
915
|
+
":findings": g.findings,
|
|
916
|
+
":evaluated_at": evaluatedAt,
|
|
917
|
+
});
|
|
918
|
+
if ((result.changes ?? 0) === 0) {
|
|
919
|
+
throw new GSDError(GSD_STALE_STATE, `quality gate row not found for ${g.milestoneId}/${g.sliceId}/${g.gateId}${g.taskId ? `/${g.taskId}` : ""}`);
|
|
920
|
+
}
|
|
921
|
+
const outcome = g.verdict === "pass"
|
|
922
|
+
? "pass"
|
|
923
|
+
: g.verdict === "omitted"
|
|
924
|
+
? "manual-attention"
|
|
925
|
+
: "fail";
|
|
926
|
+
insertGateRun({
|
|
927
|
+
traceId: `quality-gate:${g.milestoneId}:${g.sliceId}`,
|
|
928
|
+
turnId: `gate:${g.gateId}:${g.taskId ?? "slice"}`,
|
|
929
|
+
gateId: g.gateId,
|
|
930
|
+
gateType: "quality-gate",
|
|
931
|
+
milestoneId: g.milestoneId,
|
|
932
|
+
sliceId: g.sliceId,
|
|
933
|
+
taskId: g.taskId ?? undefined,
|
|
934
|
+
outcome,
|
|
935
|
+
failureClass: outcome === "fail" ? "verification" : outcome === "manual-attention" ? "manual-attention" : "none",
|
|
936
|
+
rationale: g.rationale,
|
|
937
|
+
findings: g.findings,
|
|
938
|
+
attempt: 1,
|
|
939
|
+
maxAttempts: 1,
|
|
940
|
+
retryable: false,
|
|
941
|
+
evaluatedAt,
|
|
942
|
+
});
|
|
808
943
|
});
|
|
809
944
|
}
|
|
810
945
|
export function markAllGatesOmitted(milestoneId, sliceId) {
|
|
@@ -1012,6 +1147,13 @@ export function deleteArtifactsByPathPrefix(prefix) {
|
|
|
1012
1147
|
return Number(countRow?.["count"] ?? 0);
|
|
1013
1148
|
});
|
|
1014
1149
|
}
|
|
1150
|
+
/** List artifact rows whose paths share a DB-relative prefix. */
|
|
1151
|
+
export function getArtifactsByPathPrefix(prefix) {
|
|
1152
|
+
if (!getDbOrNull())
|
|
1153
|
+
return [];
|
|
1154
|
+
const rows = getDbOrNull().prepare("SELECT * FROM artifacts WHERE path LIKE :prefix ORDER BY path").all({ ":prefix": `${prefix}%` });
|
|
1155
|
+
return rows.map(rowToArtifact);
|
|
1156
|
+
}
|
|
1015
1157
|
/**
|
|
1016
1158
|
* Drop hierarchy rows in dependency order inside a transaction. Used by
|
|
1017
1159
|
* `gsd recover --confirm` to rebuild engine state from markdown.
|
|
@@ -603,6 +603,9 @@ export const _dispatchWorkflowForTest = dispatchWorkflow;
|
|
|
603
603
|
export function getDiscussableFutureMilestones(registry, activeMilestoneId) {
|
|
604
604
|
return registry.filter((m) => m.id !== activeMilestoneId && m.status !== "complete" && m.status !== "parked");
|
|
605
605
|
}
|
|
606
|
+
function findDiscussTargetMilestone(registry, milestoneId) {
|
|
607
|
+
return registry.find((m) => m.id === milestoneId || m.id.startsWith(`${milestoneId}-`));
|
|
608
|
+
}
|
|
606
609
|
function getStructuredQuestionsAvailability(pi, ctx) {
|
|
607
610
|
if (!ctx)
|
|
608
611
|
return "false";
|
|
@@ -964,12 +967,20 @@ export async function showDiscuss(ctx, pi, basePath, options) {
|
|
|
964
967
|
if (slash > 0) {
|
|
965
968
|
const mid = target.slice(0, slash);
|
|
966
969
|
const sid = target.slice(slash + 1);
|
|
967
|
-
const targetMilestone = state.registry
|
|
968
|
-
if (!targetMilestone
|
|
969
|
-
ctx.ui.notify(`Milestone ${mid}
|
|
970
|
+
const targetMilestone = findDiscussTargetMilestone(state.registry, mid);
|
|
971
|
+
if (!targetMilestone) {
|
|
972
|
+
ctx.ui.notify(`Milestone ${mid} was not found in the roadmap. Use /gsd new-milestone to add it, or /gsd status to see available milestones.`, "warning");
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
if (targetMilestone.status === "complete") {
|
|
976
|
+
ctx.ui.notify(`Milestone ${targetMilestone.id} is already complete.`, "info");
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
979
|
+
if (targetMilestone.status === "parked") {
|
|
980
|
+
ctx.ui.notify(`Milestone ${targetMilestone.id} is parked. Run /gsd unpark ${targetMilestone.id} to reactivate.`, "warning");
|
|
970
981
|
return;
|
|
971
982
|
}
|
|
972
|
-
const slices = await loadDiscussNormSlices(basePath,
|
|
983
|
+
const slices = await loadDiscussNormSlices(basePath, targetMilestone.id);
|
|
973
984
|
const chosen = slices.find((s) => s.id.toUpperCase() === sid.toUpperCase());
|
|
974
985
|
if (!chosen) {
|
|
975
986
|
ctx.ui.notify(`Slice ${target} was not found in discussable slices.`, "warning");
|
|
@@ -979,19 +990,27 @@ export async function showDiscuss(ctx, pi, basePath, options) {
|
|
|
979
990
|
ctx.ui.notify(`Slice ${target} is already complete; nothing to discuss.`, "info");
|
|
980
991
|
return;
|
|
981
992
|
}
|
|
982
|
-
const discussBasePath = resolveDiscussSliceBasePath(basePath,
|
|
983
|
-
const contextFile = resolveSliceFile(discussBasePath,
|
|
993
|
+
const discussBasePath = resolveDiscussSliceBasePath(basePath, targetMilestone.id);
|
|
994
|
+
const contextFile = resolveSliceFile(discussBasePath, targetMilestone.id, sid, "CONTEXT");
|
|
984
995
|
const sqAvail = getStructuredQuestionsAvailability(pi, ctx);
|
|
985
|
-
const prompt = await buildDiscussSlicePrompt(
|
|
996
|
+
const prompt = await buildDiscussSlicePrompt(targetMilestone.id, sid, chosen.title, discussBasePath, {
|
|
986
997
|
rediscuss: !!contextFile,
|
|
987
998
|
structuredQuestionsAvailable: sqAvail,
|
|
988
999
|
});
|
|
989
1000
|
await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "discuss-slice", { basePath: discussBasePath });
|
|
990
1001
|
return;
|
|
991
1002
|
}
|
|
992
|
-
const targetMilestone = state.registry
|
|
993
|
-
if (!targetMilestone
|
|
994
|
-
ctx.ui.notify(`Milestone ${target}
|
|
1003
|
+
const targetMilestone = findDiscussTargetMilestone(state.registry, target);
|
|
1004
|
+
if (!targetMilestone) {
|
|
1005
|
+
ctx.ui.notify(`Milestone ${target} was not found in the roadmap. Use /gsd new-milestone to add it, or /gsd status to see available milestones.`, "warning");
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
if (targetMilestone.status === "complete") {
|
|
1009
|
+
ctx.ui.notify(`Milestone ${targetMilestone.id} is already complete.`, "info");
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
if (targetMilestone.status === "parked") {
|
|
1013
|
+
ctx.ui.notify(`Milestone ${targetMilestone.id} is parked. Run /gsd unpark ${targetMilestone.id} to reactivate.`, "warning");
|
|
995
1014
|
return;
|
|
996
1015
|
}
|
|
997
1016
|
await dispatchDiscussForMilestone(ctx, pi, basePath, targetMilestone.id, targetMilestone.title, {});
|