@opengsd/gsd-pi 1.1.1-dev.74e8dd1 → 1.1.1-dev.75048e7
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/dist/cli.js +3 -2
- package/dist/help-text.js +10 -6
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +495 -0
- package/dist/resources/extensions/browser-tools/engine/selection.js +16 -0
- package/dist/resources/extensions/browser-tools/extension-manifest.json +2 -2
- package/dist/resources/extensions/browser-tools/index.js +57 -9
- package/dist/resources/extensions/browser-tools/package.json +5 -1
- package/dist/resources/extensions/gsd/auto/orchestrator.js +0 -1
- package/dist/resources/extensions/gsd/auto-dashboard.js +77 -13
- package/dist/resources/extensions/gsd/auto-dispatch.js +16 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +21 -3
- package/dist/resources/extensions/gsd/auto-prompts.js +63 -22
- package/dist/resources/extensions/gsd/auto-recovery.js +3 -4
- package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
- package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +18 -66
- package/dist/resources/extensions/gsd/auto-worktree.js +18 -5
- package/dist/resources/extensions/gsd/auto.js +9 -2
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +20 -14
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +28 -13
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +18 -29
- package/dist/resources/extensions/gsd/browser-evidence.js +29 -2
- package/dist/resources/extensions/gsd/closeout-consistency-gate.js +61 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -2
- package/dist/resources/extensions/gsd/commands-handlers.js +76 -11
- package/dist/resources/extensions/gsd/commands-mcp-status.js +2 -1
- package/dist/resources/extensions/gsd/dashboard-overlay.js +21 -7
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +8 -0
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +2 -2
- package/dist/resources/extensions/gsd/escalation.js +4 -4
- package/dist/resources/extensions/gsd/forensics.js +74 -2
- package/dist/resources/extensions/gsd/gsd-db.js +5 -2
- package/dist/resources/extensions/gsd/guided-flow.js +118 -175
- package/dist/resources/extensions/gsd/mcp-project-config.js +9 -76
- package/dist/resources/extensions/gsd/memory-store.js +4 -1
- package/dist/resources/extensions/gsd/milestone-closeout.js +3 -1
- package/dist/resources/extensions/gsd/pending-auto-start.js +0 -1
- package/dist/resources/extensions/gsd/post-unit-hooks.js +9 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +39 -0
- package/dist/resources/extensions/gsd/prompt-loader.js +7 -0
- package/dist/resources/extensions/gsd/prompts/forensics.md +61 -1
- package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +3 -1
- package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +3 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/reactive-execute.md +3 -1
- package/dist/resources/extensions/gsd/prompts/run-uat.md +25 -21
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
- package/dist/resources/extensions/gsd/recovery-classification.js +20 -0
- package/dist/resources/extensions/gsd/rule-registry.js +428 -52
- package/dist/resources/extensions/gsd/state.js +2 -2
- package/dist/resources/extensions/gsd/templates/plan.md +3 -1
- package/dist/resources/extensions/gsd/tool-contract.js +5 -0
- package/dist/resources/extensions/gsd/tool-presentation-plan.js +30 -7
- package/dist/resources/extensions/gsd/tools/complete-slice.js +15 -1
- package/dist/resources/extensions/gsd/tools/complete-task.js +11 -1
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +46 -16
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +132 -18
- package/dist/resources/extensions/gsd/unit-tool-contracts.js +169 -0
- package/dist/resources/extensions/gsd/verdict-parser.js +59 -15
- package/dist/resources/extensions/gsd/verification-gate.js +72 -1
- package/dist/resources/extensions/gsd/workflow-mcp.js +3 -75
- package/dist/resources/extensions/shared/gsd-browser-cli.js +145 -0
- package/dist/rtk.d.ts +7 -1
- package/dist/rtk.js +27 -11
- package/dist/update-check.d.ts +15 -1
- package/dist/update-check.js +87 -12
- package/dist/update-cmd.d.ts +1 -0
- package/dist/update-cmd.js +53 -2
- 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/prerender-manifest.json +3 -3
- 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.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- 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 +1 -1
- 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 +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- 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 +1 -1
- 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 +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +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/server/server-reference-manifest.json +1 -1
- package/package.json +4 -2
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/dist/agent-session.d.ts +9 -0
- package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/agent-session.js +32 -0
- package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
- package/packages/gsd-agent-core/dist/index.d.ts +1 -0
- package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/index.js +1 -0
- package/packages/gsd-agent-core/dist/index.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts +2 -0
- package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-compaction.js +8 -2
- package/packages/gsd-agent-core/dist/session/agent-session-compaction.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +7 -0
- package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +69 -1
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
- package/packages/gsd-agent-core/dist/turn-latency.d.ts +47 -0
- package/packages/gsd-agent-core/dist/turn-latency.d.ts.map +1 -0
- package/packages/gsd-agent-core/dist/turn-latency.js +123 -0
- package/packages/gsd-agent-core/dist/turn-latency.js.map +1 -0
- package/packages/gsd-agent-core/package.json +6 -6
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts +21 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js +213 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +5 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +20 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js +7 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.js +6 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -1
- package/packages/mcp-server/dist/remote-questions.js +23 -9
- package/packages/mcp-server/dist/remote-questions.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +2 -2
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +42 -3
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/dist/agent.d.ts +5 -1
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +2 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/dist/harness/agent-harness.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/harness/agent-harness.js +3 -1
- package/packages/pi-agent-core/dist/harness/agent-harness.js.map +1 -1
- package/packages/pi-agent-core/dist/harness/types.d.ts +1 -0
- package/packages/pi-agent-core/dist/harness/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/harness/types.js.map +1 -1
- package/packages/pi-agent-core/dist/types.d.ts +6 -1
- package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/types.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/api-registry.d.ts +2 -0
- package/packages/pi-ai/dist/api-registry.d.ts.map +1 -1
- package/packages/pi-ai/dist/api-registry.js +23 -0
- package/packages/pi-ai/dist/api-registry.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +74 -23
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +82 -31
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/stream.js +6 -6
- package/packages/pi-ai/dist/stream.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +2 -2
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.js +2 -2
- package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.js +3 -2
- package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/render-utils.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/tools/render-utils.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/render-utils.js +6 -0
- package/packages/pi-coding-agent/dist/core/tools/render-utils.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.js +3 -2
- package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +1 -1
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +579 -0
- package/src/resources/extensions/browser-tools/engine/selection.ts +19 -0
- package/src/resources/extensions/browser-tools/extension-manifest.json +2 -2
- package/src/resources/extensions/browser-tools/index.ts +60 -9
- package/src/resources/extensions/browser-tools/package.json +5 -1
- package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +35 -0
- package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +33 -0
- package/src/resources/extensions/gsd/auto/orchestrator.ts +0 -1
- package/src/resources/extensions/gsd/auto-dashboard.ts +82 -14
- package/src/resources/extensions/gsd/auto-dispatch.ts +19 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +28 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +97 -15
- package/src/resources/extensions/gsd/auto-recovery.ts +3 -3
- package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -0
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
- package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +43 -74
- package/src/resources/extensions/gsd/auto-worktree.ts +23 -5
- package/src/resources/extensions/gsd/auto.ts +12 -2
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +20 -14
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +32 -13
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +50 -54
- package/src/resources/extensions/gsd/browser-evidence.ts +26 -2
- package/src/resources/extensions/gsd/closeout-consistency-gate.ts +137 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -2
- package/src/resources/extensions/gsd/commands-handlers.ts +76 -11
- package/src/resources/extensions/gsd/commands-mcp-status.ts +2 -1
- package/src/resources/extensions/gsd/dashboard-overlay.ts +28 -7
- package/src/resources/extensions/gsd/docs/preferences-reference.md +8 -0
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +2 -2
- package/src/resources/extensions/gsd/escalation.ts +4 -4
- package/src/resources/extensions/gsd/forensics.ts +99 -5
- package/src/resources/extensions/gsd/gsd-db.ts +5 -2
- package/src/resources/extensions/gsd/guided-flow.ts +214 -216
- package/src/resources/extensions/gsd/mcp-project-config.ts +13 -78
- package/src/resources/extensions/gsd/memory-store.ts +4 -1
- package/src/resources/extensions/gsd/milestone-closeout.ts +3 -1
- package/src/resources/extensions/gsd/pending-auto-start.ts +0 -2
- package/src/resources/extensions/gsd/post-unit-hooks.ts +14 -1
- package/src/resources/extensions/gsd/preferences-validation.ts +36 -0
- package/src/resources/extensions/gsd/prompt-loader.ts +8 -0
- package/src/resources/extensions/gsd/prompts/forensics.md +61 -1
- package/src/resources/extensions/gsd/prompts/gate-evaluate.md +3 -1
- package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +3 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/reactive-execute.md +3 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +25 -21
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
- package/src/resources/extensions/gsd/recovery-classification.ts +20 -0
- package/src/resources/extensions/gsd/rule-registry.ts +558 -58
- package/src/resources/extensions/gsd/rule-types.ts +2 -0
- package/src/resources/extensions/gsd/state.ts +2 -2
- package/src/resources/extensions/gsd/templates/plan.md +3 -1
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +105 -4
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +10 -2
- package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +4 -1
- package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +12 -2
- package/src/resources/extensions/gsd/tests/browser-evidence.test.ts +142 -0
- package/src/resources/extensions/gsd/tests/check-auto-start-pending-gate.test.ts +9 -15
- package/src/resources/extensions/gsd/tests/check-auto-start-ready-guard.test.ts +26 -16
- package/src/resources/extensions/gsd/tests/commands-dispatcher-unmerged-milestone.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/deep-planning-mode-dispatch.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/discuss-milestone-structured-questions.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +40 -1
- package/src/resources/extensions/gsd/tests/doctor-runtime-checks.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/escalation.test.ts +16 -27
- package/src/resources/extensions/gsd/tests/forensics-issue-routing.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/forensics-prompt-rendering.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/forensics-tool-scope.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +31 -79
- package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +40 -1
- package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +86 -0
- package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +5 -3
- package/src/resources/extensions/gsd/tests/guided-flow-state-rebuild.test.ts +40 -4
- package/src/resources/extensions/gsd/tests/guided-flow.test.ts +12 -9
- package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +72 -10
- package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/memory-maintenance.test.ts +39 -8
- package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +10 -1
- package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +9 -1
- package/src/resources/extensions/gsd/tests/new-milestone-discuss-routing.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +157 -0
- package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +179 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +73 -1
- package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +7 -8
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +75 -0
- package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +139 -0
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +6 -3
- package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +133 -0
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +410 -0
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +15 -0
- package/src/resources/extensions/gsd/tool-contract.ts +6 -0
- package/src/resources/extensions/gsd/tool-presentation-plan.ts +63 -7
- package/src/resources/extensions/gsd/tools/complete-slice.ts +14 -1
- package/src/resources/extensions/gsd/tools/complete-task.ts +20 -2
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +46 -15
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +163 -20
- package/src/resources/extensions/gsd/types.ts +69 -5
- package/src/resources/extensions/gsd/unit-tool-contracts.ts +186 -0
- package/src/resources/extensions/gsd/verdict-parser.ts +54 -13
- package/src/resources/extensions/gsd/verification-gate.ts +87 -1
- package/src/resources/extensions/gsd/workflow-mcp.ts +3 -75
- package/src/resources/extensions/shared/gsd-browser-cli.ts +172 -0
- package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound-corrections.test.ts +0 -246
- package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound.test.ts +0 -218
- /package/dist/web/standalone/.next/static/{eRWf-RI9bzbrwEurm_3uI → h4TGni4xJzlZjGkxaT6uU}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{eRWf-RI9bzbrwEurm_3uI → h4TGni4xJzlZjGkxaT6uU}/_ssgManifest.js +0 -0
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { join } from "node:path";
|
|
14
14
|
|
|
15
|
-
import type { CompleteTaskParams } from "../types.js";
|
|
15
|
+
import type { CompleteTaskParams, EscalationArtifact } from "../types.js";
|
|
16
16
|
import { isClosedStatus } from "../status-guards.js";
|
|
17
17
|
import {
|
|
18
18
|
transaction,
|
|
@@ -48,6 +48,14 @@ export interface CompleteTaskResult {
|
|
|
48
48
|
sliceId: string;
|
|
49
49
|
milestoneId: string;
|
|
50
50
|
summaryPath: string;
|
|
51
|
+
escalation?: {
|
|
52
|
+
artifactPath: string;
|
|
53
|
+
question: string;
|
|
54
|
+
options: EscalationArtifact["options"];
|
|
55
|
+
recommendation: string;
|
|
56
|
+
recommendationRationale: string;
|
|
57
|
+
continueWithDefault: boolean;
|
|
58
|
+
};
|
|
51
59
|
/**
|
|
52
60
|
* True when this call re-completed an already-closed task from a turn that
|
|
53
61
|
* had been superseded by timeout recovery or cancellation. The underlying
|
|
@@ -407,9 +415,18 @@ export async function handleCompleteTask(
|
|
|
407
415
|
// overwrite it; gate rows are UPSERT-keyed per task and will also be
|
|
408
416
|
// overwritten. This restores the invariant that deriveState() sees a
|
|
409
417
|
// consistent "task not done" view so the loop re-dispatches the task.
|
|
418
|
+
let escalationMetadata: CompleteTaskResult["escalation"] | undefined;
|
|
410
419
|
if (validatedEscalationArtifact) {
|
|
411
420
|
try {
|
|
412
|
-
writeEscalationArtifact(artifactBasePath, validatedEscalationArtifact);
|
|
421
|
+
const escalationPath = writeEscalationArtifact(artifactBasePath, validatedEscalationArtifact);
|
|
422
|
+
escalationMetadata = {
|
|
423
|
+
artifactPath: escalationPath,
|
|
424
|
+
question: validatedEscalationArtifact.question,
|
|
425
|
+
options: validatedEscalationArtifact.options,
|
|
426
|
+
recommendation: validatedEscalationArtifact.recommendation,
|
|
427
|
+
recommendationRationale: validatedEscalationArtifact.recommendationRationale,
|
|
428
|
+
continueWithDefault: validatedEscalationArtifact.continueWithDefault,
|
|
429
|
+
};
|
|
413
430
|
} catch (escalationErr) {
|
|
414
431
|
const msg = `complete-task escalation write failed for ${params.milestoneId}/${params.sliceId}/${params.taskId}: ${(escalationErr as Error).message}`;
|
|
415
432
|
logWarning("tool", msg);
|
|
@@ -477,6 +494,7 @@ export async function handleCompleteTask(
|
|
|
477
494
|
sliceId: params.sliceId,
|
|
478
495
|
milestoneId: params.milestoneId,
|
|
479
496
|
summaryPath,
|
|
497
|
+
...(escalationMetadata ? { escalation: escalationMetadata } : {}),
|
|
480
498
|
...(projectionStale ? { stale: true } : {}),
|
|
481
499
|
};
|
|
482
500
|
}
|
|
@@ -78,18 +78,10 @@ function getRequiredVerificationClasses(milestoneId: string): string[] {
|
|
|
78
78
|
return required;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const artifact = getArtifact(artifactPath);
|
|
86
|
-
if (artifact?.full_content) chunks.push(artifact.full_content);
|
|
87
|
-
|
|
88
|
-
const assessmentPath = resolveSliceFile(basePath, milestoneId, slice.id, "ASSESSMENT");
|
|
89
|
-
const assessmentContent = assessmentPath ? await loadFile(assessmentPath) : null;
|
|
90
|
-
if (assessmentContent) chunks.push(assessmentContent);
|
|
91
|
-
}
|
|
92
|
-
return chunks.join("\n\n");
|
|
81
|
+
function hasRuntimeExecutableUatEvidenceText(text: string): boolean {
|
|
82
|
+
if (!/\buatType:\s*runtime-executable\b/i.test(text)) return false;
|
|
83
|
+
if (!/\bverdict:\s*PASS\b/i.test(text)) return false;
|
|
84
|
+
return /^\|\s*[^|\n]+\s*\|\s*runtime\s*\|\s*PASS\s*\|[^|\n]*\bgsd_uat_exec\b/mi.test(text);
|
|
93
85
|
}
|
|
94
86
|
|
|
95
87
|
async function browserEvidenceGateRequiresAttention(
|
|
@@ -114,7 +106,38 @@ async function browserEvidenceGateRequiresAttention(
|
|
|
114
106
|
]);
|
|
115
107
|
if (!hasBrowserRequiredText(requirementText)) return false;
|
|
116
108
|
|
|
117
|
-
|
|
109
|
+
// Collect per-slice evidence so the runtime bypass is checked independently
|
|
110
|
+
// for each slice. Concatenating all slices before checking would allow runtime
|
|
111
|
+
// evidence from one slice to cover another slice's browser requirements.
|
|
112
|
+
const sliceEvidencePairs: Array<{ sliceRequirementText: string; evidenceText: string }> = [];
|
|
113
|
+
for (const slice of slices) {
|
|
114
|
+
const chunks: string[] = [];
|
|
115
|
+
const artifactPath = `milestones/${params.milestoneId}/slices/${slice.id}/${slice.id}-ASSESSMENT.md`;
|
|
116
|
+
const artifact = getArtifact(artifactPath);
|
|
117
|
+
if (artifact?.full_content) chunks.push(artifact.full_content);
|
|
118
|
+
const assessmentPath = resolveSliceFile(basePath, params.milestoneId, slice.id, "ASSESSMENT");
|
|
119
|
+
const assessmentContent = assessmentPath ? await loadFile(assessmentPath) : null;
|
|
120
|
+
if (assessmentContent) chunks.push(assessmentContent);
|
|
121
|
+
sliceEvidencePairs.push({
|
|
122
|
+
sliceRequirementText: compactTextParts([slice.demo, slice.goal, slice.success_criteria]),
|
|
123
|
+
evidenceText: chunks.join("\n\n"),
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
const persistedEvidence = sliceEvidencePairs.map((s) => s.evidenceText).join("\n\n");
|
|
127
|
+
|
|
128
|
+
// Runtime bypass: each slice whose own requirement text has browser-observable
|
|
129
|
+
// criteria must have its own runtime-executable UAT evidence. When no individual
|
|
130
|
+
// slice has slice-level browser requirements (e.g., they come from milestone-level
|
|
131
|
+
// fields only), fall back to checking whether any slice has runtime evidence.
|
|
132
|
+
const browserRequiringSlices = sliceEvidencePairs.filter((s) =>
|
|
133
|
+
hasBrowserRequiredText(s.sliceRequirementText),
|
|
134
|
+
);
|
|
135
|
+
const runtimeBypasses =
|
|
136
|
+
browserRequiringSlices.length > 0
|
|
137
|
+
? browserRequiringSlices.every((s) => hasRuntimeExecutableUatEvidenceText(s.evidenceText))
|
|
138
|
+
: sliceEvidencePairs.some((s) => hasRuntimeExecutableUatEvidenceText(s.evidenceText));
|
|
139
|
+
if (runtimeBypasses) return false;
|
|
140
|
+
|
|
118
141
|
const validationEvidence = compactTextParts([
|
|
119
142
|
params.successCriteriaChecklist,
|
|
120
143
|
params.verificationClasses,
|
|
@@ -184,14 +207,22 @@ export async function handleValidateMilestone(
|
|
|
184
207
|
const requiredClasses = getRequiredVerificationClasses(params.milestoneId);
|
|
185
208
|
if (requiredClasses.length > 0) {
|
|
186
209
|
const verificationClasses = params.verificationClasses ?? "";
|
|
187
|
-
const
|
|
210
|
+
const missingClasses = requiredClasses.filter(
|
|
188
211
|
(className) => !new RegExp(`\\b${className}\\b`, "i").test(verificationClasses),
|
|
189
212
|
);
|
|
190
|
-
if (
|
|
213
|
+
if (missingClasses.length === 1) {
|
|
214
|
+
const missingClass = missingClasses[0];
|
|
191
215
|
return {
|
|
192
216
|
error: `verificationClasses must include canonical row "${missingClass}" because this milestone planned ${missingClass.toLowerCase()} verification`,
|
|
193
217
|
};
|
|
194
218
|
}
|
|
219
|
+
if (missingClasses.length > 1) {
|
|
220
|
+
const quotedClasses = missingClasses.map((className) => `"${className}"`).join(", ");
|
|
221
|
+
const plannedClasses = missingClasses.map((className) => className.toLowerCase()).join(", ");
|
|
222
|
+
return {
|
|
223
|
+
error: `verificationClasses must include canonical rows ${quotedClasses} because this milestone planned ${plannedClasses} verification`,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
195
226
|
}
|
|
196
227
|
|
|
197
228
|
const artifactBasePath = resolveCanonicalMilestoneRoot(basePath, params.milestoneId);
|
|
@@ -24,7 +24,7 @@ import { isAbsolute, join, resolve } from "node:path";
|
|
|
24
24
|
import type { CompleteMilestoneParams } from "./complete-milestone.js";
|
|
25
25
|
import { handleCompleteMilestone } from "./complete-milestone.js";
|
|
26
26
|
import { handleCompleteTask } from "./complete-task.js";
|
|
27
|
-
import type { CompleteSliceParams } from "../types.js";
|
|
27
|
+
import type { CompleteSliceParams, EscalationOption } from "../types.js";
|
|
28
28
|
import { handleCompleteSlice } from "./complete-slice.js";
|
|
29
29
|
import type { PlanMilestoneParams } from "./plan-milestone.js";
|
|
30
30
|
import { handlePlanMilestone } from "./plan-milestone.js";
|
|
@@ -48,9 +48,11 @@ import { loadEffectiveGSDPreferences } from "../preferences.js";
|
|
|
48
48
|
import { parseProject } from "../schemas/parsers.js";
|
|
49
49
|
import { getAutoRuntimeSnapshot } from "../auto-runtime-state.js";
|
|
50
50
|
import {
|
|
51
|
+
buildRunUatPresentationForType,
|
|
51
52
|
canonicalWorkflowToolName,
|
|
52
53
|
parseMcpToolName,
|
|
53
54
|
RUN_UAT_FORBIDDEN_TOOL_NAMES,
|
|
55
|
+
RUN_UAT_TOOL_PRESENTATION_PLAN_ID,
|
|
54
56
|
RUN_UAT_WORKFLOW_TOOL_NAMES,
|
|
55
57
|
} from "../tool-presentation-plan.js";
|
|
56
58
|
|
|
@@ -90,7 +92,7 @@ function blockIfWrongAutoUnit(requiredUnitType: string, operation: string): Tool
|
|
|
90
92
|
if (!snapshot.active || !snapshot.currentUnit) return null;
|
|
91
93
|
if (snapshot.currentUnit.type === requiredUnitType) return null;
|
|
92
94
|
|
|
93
|
-
const error = `HARD BLOCK: ${operation} may only run from ${requiredUnitType}; active unit is ${snapshot.currentUnit.type}. The orchestrator owns phase transitions.`;
|
|
95
|
+
const error = `HARD BLOCK: Tool Contract failure: ${operation} may only run from ${requiredUnitType}; active unit is ${snapshot.currentUnit.type}. Fix unit-tool-contracts.ts or the active Unit prompt. The orchestrator owns phase transitions.`;
|
|
94
96
|
return {
|
|
95
97
|
content: [{ type: "text", text: error }],
|
|
96
98
|
details: { operation, error },
|
|
@@ -178,7 +180,11 @@ export async function executeSummarySave(
|
|
|
178
180
|
if (rootArtifactGuard.block) {
|
|
179
181
|
return {
|
|
180
182
|
content: [{ type: "text", text: `Error saving artifact: ${rootArtifactGuard.reason ?? "root artifact write blocked"}` }],
|
|
181
|
-
details: {
|
|
183
|
+
details: {
|
|
184
|
+
operation: "save_summary",
|
|
185
|
+
error: "root_artifact_write_blocked",
|
|
186
|
+
displayReason: "Approval confirmation required before saving final project setup artifacts.",
|
|
187
|
+
},
|
|
182
188
|
isError: true,
|
|
183
189
|
};
|
|
184
190
|
}
|
|
@@ -191,9 +197,13 @@ export async function executeSummarySave(
|
|
|
191
197
|
if (contextGuard.block) {
|
|
192
198
|
return {
|
|
193
199
|
content: [{ type: "text", text: `Error saving artifact: ${contextGuard.reason ?? "context write blocked"}` }],
|
|
194
|
-
details: {
|
|
195
|
-
|
|
196
|
-
|
|
200
|
+
details: {
|
|
201
|
+
operation: "save_summary",
|
|
202
|
+
error: "context_write_blocked",
|
|
203
|
+
displayReason: "Depth check required before writing milestone context.",
|
|
204
|
+
},
|
|
205
|
+
isError: true,
|
|
206
|
+
};
|
|
197
207
|
}
|
|
198
208
|
try {
|
|
199
209
|
let relativePath: string;
|
|
@@ -347,6 +357,14 @@ type VerificationEvidenceInput =
|
|
|
347
357
|
}
|
|
348
358
|
| string;
|
|
349
359
|
|
|
360
|
+
interface TaskEscalationInput {
|
|
361
|
+
question: string;
|
|
362
|
+
options: EscalationOption[];
|
|
363
|
+
recommendation: string;
|
|
364
|
+
recommendationRationale: string;
|
|
365
|
+
continueWithDefault: boolean;
|
|
366
|
+
}
|
|
367
|
+
|
|
350
368
|
export interface TaskCompleteParams {
|
|
351
369
|
taskId: string;
|
|
352
370
|
sliceId: string;
|
|
@@ -359,6 +377,7 @@ export interface TaskCompleteParams {
|
|
|
359
377
|
keyFiles?: string[];
|
|
360
378
|
keyDecisions?: string[];
|
|
361
379
|
blockerDiscovered?: boolean;
|
|
380
|
+
escalation?: TaskEscalationInput;
|
|
362
381
|
verificationEvidence?: VerificationEvidenceInput[];
|
|
363
382
|
}
|
|
364
383
|
|
|
@@ -432,6 +451,9 @@ export interface UatEvidenceRef {
|
|
|
432
451
|
kind: "gsd_uat_exec" | "gsd_exec" | "screenshot" | "log" | "url" | "browser";
|
|
433
452
|
ref: string;
|
|
434
453
|
note?: string;
|
|
454
|
+
unitType?: string;
|
|
455
|
+
tool?: string;
|
|
456
|
+
executionId?: string;
|
|
435
457
|
}
|
|
436
458
|
|
|
437
459
|
export interface UatCheckResultInput {
|
|
@@ -511,6 +533,28 @@ export async function executeTaskComplete(
|
|
|
511
533
|
isError: true,
|
|
512
534
|
};
|
|
513
535
|
}
|
|
536
|
+
if (result.escalation) {
|
|
537
|
+
const recommended = result.escalation.options.find((option) => option.id === result.escalation?.recommendation);
|
|
538
|
+
const optionIds = result.escalation.options.map((option) => option.id).join("|");
|
|
539
|
+
return {
|
|
540
|
+
content: [{
|
|
541
|
+
type: "text",
|
|
542
|
+
text: [
|
|
543
|
+
`Task completed with escalation decision required: ${result.escalation.question}`,
|
|
544
|
+
`Recommendation: ${result.escalation.recommendation}${recommended ? ` (${recommended.label})` : ""} — ${result.escalation.recommendationRationale}`,
|
|
545
|
+
`Resolve with: /gsd escalate resolve ${result.taskId} <${optionIds}|accept|reject-blocker> [rationale...]`,
|
|
546
|
+
].join("\n"),
|
|
547
|
+
}],
|
|
548
|
+
details: {
|
|
549
|
+
operation: "complete_task",
|
|
550
|
+
taskId: result.taskId,
|
|
551
|
+
sliceId: result.sliceId,
|
|
552
|
+
milestoneId: result.milestoneId,
|
|
553
|
+
summaryPath: result.summaryPath,
|
|
554
|
+
escalation: result.escalation,
|
|
555
|
+
},
|
|
556
|
+
};
|
|
557
|
+
}
|
|
514
558
|
return {
|
|
515
559
|
content: [{ type: "text", text: `Completed task ${result.taskId} (${result.sliceId}/${result.milestoneId})` }],
|
|
516
560
|
details: {
|
|
@@ -977,10 +1021,68 @@ function isNonEmptyString(value: unknown): value is string {
|
|
|
977
1021
|
return typeof value === "string" && value.trim().length > 0;
|
|
978
1022
|
}
|
|
979
1023
|
|
|
1024
|
+
function mergeBlockedTools(
|
|
1025
|
+
current: UatPresentationInput["blockedTools"] | undefined,
|
|
1026
|
+
canonical: UatPresentationInput["blockedTools"],
|
|
1027
|
+
): UatPresentationInput["blockedTools"] {
|
|
1028
|
+
const merged = new Map<string, { name: string; reason: string }>();
|
|
1029
|
+
for (const entry of [...(current ?? []), ...canonical]) {
|
|
1030
|
+
merged.set(canonicalWorkflowToolName(parseMcpToolName(entry.name)?.tool ?? entry.name), entry);
|
|
1031
|
+
}
|
|
1032
|
+
return [...merged.values()];
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
function mergePresentedTools(current: readonly string[] | undefined, canonical: readonly string[]): string[] {
|
|
1036
|
+
return [...new Set([...(current ?? []), ...canonical])];
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
function normalizeUatVerdict(params: UatResultSaveParams): UatResultSaveParams {
|
|
1040
|
+
const raw = params as Partial<UatResultSaveParams> & Record<string, unknown>;
|
|
1041
|
+
if (typeof raw.verdict === "string") {
|
|
1042
|
+
return { ...params, verdict: raw.verdict.toUpperCase() as UatVerdict };
|
|
1043
|
+
}
|
|
1044
|
+
return params;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
function supplyDefaultPresentation(params: UatResultSaveParams): UatResultSaveParams {
|
|
1048
|
+
const raw = params as Partial<UatResultSaveParams> & Record<string, unknown>;
|
|
1049
|
+
if (!raw.presentation) {
|
|
1050
|
+
return { ...params, presentation: buildRunUatPresentationForType(params.uatType) };
|
|
1051
|
+
}
|
|
1052
|
+
return params;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
function mergeCanonicalPresentation(params: UatResultSaveParams): UatResultSaveParams {
|
|
1056
|
+
const canonicalPresentation = buildRunUatPresentationForType(params.uatType);
|
|
1057
|
+
const providedPresentation = params.presentation as Partial<UatPresentationInput>;
|
|
1058
|
+
return {
|
|
1059
|
+
...params,
|
|
1060
|
+
presentation: {
|
|
1061
|
+
...providedPresentation,
|
|
1062
|
+
surface: providedPresentation.surface ?? canonicalPresentation.surface,
|
|
1063
|
+
presentedTools: mergePresentedTools(providedPresentation.presentedTools, canonicalPresentation.presentedTools),
|
|
1064
|
+
blockedTools: mergeBlockedTools(providedPresentation.blockedTools, canonicalPresentation.blockedTools),
|
|
1065
|
+
toolPresentationPlanId: RUN_UAT_TOOL_PRESENTATION_PLAN_ID,
|
|
1066
|
+
} as UatPresentationInput,
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
const VALID_UAT_TYPES: readonly UatType[] = [
|
|
1071
|
+
"artifact-driven",
|
|
1072
|
+
"browser-executable",
|
|
1073
|
+
"runtime-executable",
|
|
1074
|
+
"live-runtime",
|
|
1075
|
+
"mixed",
|
|
1076
|
+
"human-experience",
|
|
1077
|
+
];
|
|
1078
|
+
|
|
980
1079
|
function ensureUatRequiredFields(params: UatResultSaveParams): string | null {
|
|
981
1080
|
if (!isNonEmptyString(params.milestoneId)) return "milestoneId is required";
|
|
982
1081
|
if (!isNonEmptyString(params.sliceId)) return "sliceId is required";
|
|
983
1082
|
if (!isNonEmptyString(params.uatType)) return "uatType is required";
|
|
1083
|
+
if (!(VALID_UAT_TYPES as readonly string[]).includes(params.uatType)) {
|
|
1084
|
+
return `uatType must be one of: ${VALID_UAT_TYPES.join(", ")}`;
|
|
1085
|
+
}
|
|
984
1086
|
if (!["PASS", "FAIL", "PARTIAL"].includes(params.verdict)) return "verdict must be PASS, FAIL, or PARTIAL";
|
|
985
1087
|
if (!Array.isArray(params.checks) || params.checks.length === 0) return "checks must contain at least one UAT check";
|
|
986
1088
|
if (!params.presentation || !Array.isArray(params.presentation.presentedTools)) return "presentation.presentedTools is required";
|
|
@@ -1116,9 +1218,21 @@ function validateUatChecks(basePath: string, params: UatResultSaveParams): strin
|
|
|
1116
1218
|
return null;
|
|
1117
1219
|
}
|
|
1118
1220
|
|
|
1221
|
+
function validateFreshUatOwnedEvidence(params: UatResultSaveParams): string | null {
|
|
1222
|
+
const hasFreshUatEvidence = params.checks.some((check) =>
|
|
1223
|
+
(check.evidence ?? []).some((evidence) => evidence.kind === "gsd_uat_exec")
|
|
1224
|
+
);
|
|
1225
|
+
return hasFreshUatEvidence
|
|
1226
|
+
? null
|
|
1227
|
+
: "UAT Assessment requires at least one fresh gsd_uat_exec evidence reference from run-uat";
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1119
1230
|
function validateUatMode(params: UatResultSaveParams): string | null {
|
|
1120
1231
|
const modes = new Set(params.checks.map((check) => check.mode));
|
|
1121
1232
|
const hasHuman = params.checks.some((check) => check.result === "NEEDS-HUMAN");
|
|
1233
|
+
if (params.uatType === "artifact-driven" && hasHuman && params.verdict === "PASS") {
|
|
1234
|
+
return "artifact-driven UAT cannot PASS with human-only checks";
|
|
1235
|
+
}
|
|
1122
1236
|
if (
|
|
1123
1237
|
hasHuman &&
|
|
1124
1238
|
params.verdict === "PASS" &&
|
|
@@ -1136,12 +1250,13 @@ function validateUatMode(params: UatResultSaveParams): string | null {
|
|
|
1136
1250
|
if (params.uatType === "live-runtime" && !modes.has("runtime") && !modes.has("browser")) {
|
|
1137
1251
|
return "live-runtime UAT requires runtime or browser evidence";
|
|
1138
1252
|
}
|
|
1139
|
-
if (params.uatType === "artifact-driven" && hasHuman && params.verdict === "PASS") {
|
|
1140
|
-
return "artifact-driven UAT cannot PASS with human-only checks";
|
|
1141
|
-
}
|
|
1142
1253
|
return null;
|
|
1143
1254
|
}
|
|
1144
1255
|
|
|
1256
|
+
function quoteToolNames(toolNames: readonly string[]): string {
|
|
1257
|
+
return toolNames.map((toolName) => `"${toolName}"`).join(", ");
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1145
1260
|
function validateCanonicalPresentation(params: UatResultSaveParams): string | null {
|
|
1146
1261
|
const aliasHints: Record<string, string> = {
|
|
1147
1262
|
gsd_save_summary: "gsd_summary_save",
|
|
@@ -1149,10 +1264,11 @@ function validateCanonicalPresentation(params: UatResultSaveParams): string | nu
|
|
|
1149
1264
|
gsd_complete_slice: "gsd_slice_complete",
|
|
1150
1265
|
gsd_milestone_complete: "gsd_complete_milestone",
|
|
1151
1266
|
};
|
|
1267
|
+
const errors: string[] = [];
|
|
1152
1268
|
for (const toolName of params.presentation.presentedTools) {
|
|
1153
1269
|
const baseName = parseMcpToolName(toolName)?.tool ?? toolName;
|
|
1154
1270
|
const canonical = aliasHints[baseName];
|
|
1155
|
-
if (canonical)
|
|
1271
|
+
if (canonical) errors.push(`presentation tool "${toolName}" uses an alias; use canonical "${canonical}"`);
|
|
1156
1272
|
}
|
|
1157
1273
|
|
|
1158
1274
|
const presentedCanonical = new Set(
|
|
@@ -1160,10 +1276,13 @@ function validateCanonicalPresentation(params: UatResultSaveParams): string | nu
|
|
|
1160
1276
|
canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName)
|
|
1161
1277
|
),
|
|
1162
1278
|
);
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1279
|
+
const missingRequiredTools = RUN_UAT_WORKFLOW_TOOL_NAMES.filter(
|
|
1280
|
+
(requiredTool) => !presentedCanonical.has(requiredTool),
|
|
1281
|
+
);
|
|
1282
|
+
if (missingRequiredTools.length === 1) {
|
|
1283
|
+
errors.push(`presentation is missing required UAT tool "${missingRequiredTools[0]}"`);
|
|
1284
|
+
} else if (missingRequiredTools.length > 1) {
|
|
1285
|
+
errors.push(`presentation is missing required UAT tools ${quoteToolNames(missingRequiredTools)}`);
|
|
1167
1286
|
}
|
|
1168
1287
|
|
|
1169
1288
|
const forbiddenCanonical = new Set(
|
|
@@ -1171,24 +1290,33 @@ function validateCanonicalPresentation(params: UatResultSaveParams): string | nu
|
|
|
1171
1290
|
.filter((toolName) => !toolName.includes("*"))
|
|
1172
1291
|
.map((toolName) => canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName)),
|
|
1173
1292
|
);
|
|
1293
|
+
const forbiddenPresentedTools: string[] = [];
|
|
1174
1294
|
for (const toolName of params.presentation.presentedTools) {
|
|
1175
1295
|
const canonical = canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName);
|
|
1176
1296
|
if (toolName === "mcp__gsd-workflow__*" || forbiddenCanonical.has(canonical)) {
|
|
1177
|
-
|
|
1297
|
+
forbiddenPresentedTools.push(toolName);
|
|
1178
1298
|
}
|
|
1179
1299
|
}
|
|
1300
|
+
if (forbiddenPresentedTools.length === 1) {
|
|
1301
|
+
errors.push(`presentation includes forbidden run-uat tool "${forbiddenPresentedTools[0]}"`);
|
|
1302
|
+
} else if (forbiddenPresentedTools.length > 1) {
|
|
1303
|
+
errors.push(`presentation includes forbidden run-uat tools ${quoteToolNames(forbiddenPresentedTools)}`);
|
|
1304
|
+
}
|
|
1180
1305
|
|
|
1181
1306
|
const blockedCanonical = new Set(
|
|
1182
1307
|
params.presentation.blockedTools.map((entry) =>
|
|
1183
1308
|
canonicalWorkflowToolName(parseMcpToolName(entry.name)?.tool ?? entry.name)
|
|
1184
1309
|
),
|
|
1185
1310
|
);
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1311
|
+
const missingBlockedTools = ["gsd_exec", "gsd_summary_save", "gsd_save_gate_result"].filter(
|
|
1312
|
+
(blockedTool) => !blockedCanonical.has(blockedTool),
|
|
1313
|
+
);
|
|
1314
|
+
if (missingBlockedTools.length === 1) {
|
|
1315
|
+
errors.push(`presentation must record "${missingBlockedTools[0]}" as blocked during run-uat`);
|
|
1316
|
+
} else if (missingBlockedTools.length > 1) {
|
|
1317
|
+
errors.push(`presentation must record ${quoteToolNames(missingBlockedTools)} as blocked during run-uat`);
|
|
1190
1318
|
}
|
|
1191
|
-
return null;
|
|
1319
|
+
return errors.length > 0 ? errors.join("; ") : null;
|
|
1192
1320
|
}
|
|
1193
1321
|
|
|
1194
1322
|
function nextUatAttempt(basePath: string, milestoneId: string, sliceId: string): number {
|
|
@@ -1258,15 +1386,30 @@ export async function executeUatResultSave(
|
|
|
1258
1386
|
params: UatResultSaveParams,
|
|
1259
1387
|
basePath: string = process.cwd(),
|
|
1260
1388
|
): Promise<ToolExecutionResult> {
|
|
1389
|
+
const unitGuard = blockIfWrongAutoUnit("run-uat", "save_uat_result");
|
|
1390
|
+
if (unitGuard) return unitGuard;
|
|
1391
|
+
|
|
1392
|
+
// Phase 1: normalize verdict and supply the canonical presentation when none was provided.
|
|
1393
|
+
params = normalizeUatVerdict(params);
|
|
1394
|
+
params = supplyDefaultPresentation(params);
|
|
1395
|
+
|
|
1261
1396
|
const dbAvailable = await ensureDbOpen(basePath);
|
|
1262
1397
|
if (!dbAvailable) return errorResult("save_uat_result", "GSD database is not available.", "db_unavailable");
|
|
1263
1398
|
|
|
1399
|
+
// Phase 2: validate the submitted presentation before the canonical merge so that
|
|
1400
|
+
// presentations missing required workflow tools are rejected rather than silently patched.
|
|
1264
1401
|
const requiredError = ensureUatRequiredFields(params);
|
|
1265
1402
|
if (requiredError) return errorResult("save_uat_result", requiredError, "invalid_params");
|
|
1266
1403
|
const presentationError = validateCanonicalPresentation(params);
|
|
1267
1404
|
if (presentationError) return errorResult("save_uat_result", presentationError, "alias_tool_name");
|
|
1405
|
+
|
|
1406
|
+
// Phase 3: merge in the canonical plan ID and read-only audit tools so the persisted
|
|
1407
|
+
// artifact always carries the full audit surface even when the provider omitted them.
|
|
1408
|
+
params = mergeCanonicalPresentation(params);
|
|
1268
1409
|
const checkError = validateUatChecks(basePath, params);
|
|
1269
1410
|
if (checkError) return errorResult("save_uat_result", checkError, "invalid_evidence");
|
|
1411
|
+
const freshEvidenceError = validateFreshUatOwnedEvidence(params);
|
|
1412
|
+
if (freshEvidenceError) return errorResult("save_uat_result", freshEvidenceError, "missing_fresh_uat_evidence");
|
|
1270
1413
|
const modeError = validateUatMode(params);
|
|
1271
1414
|
if (modeError) return errorResult("save_uat_result", modeError, "uat_mode_mismatch");
|
|
1272
1415
|
|
|
@@ -270,6 +270,29 @@ export interface GSDActiveUnit {
|
|
|
270
270
|
|
|
271
271
|
// ─── Post-Unit Hook Types ─────────────────────────────────────────────────
|
|
272
272
|
|
|
273
|
+
export type PostUnitHookCriticality = "advisory" | "blocking";
|
|
274
|
+
|
|
275
|
+
export type PostUnitHookOutcomeVerdict =
|
|
276
|
+
| "pass"
|
|
277
|
+
| "advisory"
|
|
278
|
+
| "needs-rework"
|
|
279
|
+
| "needs-remediation"
|
|
280
|
+
| "needs-attention";
|
|
281
|
+
|
|
282
|
+
export type PostUnitHookOnBlockAction =
|
|
283
|
+
| "retry-unit"
|
|
284
|
+
| "retry-task"
|
|
285
|
+
| "queue-task"
|
|
286
|
+
| "queue-slice"
|
|
287
|
+
| "pause";
|
|
288
|
+
|
|
289
|
+
export interface PostUnitHookOnBlockConfig {
|
|
290
|
+
/** Routing action for blocking hook findings. */
|
|
291
|
+
action: PostUnitHookOnBlockAction;
|
|
292
|
+
/** Optional artifact used by compatibility retry routing. */
|
|
293
|
+
artifact?: string;
|
|
294
|
+
}
|
|
295
|
+
|
|
273
296
|
export interface PostUnitHookConfig {
|
|
274
297
|
/** Unique hook identifier — used in idempotency keys and logging. */
|
|
275
298
|
name: string;
|
|
@@ -283,8 +306,12 @@ export interface PostUnitHookConfig {
|
|
|
283
306
|
model?: string;
|
|
284
307
|
/** Expected output file name (relative to task/slice dir). Used for idempotency — skip if exists. */
|
|
285
308
|
artifact?: string;
|
|
309
|
+
/** Whether the hook is advisory or blocks unit advancement. Default advisory. */
|
|
310
|
+
criticality?: PostUnitHookCriticality;
|
|
286
311
|
/** If this file is produced instead of artifact, re-run the trigger unit then re-run hooks. */
|
|
287
312
|
retry_on?: string;
|
|
313
|
+
/** Optional routing for blocking findings. */
|
|
314
|
+
on_block?: PostUnitHookOnBlockConfig;
|
|
288
315
|
/** Agent definition file to use. */
|
|
289
316
|
agent?: string;
|
|
290
317
|
/** Set false to disable without removing config. Default true. */
|
|
@@ -317,6 +344,31 @@ export interface HookDispatchResult {
|
|
|
317
344
|
unitId: string;
|
|
318
345
|
}
|
|
319
346
|
|
|
347
|
+
export interface PostUnitGateBlock {
|
|
348
|
+
/** Blocking hook name. */
|
|
349
|
+
hookName: string;
|
|
350
|
+
/** The unit type that triggered the gate. */
|
|
351
|
+
triggerUnitType: string;
|
|
352
|
+
/** The unit ID that triggered the gate. */
|
|
353
|
+
triggerUnitId: string;
|
|
354
|
+
/** Gate artifact name, when configured. */
|
|
355
|
+
artifact?: string;
|
|
356
|
+
/** Absolute path to the gate artifact, when known. */
|
|
357
|
+
artifactPath?: string;
|
|
358
|
+
/** Parsed blocking verdict, when present. */
|
|
359
|
+
verdict?: PostUnitHookOutcomeVerdict | "failed";
|
|
360
|
+
/** Configured routing action that caused the pause. */
|
|
361
|
+
action: PostUnitHookOnBlockAction;
|
|
362
|
+
/** Human-readable pause reason. */
|
|
363
|
+
reason: string;
|
|
364
|
+
/** Current hook cycle count. */
|
|
365
|
+
cycle: number;
|
|
366
|
+
/** Configured max cycle count. */
|
|
367
|
+
maxCycles: number;
|
|
368
|
+
/** Optional compatibility retry artifact. */
|
|
369
|
+
retryArtifact?: string;
|
|
370
|
+
}
|
|
371
|
+
|
|
320
372
|
// ─── Budget & Notification Types ──────────────────────────────────────────
|
|
321
373
|
|
|
322
374
|
export type BudgetEnforcementMode = "warn" | "pause" | "halt";
|
|
@@ -384,10 +436,10 @@ export interface EscalationArtifact {
|
|
|
384
436
|
/** Why the executor recommends that option (1-2 sentences). */
|
|
385
437
|
recommendationRationale: string;
|
|
386
438
|
/**
|
|
387
|
-
* When true, the
|
|
388
|
-
*
|
|
389
|
-
*
|
|
390
|
-
*
|
|
439
|
+
* When true, the recommendation is recorded as the default path but the
|
|
440
|
+
* loop still pauses until the user explicitly resolves the escalation.
|
|
441
|
+
* When false, auto-mode also pauses until the user resolves via
|
|
442
|
+
* `/gsd escalate resolve`.
|
|
391
443
|
*/
|
|
392
444
|
continueWithDefault: boolean;
|
|
393
445
|
createdAt: string;
|
|
@@ -452,6 +504,15 @@ export interface PreDispatchResult {
|
|
|
452
504
|
export interface PersistedHookState {
|
|
453
505
|
/** Cycle counts keyed as "hookName/triggerUnitType/triggerUnitId". */
|
|
454
506
|
cycleCounts: Record<string, number>;
|
|
507
|
+
/** In-flight hook, persisted so blocking gates cannot be skipped after resume. */
|
|
508
|
+
activeHook?: HookExecutionState | null;
|
|
509
|
+
/** Remaining hook queue by hook name and trigger unit. */
|
|
510
|
+
hookQueue?: Array<{
|
|
511
|
+
hookName: string;
|
|
512
|
+
triggerUnitType: string;
|
|
513
|
+
triggerUnitId: string;
|
|
514
|
+
forceRun?: boolean;
|
|
515
|
+
}>;
|
|
455
516
|
/** Timestamp of last state save. */
|
|
456
517
|
savedAt: string;
|
|
457
518
|
}
|
|
@@ -465,6 +526,8 @@ export interface HookStatusEntry {
|
|
|
465
526
|
enabled: boolean;
|
|
466
527
|
/** What unit types it targets. */
|
|
467
528
|
targets: string[];
|
|
529
|
+
/** Whether this post-unit hook is advisory or blocking. */
|
|
530
|
+
criticality?: PostUnitHookCriticality;
|
|
468
531
|
/** Current cycle counts for active triggers. */
|
|
469
532
|
activeCycles: Record<string, number>;
|
|
470
533
|
}
|
|
@@ -644,7 +707,8 @@ export interface CompleteSliceParams {
|
|
|
644
707
|
sliceTitle: string;
|
|
645
708
|
oneLiner: string;
|
|
646
709
|
narrative: string;
|
|
647
|
-
verification
|
|
710
|
+
/** @optional — if omitted, verification section is left blank in summary */
|
|
711
|
+
verification?: string;
|
|
648
712
|
uatContent: string;
|
|
649
713
|
/** @optional — defaults to [] when omitted by models with limited tool-calling */
|
|
650
714
|
keyFiles?: string[];
|