@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
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { existsSync, unlinkSync } from "node:fs";
|
|
13
13
|
import { join } from "node:path";
|
|
14
14
|
import { isClosedStatus } from "../status-guards.js";
|
|
15
|
-
import { transaction, insertMilestone, insertSlice, insertTask, insertVerificationEvidence, getMilestone, getSlice, getTask, updateTaskStatus, deleteVerificationEvidence, saveGateResult, getPendingGatesForTurn, } from "../gsd-db.js";
|
|
15
|
+
import { transaction, insertMilestone, insertSlice, insertTask, insertVerificationEvidence, getMilestone, getSlice, getTask, updateTaskStatus, deleteVerificationEvidence, saveGateResult, getPendingGatesForTurn, getUnresolvedBlockingReworkFindingsForTask, getBlockingReworkFindingsForTask, applyReworkResolutions, } from "../gsd-db.js";
|
|
16
16
|
import { getWorkflowDatabasePath, ensureWorkflowDbAtPath } from "../db-workspace.js";
|
|
17
17
|
import { getGatesForTurn } from "../gate-registry.js";
|
|
18
18
|
import { gsdProjectionRoot, clearPathCache, resolveMilestonePath, resolveSlicePath } from "../paths.js";
|
|
@@ -107,6 +107,44 @@ export function normalizeListParam(value) {
|
|
|
107
107
|
* Build a TaskRow-shaped object from CompleteTaskParams so the unified
|
|
108
108
|
* renderSummaryContent() can be used at completion time (#2720).
|
|
109
109
|
*/
|
|
110
|
+
function normalizeReworkResolution(params) {
|
|
111
|
+
return (params.reworkResolution ?? []).map((resolution) => ({
|
|
112
|
+
milestoneId: params.milestoneId,
|
|
113
|
+
sliceId: params.sliceId,
|
|
114
|
+
taskId: params.taskId,
|
|
115
|
+
findingId: resolution.findingId,
|
|
116
|
+
status: resolution.status,
|
|
117
|
+
evidence: resolution.evidence,
|
|
118
|
+
decisionRef: resolution.decisionRef,
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
function unresolvedReworkError(missingFindingIds) {
|
|
122
|
+
const plural = missingFindingIds.length === 1 ? "finding" : "findings";
|
|
123
|
+
return `unresolved blocking rework ${plural}: ${missingFindingIds.join(", ")} — provide reworkResolution entries with status resolved and evidence, or status deferred-with-override with evidence and decisionRef, before completing the task`;
|
|
124
|
+
}
|
|
125
|
+
function satisfiesBlockingReworkFinding(resolution) {
|
|
126
|
+
if (resolution.evidence.trim().length === 0)
|
|
127
|
+
return false;
|
|
128
|
+
if (resolution.status === "resolved")
|
|
129
|
+
return true;
|
|
130
|
+
return (resolution.decisionRef ?? "").trim().length > 0;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Restore rework findings to their pre-completion state.
|
|
134
|
+
*
|
|
135
|
+
* The completion transaction flips blocking findings to resolved/
|
|
136
|
+
* deferred-with-override via applyReworkResolutions. When a later projection
|
|
137
|
+
* or escalation write fails and we compensate by reverting the task to
|
|
138
|
+
* `pending`, we must also revert those findings. Otherwise a retry sees no
|
|
139
|
+
* unresolved blocking findings (getUnresolvedBlockingReworkFindingsForTask
|
|
140
|
+
* only returns `pending` rows) and completes the task with the blocking gate
|
|
141
|
+
* silently skipped.
|
|
142
|
+
*/
|
|
143
|
+
function revertAppliedReworkResolutions(snapshot) {
|
|
144
|
+
if (snapshot.length === 0)
|
|
145
|
+
return;
|
|
146
|
+
applyReworkResolutions(snapshot);
|
|
147
|
+
}
|
|
110
148
|
function paramsToTaskRow(params, completedAt) {
|
|
111
149
|
return {
|
|
112
150
|
milestone_id: params.milestoneId,
|
|
@@ -182,6 +220,7 @@ export async function handleCompleteTask(params, basePath) {
|
|
|
182
220
|
let guardError = null;
|
|
183
221
|
let summaryMd = "";
|
|
184
222
|
let repairTaskSummaryRow = null;
|
|
223
|
+
let appliedReworkSnapshot = [];
|
|
185
224
|
const rollbackDbPath = getWorkflowDatabasePath();
|
|
186
225
|
// ── ADR-011 Phase 2: validate escalation payload BEFORE any side effects ─
|
|
187
226
|
// Building the artifact runs the full shape validation (2-4 options, unique
|
|
@@ -192,6 +231,7 @@ export async function handleCompleteTask(params, basePath) {
|
|
|
192
231
|
// no escalation recorded, and the loop would silently advance past it.
|
|
193
232
|
// The filesystem write happens later (after side effects) because that's
|
|
194
233
|
// the cheapest ordering and validation is where 99% of failures live.
|
|
234
|
+
const reworkResolutions = normalizeReworkResolution(params);
|
|
195
235
|
let validatedEscalationArtifact = null;
|
|
196
236
|
let escalationWriteEnabled = false;
|
|
197
237
|
if (params.escalation) {
|
|
@@ -231,6 +271,17 @@ export async function handleCompleteTask(params, basePath) {
|
|
|
231
271
|
return;
|
|
232
272
|
}
|
|
233
273
|
const existingTask = getTask(params.milestoneId, params.sliceId, params.taskId);
|
|
274
|
+
const unresolvedRework = getUnresolvedBlockingReworkFindingsForTask(params.milestoneId, params.sliceId, params.taskId);
|
|
275
|
+
const resolvedFindingIds = new Set(reworkResolutions
|
|
276
|
+
.filter(satisfiesBlockingReworkFinding)
|
|
277
|
+
.map((resolution) => resolution.findingId));
|
|
278
|
+
const missingFindingIds = unresolvedRework
|
|
279
|
+
.filter((finding) => !resolvedFindingIds.has(finding.finding_id))
|
|
280
|
+
.map((finding) => finding.finding_id);
|
|
281
|
+
if (missingFindingIds.length > 0) {
|
|
282
|
+
guardError = unresolvedReworkError(missingFindingIds);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
234
285
|
if (existingTask && isClosedStatus(existingTask.status)) {
|
|
235
286
|
// Stale-turn path: a timed-out turn that was superseded by recovery
|
|
236
287
|
// can still reach this code when its LLM call eventually returns and
|
|
@@ -278,6 +329,33 @@ export async function handleCompleteTask(params, basePath) {
|
|
|
278
329
|
keyDecisions: params.keyDecisions ?? [],
|
|
279
330
|
fullSummaryMd: summaryMd,
|
|
280
331
|
});
|
|
332
|
+
// Only persist resolutions that actually satisfy the evidence
|
|
333
|
+
// requirement. The guard above admits a finding as long as ONE satisfying
|
|
334
|
+
// entry exists, but applyReworkResolutions writes by findingId and lets the
|
|
335
|
+
// last entry win. Persisting every entry would let a later non-satisfying
|
|
336
|
+
// duplicate (empty evidence, or deferred-with-override without decisionRef)
|
|
337
|
+
// overwrite a valid resolution and leave the finding non-pending without
|
|
338
|
+
// acceptable evidence. Filtering with the same predicate the guard uses
|
|
339
|
+
// keeps the applied set and the gate consistent.
|
|
340
|
+
const resolutionsToApply = reworkResolutions.filter(satisfiesBlockingReworkFinding);
|
|
341
|
+
if (resolutionsToApply.length > 0) {
|
|
342
|
+
// Snapshot the pre-resolution state of the findings we're about to
|
|
343
|
+
// change so a compensating rollback can restore them exactly (see
|
|
344
|
+
// revertAppliedReworkResolutions).
|
|
345
|
+
const resolvedFindingIdsForSnapshot = new Set(resolutionsToApply.map((r) => r.findingId));
|
|
346
|
+
appliedReworkSnapshot = getBlockingReworkFindingsForTask(params.milestoneId, params.sliceId, params.taskId)
|
|
347
|
+
.filter((finding) => resolvedFindingIdsForSnapshot.has(finding.finding_id))
|
|
348
|
+
.map((finding) => ({
|
|
349
|
+
milestoneId: params.milestoneId,
|
|
350
|
+
sliceId: params.sliceId,
|
|
351
|
+
taskId: params.taskId,
|
|
352
|
+
findingId: finding.finding_id,
|
|
353
|
+
status: finding.status,
|
|
354
|
+
evidence: finding.evidence,
|
|
355
|
+
decisionRef: finding.decision_ref,
|
|
356
|
+
}));
|
|
357
|
+
applyReworkResolutions(resolutionsToApply);
|
|
358
|
+
}
|
|
281
359
|
for (const evidence of (params.verificationEvidence ?? [])) {
|
|
282
360
|
insertVerificationEvidence({
|
|
283
361
|
taskId: params.taskId,
|
|
@@ -340,6 +418,7 @@ export async function handleCompleteTask(params, basePath) {
|
|
|
340
418
|
ensureWorkflowDbAtPath(rollbackDbPath);
|
|
341
419
|
deleteVerificationEvidence(params.milestoneId, params.sliceId, params.taskId);
|
|
342
420
|
updateTaskStatus(params.milestoneId, params.sliceId, params.taskId, "pending");
|
|
421
|
+
revertAppliedReworkResolutions(appliedReworkSnapshot);
|
|
343
422
|
invalidateStateCache();
|
|
344
423
|
rollbackSucceeded = true;
|
|
345
424
|
}
|
|
@@ -430,6 +509,7 @@ export async function handleCompleteTask(params, basePath) {
|
|
|
430
509
|
try {
|
|
431
510
|
deleteVerificationEvidence(params.milestoneId, params.sliceId, params.taskId);
|
|
432
511
|
updateTaskStatus(params.milestoneId, params.sliceId, params.taskId, 'pending');
|
|
512
|
+
revertAppliedReworkResolutions(appliedReworkSnapshot);
|
|
433
513
|
invalidateStateCache();
|
|
434
514
|
logWarning("tool", `complete-task rolled back DB completion for ${params.milestoneId}/${params.sliceId}/${params.taskId} after escalation write failure; SUMMARY.md left on disk for retry.`);
|
|
435
515
|
}
|
|
@@ -447,6 +527,7 @@ export async function handleCompleteTask(params, basePath) {
|
|
|
447
527
|
try {
|
|
448
528
|
deleteVerificationEvidence(params.milestoneId, params.sliceId, params.taskId);
|
|
449
529
|
updateTaskStatus(params.milestoneId, params.sliceId, params.taskId, 'pending');
|
|
530
|
+
revertAppliedReworkResolutions(appliedReworkSnapshot);
|
|
450
531
|
invalidateStateCache();
|
|
451
532
|
logWarning("tool", `complete-task rolled back DB completion for ${params.milestoneId}/${params.sliceId}/${params.taskId} because hard-blocker escalation handling is disabled; SUMMARY.md left on disk for retry.`);
|
|
452
533
|
}
|
|
@@ -5,7 +5,7 @@ import { getGateIdsForTurn } from "../gate-registry.js";
|
|
|
5
5
|
import { transaction, getSlice, getTask, insertTask, upsertTaskPlanning, insertGateRow, setSliceSketchFlag } from "../gsd-db.js";
|
|
6
6
|
import { invalidateStateCache } from "../state.js";
|
|
7
7
|
import { renderTaskPlanFromDb, renderPlanFromDb } from "../markdown-renderer.js";
|
|
8
|
-
import {
|
|
8
|
+
import { resolveMilestonePath, resolveSlicePath } from "../paths.js";
|
|
9
9
|
import { flushWorkflowProjections } from "../projection-flush.js";
|
|
10
10
|
import { writeManifest } from "../workflow-manifest.js";
|
|
11
11
|
import { appendEvent } from "../workflow-events.js";
|
|
@@ -193,21 +193,19 @@ export async function handlePlanTask(rawParams, basePath) {
|
|
|
193
193
|
return { error: guardError };
|
|
194
194
|
}
|
|
195
195
|
try {
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
// Guard: resolveTasksDir is null in flat-phase (no tasks/ subdir exists).
|
|
196
|
+
const milestonePath = resolveMilestonePath(basePath, params.milestoneId);
|
|
197
|
+
const slicePath = resolveSlicePath(basePath, params.milestoneId, params.sliceId);
|
|
198
|
+
const isLegacySliceLayout = Boolean(milestonePath && slicePath && slicePath !== milestonePath);
|
|
199
|
+
let renderedPath;
|
|
201
200
|
let slicePlanSynced = false;
|
|
202
|
-
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
await renderPlanFromDb(basePath, params.milestoneId, params.sliceId);
|
|
206
|
-
slicePlanSynced = true;
|
|
207
|
-
}
|
|
201
|
+
if (isLegacySliceLayout) {
|
|
202
|
+
const renderResult = await renderTaskPlanFromDb(basePath, params.milestoneId, params.sliceId, params.taskId);
|
|
203
|
+
renderedPath = renderResult.taskPlanPath;
|
|
208
204
|
}
|
|
209
|
-
|
|
210
|
-
|
|
205
|
+
else {
|
|
206
|
+
const renderResult = await renderPlanFromDb(basePath, params.milestoneId, params.sliceId);
|
|
207
|
+
renderedPath = renderResult.planPath;
|
|
208
|
+
slicePlanSynced = true;
|
|
211
209
|
}
|
|
212
210
|
if (slicePlanSynced) {
|
|
213
211
|
setSliceSketchFlag(params.milestoneId, params.sliceId, false);
|
|
@@ -234,7 +232,7 @@ export async function handlePlanTask(rawParams, basePath) {
|
|
|
234
232
|
milestoneId: params.milestoneId,
|
|
235
233
|
sliceId: params.sliceId,
|
|
236
234
|
taskId: params.taskId,
|
|
237
|
-
taskPlanPath:
|
|
235
|
+
taskPlanPath: renderedPath,
|
|
238
236
|
};
|
|
239
237
|
}
|
|
240
238
|
catch (err) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, unlinkSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import {
|
|
2
|
+
import { join, relative } from "node:path";
|
|
3
|
+
import { gsdProjectionRoot, gsdRoot, resolveMilestoneFile, resolveMilestonePath, targetMilestoneFile } from "../paths.js";
|
|
4
4
|
import { clearParseCache } from "../files.js";
|
|
5
5
|
import { isClosedStatus } from "../status-guards.js";
|
|
6
6
|
import { isNonEmptyString } from "../validation.js";
|
|
@@ -11,6 +11,14 @@ import { flushWorkflowProjections } from "../projection-flush.js";
|
|
|
11
11
|
import { writeManifest } from "../workflow-manifest.js";
|
|
12
12
|
import { appendEvent } from "../workflow-events.js";
|
|
13
13
|
import { logWarning } from "../workflow-logger.js";
|
|
14
|
+
function assessmentDbPathForRenderedFile(basePath, absPath) {
|
|
15
|
+
const projectionRoot = gsdProjectionRoot(basePath);
|
|
16
|
+
const projectionRel = relative(projectionRoot, absPath);
|
|
17
|
+
const root = projectionRel && !projectionRel.startsWith("..") && !projectionRel.startsWith("/")
|
|
18
|
+
? projectionRoot
|
|
19
|
+
: gsdRoot(basePath);
|
|
20
|
+
return `.gsd/${relative(root, absPath).replace(/\\/g, "/")}`;
|
|
21
|
+
}
|
|
14
22
|
function validateParams(params) {
|
|
15
23
|
if (!isNonEmptyString(params?.milestoneId))
|
|
16
24
|
throw new Error("milestoneId is required");
|
|
@@ -74,10 +82,6 @@ export async function handleReassessRoadmap(rawParams, basePath) {
|
|
|
74
82
|
catch (err) {
|
|
75
83
|
return { error: `validation failed: ${err.message}` };
|
|
76
84
|
}
|
|
77
|
-
// ── Compute assessment artifact path ──────────────────────────────
|
|
78
|
-
// Assessment lives in the completed slice's directory.
|
|
79
|
-
// Use relSliceFile so the path is layout-aware (flat-phase or legacy).
|
|
80
|
-
const assessmentRelPath = relSliceFile(basePath, params.milestoneId, params.completedSliceId, "ASSESSMENT");
|
|
81
85
|
// ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
|
|
82
86
|
// Guards must be inside the transaction so the state they check cannot
|
|
83
87
|
// change between the read and the write (#2723).
|
|
@@ -152,15 +156,6 @@ export async function handleReassessRoadmap(rawParams, basePath) {
|
|
|
152
156
|
}
|
|
153
157
|
}
|
|
154
158
|
}
|
|
155
|
-
// Record assessment
|
|
156
|
-
insertAssessment({
|
|
157
|
-
path: assessmentRelPath,
|
|
158
|
-
milestoneId: params.milestoneId,
|
|
159
|
-
sliceId: params.completedSliceId,
|
|
160
|
-
status: params.verdict,
|
|
161
|
-
scope: "roadmap",
|
|
162
|
-
fullContent: params.assessment,
|
|
163
|
-
});
|
|
164
159
|
// Apply slice modifications
|
|
165
160
|
for (const mod of params.sliceChanges.modified) {
|
|
166
161
|
updateSliceFields(params.milestoneId, mod.sliceId, {
|
|
@@ -220,6 +215,14 @@ export async function handleReassessRoadmap(rawParams, basePath) {
|
|
|
220
215
|
assessment: params.assessment,
|
|
221
216
|
completedSliceId: params.completedSliceId,
|
|
222
217
|
});
|
|
218
|
+
insertAssessment({
|
|
219
|
+
path: assessmentDbPathForRenderedFile(basePath, assessmentResult.assessmentPath),
|
|
220
|
+
milestoneId: params.milestoneId,
|
|
221
|
+
sliceId: params.completedSliceId,
|
|
222
|
+
status: params.verdict,
|
|
223
|
+
scope: "roadmap",
|
|
224
|
+
fullContent: params.assessment,
|
|
225
|
+
});
|
|
223
226
|
// ── Remove stale VALIDATION file from disk (#2957) ────────────
|
|
224
227
|
const hasStructuralChanges = params.sliceChanges.added.length > 0 ||
|
|
225
228
|
params.sliceChanges.modified.length > 0 ||
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { clearParseCache } from "../files.js";
|
|
2
|
+
import { getSlice, getTask, insertReplanHistory, transaction, upsertTaskPlanning, } from "../gsd-db.js";
|
|
3
|
+
import { invalidateStateCache } from "../state.js";
|
|
4
|
+
import { isClosedStatus } from "../status-guards.js";
|
|
5
|
+
import { isNonEmptyString, validateStringArray } from "../validation.js";
|
|
6
|
+
import { renderPlanFromDb, renderTaskPlanFromDb } from "../markdown-renderer.js";
|
|
7
|
+
import { resolveMilestonePath, resolveSlicePath } from "../paths.js";
|
|
8
|
+
import { flushWorkflowProjections } from "../projection-flush.js";
|
|
9
|
+
import { writeManifest } from "../workflow-manifest.js";
|
|
10
|
+
import { appendEvent } from "../workflow-events.js";
|
|
11
|
+
import { logWarning } from "../workflow-logger.js";
|
|
12
|
+
function validateParams(params) {
|
|
13
|
+
if (!isNonEmptyString(params?.milestoneId))
|
|
14
|
+
throw new Error("milestoneId is required");
|
|
15
|
+
if (!isNonEmptyString(params?.sliceId))
|
|
16
|
+
throw new Error("sliceId is required");
|
|
17
|
+
if (!isNonEmptyString(params?.taskId))
|
|
18
|
+
throw new Error("taskId is required");
|
|
19
|
+
if (!isNonEmptyString(params?.title))
|
|
20
|
+
throw new Error("title is required");
|
|
21
|
+
if (!isNonEmptyString(params?.description))
|
|
22
|
+
throw new Error("description is required");
|
|
23
|
+
if (!isNonEmptyString(params?.estimate))
|
|
24
|
+
throw new Error("estimate is required");
|
|
25
|
+
if (!isNonEmptyString(params?.verify))
|
|
26
|
+
throw new Error("verify is required");
|
|
27
|
+
return {
|
|
28
|
+
...params,
|
|
29
|
+
files: validateStringArray(params.files, "files"),
|
|
30
|
+
inputs: validateStringArray(params.inputs, "inputs"),
|
|
31
|
+
expectedOutput: validateStringArray(params.expectedOutput, "expectedOutput"),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function replanSummary(params) {
|
|
35
|
+
const ref = params.reworkBriefRef?.trim();
|
|
36
|
+
return ref
|
|
37
|
+
? `Task ${params.taskId} replanned from rework brief ${ref}`
|
|
38
|
+
: `Task ${params.taskId} replanned`;
|
|
39
|
+
}
|
|
40
|
+
export async function handleReplanTask(rawParams, basePath) {
|
|
41
|
+
let params;
|
|
42
|
+
try {
|
|
43
|
+
params = validateParams(rawParams);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
return { error: `validation failed: ${err.message}` };
|
|
47
|
+
}
|
|
48
|
+
let guardError = null;
|
|
49
|
+
try {
|
|
50
|
+
transaction(() => {
|
|
51
|
+
const parentSlice = getSlice(params.milestoneId, params.sliceId);
|
|
52
|
+
if (!parentSlice) {
|
|
53
|
+
guardError = `missing parent slice: ${params.milestoneId}/${params.sliceId}`;
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (isClosedStatus(parentSlice.status)) {
|
|
57
|
+
guardError = `cannot replan a task in a closed slice: ${params.sliceId} (status: ${parentSlice.status})`;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const task = getTask(params.milestoneId, params.sliceId, params.taskId);
|
|
61
|
+
if (!task) {
|
|
62
|
+
guardError = `task not found: ${params.milestoneId}/${params.sliceId}/${params.taskId}`;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (isClosedStatus(task.status)) {
|
|
66
|
+
guardError = `cannot replan completed task ${params.taskId} — use gsd_task_reopen first`;
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
upsertTaskPlanning(params.milestoneId, params.sliceId, params.taskId, {
|
|
70
|
+
title: params.title,
|
|
71
|
+
description: params.description,
|
|
72
|
+
estimate: params.estimate,
|
|
73
|
+
files: params.files,
|
|
74
|
+
verify: params.verify,
|
|
75
|
+
inputs: params.inputs,
|
|
76
|
+
expectedOutput: params.expectedOutput,
|
|
77
|
+
fullPlanMd: params.fullPlanMd,
|
|
78
|
+
});
|
|
79
|
+
insertReplanHistory({
|
|
80
|
+
milestoneId: params.milestoneId,
|
|
81
|
+
sliceId: params.sliceId,
|
|
82
|
+
taskId: params.taskId,
|
|
83
|
+
summary: replanSummary(params),
|
|
84
|
+
previousArtifactPath: params.reworkBriefRef ?? null,
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
return { error: `db write failed: ${err.message}` };
|
|
90
|
+
}
|
|
91
|
+
if (guardError)
|
|
92
|
+
return { error: guardError };
|
|
93
|
+
try {
|
|
94
|
+
const milestonePath = resolveMilestonePath(basePath, params.milestoneId);
|
|
95
|
+
const slicePath = resolveSlicePath(basePath, params.milestoneId, params.sliceId);
|
|
96
|
+
const isLegacySliceLayout = Boolean(milestonePath && slicePath && slicePath !== milestonePath);
|
|
97
|
+
const renderResult = isLegacySliceLayout
|
|
98
|
+
? await renderTaskPlanFromDb(basePath, params.milestoneId, params.sliceId, params.taskId)
|
|
99
|
+
: await renderPlanFromDb(basePath, params.milestoneId, params.sliceId);
|
|
100
|
+
const taskPlanPath = "taskPlanPath" in renderResult ? renderResult.taskPlanPath : renderResult.planPath;
|
|
101
|
+
invalidateStateCache();
|
|
102
|
+
clearParseCache();
|
|
103
|
+
try {
|
|
104
|
+
await flushWorkflowProjections(basePath, { milestoneId: params.milestoneId });
|
|
105
|
+
writeManifest(basePath);
|
|
106
|
+
appendEvent(basePath, {
|
|
107
|
+
cmd: "replan-task",
|
|
108
|
+
params: { milestoneId: params.milestoneId, sliceId: params.sliceId, taskId: params.taskId },
|
|
109
|
+
ts: new Date().toISOString(),
|
|
110
|
+
actor: "agent",
|
|
111
|
+
actor_name: params.actorName,
|
|
112
|
+
trigger_reason: params.triggerReason,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
catch (hookErr) {
|
|
116
|
+
logWarning("tool", `replan-task post-mutation hook warning: ${hookErr.message}`);
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
milestoneId: params.milestoneId,
|
|
120
|
+
sliceId: params.sliceId,
|
|
121
|
+
taskId: params.taskId,
|
|
122
|
+
taskPlanPath,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
return { error: `render failed: ${err.message}` };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { saveReworkBrief } from "../gsd-db.js";
|
|
2
|
+
import { isNonEmptyString, validateStringArray } from "../validation.js";
|
|
3
|
+
function validateFinding(finding, index) {
|
|
4
|
+
if (!isNonEmptyString(finding?.findingId))
|
|
5
|
+
throw new Error(`findings[${index}].findingId is required`);
|
|
6
|
+
if (finding.severity !== "blocking" && finding.severity !== "advisory") {
|
|
7
|
+
throw new Error(`findings[${index}].severity must be blocking or advisory`);
|
|
8
|
+
}
|
|
9
|
+
if (!isNonEmptyString(finding.description))
|
|
10
|
+
throw new Error(`findings[${index}].description is required`);
|
|
11
|
+
if (!isNonEmptyString(finding.requiredFix))
|
|
12
|
+
throw new Error(`findings[${index}].requiredFix is required`);
|
|
13
|
+
const status = finding.status ?? "pending";
|
|
14
|
+
if (status !== "pending" && status !== "resolved" && status !== "deferred-with-override") {
|
|
15
|
+
throw new Error(`findings[${index}].status must be pending, resolved, or deferred-with-override`);
|
|
16
|
+
}
|
|
17
|
+
// A blocking finding is only enforced by gsd_task_complete while it is
|
|
18
|
+
// `pending` (getUnresolvedBlockingReworkFindingsForTask returns pending rows
|
|
19
|
+
// only). Persisting a blocking finding as resolved/deferred-with-override
|
|
20
|
+
// at save time therefore bypasses the completion gate entirely, so require
|
|
21
|
+
// the same justification a reworkResolution would (evidence for `resolved`,
|
|
22
|
+
// evidence + decisionRef for `deferred-with-override`). Advisory findings do
|
|
23
|
+
// not gate completion, so their status is left as-is.
|
|
24
|
+
if (finding.severity === "blocking" && status !== "pending") {
|
|
25
|
+
if (!isNonEmptyString(finding.evidence)) {
|
|
26
|
+
throw new Error(`findings[${index}].evidence is required when a blocking finding is saved as ${status}`);
|
|
27
|
+
}
|
|
28
|
+
if (status === "deferred-with-override" && !isNonEmptyString(finding.decisionRef)) {
|
|
29
|
+
throw new Error(`findings[${index}].decisionRef is required when a blocking finding is saved as deferred-with-override`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
...finding,
|
|
34
|
+
verificationCommands: validateStringArray(finding.verificationCommands, `findings[${index}].verificationCommands`),
|
|
35
|
+
status,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function validateParams(params) {
|
|
39
|
+
if (!isNonEmptyString(params?.milestoneId))
|
|
40
|
+
throw new Error("milestoneId is required");
|
|
41
|
+
if (!isNonEmptyString(params?.sliceId))
|
|
42
|
+
throw new Error("sliceId is required");
|
|
43
|
+
if (!isNonEmptyString(params?.taskId))
|
|
44
|
+
throw new Error("taskId is required");
|
|
45
|
+
if (!Array.isArray(params.findings) || params.findings.length === 0) {
|
|
46
|
+
throw new Error("findings must be a non-empty array");
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
...params,
|
|
50
|
+
findings: params.findings.map(validateFinding),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export async function handleReworkBriefSave(rawParams) {
|
|
54
|
+
let params;
|
|
55
|
+
try {
|
|
56
|
+
params = validateParams(rawParams);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
return { error: `validation failed: ${err.message}` };
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const { briefId } = saveReworkBrief(params);
|
|
63
|
+
return {
|
|
64
|
+
briefId,
|
|
65
|
+
milestoneId: params.milestoneId,
|
|
66
|
+
sliceId: params.sliceId,
|
|
67
|
+
taskId: params.taskId,
|
|
68
|
+
findingCount: params.findings.length,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
return { error: `db write failed: ${err.message}` };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -18,6 +18,8 @@ import { handleCompleteSlice } from "./complete-slice.js";
|
|
|
18
18
|
import { handlePlanMilestone } from "./plan-milestone.js";
|
|
19
19
|
import { handlePlanSlice } from "./plan-slice.js";
|
|
20
20
|
import { handleReplanSlice } from "./replan-slice.js";
|
|
21
|
+
import { handleReplanTask } from "./replan-task.js";
|
|
22
|
+
import { handleReworkBriefSave } from "./rework-brief.js";
|
|
21
23
|
import { handleReopenMilestone } from "./reopen-milestone.js";
|
|
22
24
|
import { handleReopenSlice } from "./reopen-slice.js";
|
|
23
25
|
import { handleReopenTask } from "./reopen-task.js";
|
|
@@ -1277,6 +1279,85 @@ export async function executePlanSlice(params, basePath = process.cwd()) {
|
|
|
1277
1279
|
};
|
|
1278
1280
|
}
|
|
1279
1281
|
}
|
|
1282
|
+
export async function executeReplanTask(params, basePath = process.cwd()) {
|
|
1283
|
+
const dbAvailable = await ensureDbOpen(basePath);
|
|
1284
|
+
if (!dbAvailable) {
|
|
1285
|
+
return {
|
|
1286
|
+
content: [{ type: "text", text: "Error: GSD database is not available. Cannot replan task." }],
|
|
1287
|
+
details: { operation: "replan_task", error: "db_unavailable" },
|
|
1288
|
+
isError: true,
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1291
|
+
try {
|
|
1292
|
+
const result = await handleReplanTask(params, basePath);
|
|
1293
|
+
if ("error" in result) {
|
|
1294
|
+
return {
|
|
1295
|
+
content: [{ type: "text", text: `Error replanning task: ${result.error}` }],
|
|
1296
|
+
details: { operation: "replan_task", error: result.error },
|
|
1297
|
+
isError: true,
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1300
|
+
return {
|
|
1301
|
+
content: [{ type: "text", text: `Replanned task ${result.taskId} (${result.sliceId}/${result.milestoneId})` }],
|
|
1302
|
+
details: {
|
|
1303
|
+
operation: "replan_task",
|
|
1304
|
+
milestoneId: result.milestoneId,
|
|
1305
|
+
sliceId: result.sliceId,
|
|
1306
|
+
taskId: result.taskId,
|
|
1307
|
+
taskPlanPath: result.taskPlanPath,
|
|
1308
|
+
},
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
catch (err) {
|
|
1312
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1313
|
+
logError("tool", `replan_task tool failed: ${msg}`, { tool: "gsd_replan_task", error: String(err) });
|
|
1314
|
+
return {
|
|
1315
|
+
content: [{ type: "text", text: `Error replanning task: ${msg}` }],
|
|
1316
|
+
details: { operation: "replan_task", error: msg },
|
|
1317
|
+
isError: true,
|
|
1318
|
+
};
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
export async function executeReworkBriefSave(params, basePath = process.cwd()) {
|
|
1322
|
+
const dbAvailable = await ensureDbOpen(basePath);
|
|
1323
|
+
if (!dbAvailable) {
|
|
1324
|
+
return {
|
|
1325
|
+
content: [{ type: "text", text: "Error: GSD database is not available. Cannot save rework brief." }],
|
|
1326
|
+
details: { operation: "rework_brief_save", error: "db_unavailable" },
|
|
1327
|
+
isError: true,
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
try {
|
|
1331
|
+
const result = await handleReworkBriefSave(params);
|
|
1332
|
+
if ("error" in result) {
|
|
1333
|
+
return {
|
|
1334
|
+
content: [{ type: "text", text: `Error saving rework brief: ${result.error}` }],
|
|
1335
|
+
details: { operation: "rework_brief_save", error: result.error },
|
|
1336
|
+
isError: true,
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
return {
|
|
1340
|
+
content: [{ type: "text", text: `Saved rework brief ${result.briefId} with ${result.findingCount} finding(s)` }],
|
|
1341
|
+
details: {
|
|
1342
|
+
operation: "rework_brief_save",
|
|
1343
|
+
briefId: result.briefId,
|
|
1344
|
+
milestoneId: result.milestoneId,
|
|
1345
|
+
sliceId: result.sliceId,
|
|
1346
|
+
taskId: result.taskId,
|
|
1347
|
+
findingCount: result.findingCount,
|
|
1348
|
+
},
|
|
1349
|
+
};
|
|
1350
|
+
}
|
|
1351
|
+
catch (err) {
|
|
1352
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1353
|
+
logError("tool", `rework_brief_save tool failed: ${msg}`, { tool: "gsd_rework_brief_save", error: String(err) });
|
|
1354
|
+
return {
|
|
1355
|
+
content: [{ type: "text", text: `Error saving rework brief: ${msg}` }],
|
|
1356
|
+
details: { operation: "rework_brief_save", error: msg },
|
|
1357
|
+
isError: true,
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1280
1361
|
export async function executeReplanSlice(params, basePath = process.cwd()) {
|
|
1281
1362
|
const dbAvailable = await ensureDbOpen(basePath);
|
|
1282
1363
|
if (!dbAvailable) {
|
|
@@ -232,6 +232,8 @@ export const UNIT_REGISTRY = {
|
|
|
232
232
|
"gsd_slice_complete",
|
|
233
233
|
"gsd_task_reopen",
|
|
234
234
|
"gsd_replan_slice",
|
|
235
|
+
"gsd_replan_task",
|
|
236
|
+
"gsd_rework_brief_save",
|
|
235
237
|
"gsd_decision_save",
|
|
236
238
|
"gsd_capture_thought",
|
|
237
239
|
"gsd_requirement_update",
|
|
@@ -245,6 +247,8 @@ export const UNIT_REGISTRY = {
|
|
|
245
247
|
"gsd_slice_complete",
|
|
246
248
|
"gsd_task_reopen",
|
|
247
249
|
"gsd_replan_slice",
|
|
250
|
+
"gsd_replan_task",
|
|
251
|
+
"gsd_rework_brief_save",
|
|
248
252
|
"gsd_requirement_update",
|
|
249
253
|
"gsd_summary_save",
|
|
250
254
|
],
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { gsdRoot, resolveMilestoneFile, resolveSliceFile } from "../paths.js";
|
|
6
|
-
import { isDbAvailable, getMilestoneSlices,
|
|
6
|
+
import { isDbAvailable, getMilestoneSlices, getTasksBySliceIds } from "../gsd-db.js";
|
|
7
7
|
const PLAN_V2_CLARIFY_ROUND_LIMIT = 3;
|
|
8
8
|
export const EXECUTION_ENTRY_PHASES = new Set([
|
|
9
9
|
"executing",
|
|
@@ -17,6 +17,19 @@ export function isExecutionEntryPhase(phase) {
|
|
|
17
17
|
function graphOutputPath(basePath) {
|
|
18
18
|
return join(gsdRoot(basePath), "runtime", "uok-plan-v2-graph.json");
|
|
19
19
|
}
|
|
20
|
+
// True when the on-disk graph matches `output` ignoring the volatile `compiledAt`
|
|
21
|
+
// stamp, so redundant recompiles on the dispatch hot path skip the disk write.
|
|
22
|
+
function graphBodyUnchanged(outPath, output) {
|
|
23
|
+
try {
|
|
24
|
+
const existing = JSON.parse(readFileSync(outPath, "utf-8"));
|
|
25
|
+
const { compiledAt: _existingStamp, ...existingBody } = existing;
|
|
26
|
+
const { compiledAt: _outputStamp, ...outputBody } = output;
|
|
27
|
+
return JSON.stringify(existingBody) === JSON.stringify(outputBody);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false; // Missing, unreadable, or corrupt — write.
|
|
31
|
+
}
|
|
32
|
+
}
|
|
20
33
|
function hasFileContent(path) {
|
|
21
34
|
if (!path || !existsSync(path))
|
|
22
35
|
return false;
|
|
@@ -88,9 +101,10 @@ export function compileUnitGraphFromState(basePath, state) {
|
|
|
88
101
|
finalizedContextIncluded,
|
|
89
102
|
};
|
|
90
103
|
}
|
|
104
|
+
const tasksBySlice = getTasksBySliceIds(slices.map((slice) => ({ milestoneId: mid, sliceId: slice.id })));
|
|
91
105
|
for (const slice of slices) {
|
|
92
106
|
const sid = slice.id;
|
|
93
|
-
const tasks =
|
|
107
|
+
const tasks = (tasksBySlice.get(`${mid}\0${sid}`) ?? [])
|
|
94
108
|
.sort((a, b) => Number(a.sequence ?? 0) - Number(b.sequence ?? 0));
|
|
95
109
|
let previousTaskNodeId = null;
|
|
96
110
|
for (const task of tasks) {
|
|
@@ -138,7 +152,9 @@ export function compileUnitGraphFromState(basePath, state) {
|
|
|
138
152
|
};
|
|
139
153
|
const outPath = graphOutputPath(basePath);
|
|
140
154
|
mkdirSync(join(gsdRoot(basePath), "runtime"), { recursive: true });
|
|
141
|
-
|
|
155
|
+
if (!graphBodyUnchanged(outPath, output)) {
|
|
156
|
+
writeFileSync(outPath, JSON.stringify(output, null, 2) + "\n", "utf-8");
|
|
157
|
+
}
|
|
142
158
|
return {
|
|
143
159
|
ok: true,
|
|
144
160
|
graphPath: outPath,
|
|
@@ -143,6 +143,9 @@ function syncOtherMilestoneArtifacts(srcMilestonesDir, dstMilestonesDir, current
|
|
|
143
143
|
logWarning("worktree", `milestone artifact scan failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
|
+
function syncFlatPhaseArtifacts(prGsd, wtGsd) {
|
|
147
|
+
safeCopyRecursive(join(prGsd, "phases"), join(wtGsd, "phases"), { force: false });
|
|
148
|
+
}
|
|
146
149
|
/**
|
|
147
150
|
* Root-level .gsd/ files copied from worktree back to project root for
|
|
148
151
|
* post-merge diagnostics. Markdown projections are NOT in this list — DB
|
|
@@ -208,6 +211,9 @@ export function _projectRootToWorktreeImpl(projectRoot, worktreePath_, milestone
|
|
|
208
211
|
// Root PROJECT/REQUIREMENTS/DECISIONS projections must be readable from a
|
|
209
212
|
// worktree-bound unit; the project root remains authoritative.
|
|
210
213
|
syncRootProjectionFilesToWorktree(prGsd, wtGsd);
|
|
214
|
+
// Flat-phase artifacts (phases/NN-slug/NN-CONTEXT.md, NN-DISCUSSION.md,
|
|
215
|
+
// ROADMAP, etc.) must be available before the first worktree dispatch.
|
|
216
|
+
syncFlatPhaseArtifacts(prGsd, wtGsd);
|
|
211
217
|
// Copy milestone directory from project root to worktree — additive only.
|
|
212
218
|
// force:false prevents cpSync from overwriting existing worktree files.
|
|
213
219
|
// Without this, worktree-local files (e.g. VALIDATION.md written
|