@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
|
@@ -8,6 +8,7 @@ import { mock } from "node:test";
|
|
|
8
8
|
import { postUnitPostVerification, type PostUnitContext } from "../auto-post-unit.ts";
|
|
9
9
|
import { AutoSession } from "../auto/session.ts";
|
|
10
10
|
import { checkPostUnitHooks, resetHookState, resolveHookArtifactPath } from "../post-unit-hooks.ts";
|
|
11
|
+
import { emitJournalEvent } from "../journal.ts";
|
|
11
12
|
import { _clearGsdRootCache } from "../paths.ts";
|
|
12
13
|
import { invalidateAllCaches } from "../cache.ts";
|
|
13
14
|
|
|
@@ -28,6 +29,43 @@ post_unit_hooks:
|
|
|
28
29
|
writeFileSync(join(basePath, ".gsd", "PREFERENCES.md"), content, "utf-8");
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
function writeFailingHookPreferences(basePath: string): void {
|
|
33
|
+
const content = `---
|
|
34
|
+
post_unit_hooks:
|
|
35
|
+
- name: review-arbiter
|
|
36
|
+
after:
|
|
37
|
+
- execute-task
|
|
38
|
+
prompt: Review {taskId}
|
|
39
|
+
artifact: REVIEW-DEBATE.md
|
|
40
|
+
max_cycles: 1
|
|
41
|
+
enabled: true
|
|
42
|
+
- name: follow-up-review
|
|
43
|
+
after:
|
|
44
|
+
- execute-task
|
|
45
|
+
prompt: Follow-up review {taskId}
|
|
46
|
+
enabled: true
|
|
47
|
+
---
|
|
48
|
+
`;
|
|
49
|
+
writeFileSync(join(basePath, ".gsd", "PREFERENCES.md"), content, "utf-8");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function writeBlockingPreferences(basePath: string): void {
|
|
53
|
+
const content = `---
|
|
54
|
+
post_unit_hooks:
|
|
55
|
+
- name: review-arbiter
|
|
56
|
+
after:
|
|
57
|
+
- execute-task
|
|
58
|
+
prompt: Review {taskId}
|
|
59
|
+
agent: arbiter
|
|
60
|
+
artifact: REVIEW-DEBATE.md
|
|
61
|
+
criticality: blocking
|
|
62
|
+
max_cycles: 2
|
|
63
|
+
enabled: true
|
|
64
|
+
---
|
|
65
|
+
`;
|
|
66
|
+
writeFileSync(join(basePath, ".gsd", "PREFERENCES.md"), content, "utf-8");
|
|
67
|
+
}
|
|
68
|
+
|
|
31
69
|
test("post-unit retry_on marks trigger unit as retry in orchestrator before redispatch", async () => {
|
|
32
70
|
const originalCwd = process.cwd();
|
|
33
71
|
const base = mkdtempSync(join(tmpdir(), "gsd-post-unit-retry-"));
|
|
@@ -91,3 +129,144 @@ test("post-unit retry_on marks trigger unit as retry in orchestrator before redi
|
|
|
91
129
|
rmSync(base, { recursive: true, force: true });
|
|
92
130
|
}
|
|
93
131
|
});
|
|
132
|
+
|
|
133
|
+
test("failed post-unit hook pauses auto-mode even when its artifact exists", async () => {
|
|
134
|
+
const originalCwd = process.cwd();
|
|
135
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-post-unit-hook-failed-"));
|
|
136
|
+
const taskDir = join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks");
|
|
137
|
+
mkdirSync(taskDir, { recursive: true });
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
process.chdir(base);
|
|
141
|
+
_clearGsdRootCache();
|
|
142
|
+
invalidateAllCaches();
|
|
143
|
+
resetHookState();
|
|
144
|
+
writeFailingHookPreferences(base);
|
|
145
|
+
|
|
146
|
+
const hookDispatch = checkPostUnitHooks("execute-task", "M001/S01/T01", base);
|
|
147
|
+
assert.equal(hookDispatch?.hookName, "review-arbiter");
|
|
148
|
+
|
|
149
|
+
const artifactPath = resolveHookArtifactPath(base, "M001/S01/T01", "REVIEW-DEBATE.md");
|
|
150
|
+
writeFileSync(artifactPath, "partial review", "utf-8");
|
|
151
|
+
emitJournalEvent(base, {
|
|
152
|
+
ts: "2026-06-03T12:00:00.000Z",
|
|
153
|
+
flowId: "flow-hook-failed",
|
|
154
|
+
seq: 3,
|
|
155
|
+
eventType: "unit-end",
|
|
156
|
+
data: {
|
|
157
|
+
unitType: "hook/review-arbiter",
|
|
158
|
+
unitId: "M001/S01/T01",
|
|
159
|
+
status: "cancelled",
|
|
160
|
+
artifactVerified: false,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const pauseAuto = mock.fn(async () => {});
|
|
165
|
+
const notifications: string[] = [];
|
|
166
|
+
const s = new AutoSession();
|
|
167
|
+
s.basePath = base;
|
|
168
|
+
s.active = true;
|
|
169
|
+
s.currentUnit = { type: "hook/review-arbiter", id: "M001/S01/T01", startedAt: Date.now() };
|
|
170
|
+
|
|
171
|
+
const pctx: PostUnitContext = {
|
|
172
|
+
s,
|
|
173
|
+
ctx: {
|
|
174
|
+
ui: {
|
|
175
|
+
notify: (message: string) => { notifications.push(message); },
|
|
176
|
+
setStatus: () => {},
|
|
177
|
+
setWidget: () => {},
|
|
178
|
+
setFooter: () => {},
|
|
179
|
+
},
|
|
180
|
+
model: { id: "test-model" },
|
|
181
|
+
} as any,
|
|
182
|
+
pi: { sendMessage: async () => {}, setModel: async () => true } as any,
|
|
183
|
+
buildSnapshotOpts: () => ({}),
|
|
184
|
+
lockBase: () => base,
|
|
185
|
+
stopAuto: async () => {},
|
|
186
|
+
pauseAuto,
|
|
187
|
+
updateProgressWidget: () => {},
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const result = await postUnitPostVerification(pctx);
|
|
191
|
+
assert.equal(result, "stopped");
|
|
192
|
+
assert.equal(pauseAuto.mock.callCount(), 1);
|
|
193
|
+
assert.ok(
|
|
194
|
+
notifications.some(message => message.includes("Post-unit hook review-arbiter failed")),
|
|
195
|
+
"pause notification should explain the failed hook",
|
|
196
|
+
);
|
|
197
|
+
} finally {
|
|
198
|
+
process.chdir(originalCwd);
|
|
199
|
+
resetHookState();
|
|
200
|
+
invalidateAllCaches();
|
|
201
|
+
_clearGsdRootCache();
|
|
202
|
+
rmSync(base, { recursive: true, force: true });
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test("post-unit blocking gate pauses auto-mode on needs-attention verdict", async () => {
|
|
207
|
+
const originalCwd = process.cwd();
|
|
208
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-post-unit-gate-"));
|
|
209
|
+
const taskDir = join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks");
|
|
210
|
+
mkdirSync(taskDir, { recursive: true });
|
|
211
|
+
|
|
212
|
+
try {
|
|
213
|
+
process.chdir(base);
|
|
214
|
+
_clearGsdRootCache();
|
|
215
|
+
invalidateAllCaches();
|
|
216
|
+
resetHookState();
|
|
217
|
+
writeBlockingPreferences(base);
|
|
218
|
+
|
|
219
|
+
const hookDispatch = checkPostUnitHooks("execute-task", "M001/S01/T01", base);
|
|
220
|
+
assert.ok(hookDispatch, "hook should dispatch for execute-task");
|
|
221
|
+
|
|
222
|
+
const artifactPath = resolveHookArtifactPath(base, "M001/S01/T01", "REVIEW-DEBATE.md");
|
|
223
|
+
writeFileSync(artifactPath, "---\nverdict: needs-attention\n---\n\nManual review required.\n", "utf-8");
|
|
224
|
+
|
|
225
|
+
const pauseAuto = mock.fn(async () => {});
|
|
226
|
+
const s = new AutoSession();
|
|
227
|
+
s.basePath = base;
|
|
228
|
+
s.active = true;
|
|
229
|
+
s.currentUnit = { type: "hook/review-arbiter", id: "M001/S01/T01", startedAt: Date.now() };
|
|
230
|
+
s.orchestration = {
|
|
231
|
+
start: async () => ({ kind: "started" }),
|
|
232
|
+
advance: async () => ({ kind: "stopped", reason: "unused" }),
|
|
233
|
+
completeActiveUnit: async () => {},
|
|
234
|
+
retryActiveUnit: async () => {},
|
|
235
|
+
resume: async () => ({ kind: "resumed" }),
|
|
236
|
+
stop: async (reason: string) => ({ kind: "stopped", reason }),
|
|
237
|
+
getStatus: () => ({ phase: "running", transitionCount: 0 }),
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const notifications: string[] = [];
|
|
241
|
+
const pctx: PostUnitContext = {
|
|
242
|
+
s,
|
|
243
|
+
ctx: {
|
|
244
|
+
ui: {
|
|
245
|
+
notify: (message: string) => { notifications.push(message); },
|
|
246
|
+
setStatus: () => {},
|
|
247
|
+
setWidget: () => {},
|
|
248
|
+
setFooter: () => {},
|
|
249
|
+
},
|
|
250
|
+
model: { id: "test-model" },
|
|
251
|
+
} as any,
|
|
252
|
+
pi: { sendMessage: async () => {}, setModel: async () => true } as any,
|
|
253
|
+
buildSnapshotOpts: () => ({}),
|
|
254
|
+
lockBase: () => base,
|
|
255
|
+
stopAuto: async () => {},
|
|
256
|
+
pauseAuto,
|
|
257
|
+
updateProgressWidget: () => {},
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const result = await postUnitPostVerification(pctx);
|
|
261
|
+
assert.equal(result, "stopped");
|
|
262
|
+
assert.equal(pauseAuto.mock.callCount(), 1);
|
|
263
|
+
assert.match(notifications.join("\n"), /Post-unit gate "review-arbiter" blocked execute-task M001\/S01\/T01/);
|
|
264
|
+
assert.match(notifications.join("\n"), /\/gsd status/);
|
|
265
|
+
} finally {
|
|
266
|
+
process.chdir(originalCwd);
|
|
267
|
+
resetHookState();
|
|
268
|
+
invalidateAllCaches();
|
|
269
|
+
_clearGsdRootCache();
|
|
270
|
+
rmSync(base, { recursive: true, force: true });
|
|
271
|
+
}
|
|
272
|
+
});
|
|
@@ -561,6 +561,35 @@ test("post-unit hook max_cycles clamping via validatePreferences", () => {
|
|
|
561
561
|
assert.equal(p4.post_unit_hooks![0].max_cycles, 3, "valid value passes through");
|
|
562
562
|
});
|
|
563
563
|
|
|
564
|
+
test("post-unit hook criticality and on_block validation", () => {
|
|
565
|
+
const base = { name: "h", after: ["execute-task"], prompt: "do something", artifact: "REVIEW.md" };
|
|
566
|
+
|
|
567
|
+
const { preferences, errors } = validatePreferences({
|
|
568
|
+
post_unit_hooks: [{
|
|
569
|
+
...base,
|
|
570
|
+
criticality: "blocking",
|
|
571
|
+
on_block: { action: "retry-unit", artifact: "NEEDS-REWORK.md" },
|
|
572
|
+
}],
|
|
573
|
+
} as any);
|
|
574
|
+
assert.equal(errors.length, 0);
|
|
575
|
+
assert.equal(preferences.post_unit_hooks![0].criticality, "blocking");
|
|
576
|
+
assert.deepEqual(preferences.post_unit_hooks![0].on_block, {
|
|
577
|
+
action: "retry-unit",
|
|
578
|
+
artifact: "NEEDS-REWORK.md",
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
const missingArtifact = validatePreferences({
|
|
582
|
+
post_unit_hooks: [{ name: "blocking", after: ["execute-task"], prompt: "do something", criticality: "blocking" }],
|
|
583
|
+
} as any);
|
|
584
|
+
assert.match(missingArtifact.errors.join("\n"), /criticality blocking requires artifact/);
|
|
585
|
+
assert.equal(missingArtifact.preferences.post_unit_hooks, undefined);
|
|
586
|
+
|
|
587
|
+
const invalidAction = validatePreferences({
|
|
588
|
+
post_unit_hooks: [{ ...base, on_block: { action: "teleport" } }],
|
|
589
|
+
} as any);
|
|
590
|
+
assert.match(invalidAction.errors.join("\n"), /invalid on_block action/);
|
|
591
|
+
});
|
|
592
|
+
|
|
564
593
|
test("pre-dispatch hook action validation via validatePreferences", () => {
|
|
565
594
|
const base = { name: "h", before: ["execute-task"] };
|
|
566
595
|
|
|
@@ -2,6 +2,14 @@ import test from "node:test";
|
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
+
import {
|
|
6
|
+
RUN_UAT_BROWSER_TOOL_NAMES,
|
|
7
|
+
buildRunUatResultPresentation,
|
|
8
|
+
buildRunUatPresentationForType,
|
|
9
|
+
RUN_UAT_READ_ONLY_TOOL_NAMES,
|
|
10
|
+
RUN_UAT_TOOL_PRESENTATION_PLAN_ID,
|
|
11
|
+
RUN_UAT_WORKFLOW_TOOL_NAMES,
|
|
12
|
+
} from "../tool-presentation-plan.ts";
|
|
5
13
|
|
|
6
14
|
const promptsDir = join(process.cwd(), "src/resources/extensions/gsd/prompts");
|
|
7
15
|
const templatesDir = join(process.cwd(), "src/resources/extensions/gsd/templates");
|
|
@@ -25,11 +33,15 @@ test("reactive-execute prompt keeps task summaries with subagents and avoids bat
|
|
|
25
33
|
test("run-uat prompt branches on dynamic UAT mode and supports runtime evidence", () => {
|
|
26
34
|
const prompt = readPrompt("run-uat");
|
|
27
35
|
assert.match(prompt, /\*\*Detected UAT mode:\*\*\s*`\{\{uatType\}\}`/);
|
|
28
|
-
assert.match(prompt, /uatType:\s
|
|
36
|
+
assert.match(prompt, /uatType:\s*"\{\{uatType\}\}"/);
|
|
37
|
+
assert.match(prompt, /gsd_uat_result_save/);
|
|
38
|
+
assert.match(prompt, /presentedTools/);
|
|
39
|
+
assert.match(prompt, /\{\{canonicalPresentation\}\}/);
|
|
29
40
|
assert.match(prompt, /live-runtime/);
|
|
30
41
|
assert.match(prompt, /browser\/runtime\/network/i);
|
|
31
42
|
assert.match(prompt, /NEEDS-HUMAN/);
|
|
32
43
|
assert.doesNotMatch(prompt, /uatType:\s*artifact-driven/);
|
|
44
|
+
assert.doesNotMatch(prompt, /Call `gsd_summary_save`/);
|
|
33
45
|
});
|
|
34
46
|
|
|
35
47
|
test("run-uat prompt lists canonical gsd_uat_exec intent values", () => {
|
|
@@ -42,6 +54,45 @@ test("run-uat prompt lists canonical gsd_uat_exec intent values", () => {
|
|
|
42
54
|
assert.match(prompt, /do not use `artifact`, `runtime`, or `human-follow-up` as `intent`/i);
|
|
43
55
|
});
|
|
44
56
|
|
|
57
|
+
test("run-uat prompt gives the complete UAT result-save presentation contract", () => {
|
|
58
|
+
const prompt = readPrompt("run-uat");
|
|
59
|
+
assert.match(prompt, /Call `gsd_uat_result_save` once after all checks are complete/);
|
|
60
|
+
assert.doesNotMatch(prompt, /Call `gsd_summary_save` with `artifact_type: "ASSESSMENT"`/);
|
|
61
|
+
assert.match(prompt, /\{\{canonicalPresentation\}\}/);
|
|
62
|
+
assert.match(prompt, /\{\{toolPresentationPlanId\}\}/);
|
|
63
|
+
|
|
64
|
+
const presentation = buildRunUatResultPresentation();
|
|
65
|
+
assert.equal(presentation.toolPresentationPlanId, RUN_UAT_TOOL_PRESENTATION_PLAN_ID);
|
|
66
|
+
for (const toolName of RUN_UAT_WORKFLOW_TOOL_NAMES) {
|
|
67
|
+
assert.ok(presentation.presentedTools.includes(toolName), `presentation should include required tool ${toolName}`);
|
|
68
|
+
}
|
|
69
|
+
for (const toolName of RUN_UAT_READ_ONLY_TOOL_NAMES) {
|
|
70
|
+
assert.ok(presentation.presentedTools.includes(toolName), `presentation should include read-only tool ${toolName}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
for (const toolName of ["gsd_exec", "gsd_summary_save", "gsd_save_gate_result"] as const) {
|
|
74
|
+
assert.ok(
|
|
75
|
+
presentation.blockedTools.some((entry) => entry.name === toolName),
|
|
76
|
+
`presentation should include blocked tool ${toolName}`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
assert.ok(
|
|
81
|
+
presentation.blockedTools.every((entry) => entry.reason === "forbidden during run-uat"),
|
|
82
|
+
"presentation should explain blocked run-uat tools",
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("browser-executable UAT presentation uses direct managed browser tools", () => {
|
|
87
|
+
const presentation = buildRunUatPresentationForType("browser-executable");
|
|
88
|
+
|
|
89
|
+
assert.equal(presentation.surface, "hybrid");
|
|
90
|
+
for (const toolName of RUN_UAT_BROWSER_TOOL_NAMES) {
|
|
91
|
+
assert.ok(presentation.presentedTools.includes(toolName), `presentation should include browser tool ${toolName}`);
|
|
92
|
+
}
|
|
93
|
+
assert.ok(!presentation.presentedTools.some((toolName) => toolName.startsWith("mcp__gsd-browser__")));
|
|
94
|
+
});
|
|
95
|
+
|
|
45
96
|
test("workflow-start prompt defaults to autonomy instead of per-phase confirmation", () => {
|
|
46
97
|
const prompt = readPrompt("workflow-start");
|
|
47
98
|
assert.match(prompt, /Keep moving by default/i);
|
|
@@ -475,6 +526,27 @@ test("reactive-execute prompt references tool calls instead of checkbox updates"
|
|
|
475
526
|
assert.match(prompt, /completion tool calls/);
|
|
476
527
|
});
|
|
477
528
|
|
|
529
|
+
test("parallel subagent prompts forbid serialized tasks arrays", () => {
|
|
530
|
+
const expectations = [
|
|
531
|
+
{ name: "reactive-execute", agent: "worker" },
|
|
532
|
+
{ name: "parallel-research-slices", agent: "scout" },
|
|
533
|
+
{ name: "gate-evaluate", agent: "tester" },
|
|
534
|
+
] as const;
|
|
535
|
+
|
|
536
|
+
for (const { name, agent } of expectations) {
|
|
537
|
+
const prompt = readPrompt(name);
|
|
538
|
+
assert.match(prompt, /tasks:\s*\[\.\.\.\]/, `${name} must show the native array placeholder`);
|
|
539
|
+
assert.match(prompt, /native JSON array/i, `${name} must explicitly require a native JSON array`);
|
|
540
|
+
assert.match(prompt, /Do NOT JSON\.stringify/i, `${name} must forbid JSON.stringify on tasks`);
|
|
541
|
+
assert.match(prompt, /must be array/i, `${name} must mention the subagent validation failure`);
|
|
542
|
+
assert.match(
|
|
543
|
+
prompt,
|
|
544
|
+
new RegExp(`tasks:\\s*\\[\\{\\s*agent:\\s*"${agent}"`, "i"),
|
|
545
|
+
`${name} must show the concrete ${agent} task call shape`,
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
|
|
478
550
|
// ─── Project-shape classifier + 3-or-4-options-with-Other-hatch contract ──
|
|
479
551
|
|
|
480
552
|
test("guided-discuss-project classifies project shape and persists the verdict to PROJECT.md", () => {
|
|
@@ -22,6 +22,20 @@ test("resolveExtensionDirFromCandidates prefers user-local dir when both trees a
|
|
|
22
22
|
assert.equal(resolved, agentDir);
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
+
test("resolveExtensionDirFromCandidates prefers source checkout dir when both trees are valid", () => {
|
|
26
|
+
const moduleDir = "/repo/src/resources/extensions/gsd";
|
|
27
|
+
const agentDir = "/home/user/.gsd/agent/extensions/gsd";
|
|
28
|
+
const paths = new Set<string>([
|
|
29
|
+
join(moduleDir, "prompts"),
|
|
30
|
+
join(moduleDir, "templates", "task-summary.md"),
|
|
31
|
+
join(agentDir, "prompts"),
|
|
32
|
+
join(agentDir, "templates", "task-summary.md"),
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
const resolved = resolveExtensionDirFromCandidates(moduleDir, agentDir, makeExists(paths));
|
|
36
|
+
assert.equal(resolved, moduleDir);
|
|
37
|
+
});
|
|
38
|
+
|
|
25
39
|
test("resolveExtensionDirFromCandidates rejects module dir missing task-summary template", () => {
|
|
26
40
|
const moduleDir = "/npm/global/gsd";
|
|
27
41
|
const agentDir = "/home/user/.gsd/agent/extensions/gsd";
|
|
@@ -24,25 +24,24 @@ describe("queued-discuss-fast-path", () => {
|
|
|
24
24
|
);
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
test("2. dispatchDiscussForMilestone computes fastPathInstruction and passes it to
|
|
27
|
+
test("2. dispatchDiscussForMilestone computes fastPathInstruction and passes it to buildDiscussMilestonePrompt", () => {
|
|
28
28
|
const source = guidedFlowSrc();
|
|
29
29
|
const fnStart = source.indexOf("async function dispatchDiscussForMilestone(");
|
|
30
30
|
assert.ok(fnStart > 0, "dispatchDiscussForMilestone must exist");
|
|
31
|
-
const fnEnd = source.indexOf("\nasync function ", fnStart + 1);
|
|
32
31
|
const fnBody = extractSourceRegion(source, "async function dispatchDiscussForMilestone(");
|
|
33
32
|
assert.ok(
|
|
34
33
|
fnBody.includes("fastPathInstruction"),
|
|
35
34
|
"dispatchDiscussForMilestone must compute fastPathInstruction",
|
|
36
35
|
);
|
|
37
36
|
assert.ok(
|
|
38
|
-
fnBody.includes("
|
|
39
|
-
"dispatchDiscussForMilestone must call
|
|
37
|
+
fnBody.includes("buildDiscussMilestonePrompt("),
|
|
38
|
+
"dispatchDiscussForMilestone must call buildDiscussMilestonePrompt",
|
|
40
39
|
);
|
|
41
|
-
const
|
|
42
|
-
const fastPathIdx = fnBody.indexOf("fastPathInstruction",
|
|
40
|
+
const buildPromptIdx = fnBody.indexOf("buildDiscussMilestonePrompt(");
|
|
41
|
+
const fastPathIdx = fnBody.indexOf("fastPathInstruction", buildPromptIdx);
|
|
43
42
|
assert.ok(
|
|
44
|
-
fastPathIdx >
|
|
45
|
-
"fastPathInstruction must be passed to
|
|
43
|
+
fastPathIdx > buildPromptIdx,
|
|
44
|
+
"fastPathInstruction must be passed to buildDiscussMilestonePrompt in dispatchDiscussForMilestone",
|
|
46
45
|
);
|
|
47
46
|
});
|
|
48
47
|
|
|
@@ -70,6 +70,50 @@ test("register-hooks hard-blocks destructive bash commands outside auto-mode", a
|
|
|
70
70
|
assert.match(block?.reason ?? "", /IaC apply\/destroy/);
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
+
test("register-hooks keeps depth-gate reason model-facing and adds displayReason", async (t) => {
|
|
74
|
+
const dir = makeTempDir("display-reason");
|
|
75
|
+
const originalCwd = process.cwd();
|
|
76
|
+
process.chdir(dir);
|
|
77
|
+
resetWriteGateState(dir);
|
|
78
|
+
|
|
79
|
+
t.after(() => {
|
|
80
|
+
try {
|
|
81
|
+
resetWriteGateState(dir);
|
|
82
|
+
} finally {
|
|
83
|
+
process.chdir(originalCwd);
|
|
84
|
+
rmSync(dir, { recursive: true, force: true });
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const handlers = new Map<string, Array<(event: any, ctx?: any) => Promise<any> | any>>();
|
|
89
|
+
const pi = {
|
|
90
|
+
on(event: string, handler: (event: any, ctx?: any) => Promise<any> | any) {
|
|
91
|
+
const existing = handlers.get(event) ?? [];
|
|
92
|
+
existing.push(handler);
|
|
93
|
+
handlers.set(event, existing);
|
|
94
|
+
},
|
|
95
|
+
} as any;
|
|
96
|
+
|
|
97
|
+
registerHooks(pi, []);
|
|
98
|
+
|
|
99
|
+
let block: any;
|
|
100
|
+
for (const handler of handlers.get("tool_call") ?? []) {
|
|
101
|
+
const result = await handler({
|
|
102
|
+
toolName: "write",
|
|
103
|
+
input: {
|
|
104
|
+
path: join(dir, ".gsd", "milestones", "M001", "M001-CONTEXT.md"),
|
|
105
|
+
content: "# M001 Context\n",
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
if (result?.block) block = result;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
assert.equal(block?.block, true);
|
|
112
|
+
assert.match(block?.reason ?? "", /HARD BLOCK: Cannot write to milestone CONTEXT\.md/);
|
|
113
|
+
assert.match(block?.reason ?? "", /ask_user_questions/);
|
|
114
|
+
assert.equal(block?.displayReason, "Depth check required before writing milestone context.");
|
|
115
|
+
});
|
|
116
|
+
|
|
73
117
|
test("register-hooks unlocks milestone depth verification from question id without guided-flow state (#4047)", async (t) => {
|
|
74
118
|
const dir = makeTempDir("manual");
|
|
75
119
|
const originalCwd = process.cwd();
|
|
@@ -8,6 +8,7 @@ import { test, describe, beforeEach } from "node:test";
|
|
|
8
8
|
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
9
9
|
import { tmpdir } from "node:os";
|
|
10
10
|
import { join } from "node:path";
|
|
11
|
+
import { emitJournalEvent } from "../journal.ts";
|
|
11
12
|
import {
|
|
12
13
|
RuleRegistry,
|
|
13
14
|
getRegistry,
|
|
@@ -16,6 +17,7 @@ import {
|
|
|
16
17
|
resetRegistry,
|
|
17
18
|
convertDispatchRules,
|
|
18
19
|
getOrCreateRegistry,
|
|
20
|
+
resolveHookArtifactPath,
|
|
19
21
|
} from "../rule-registry.ts";
|
|
20
22
|
import type { UnifiedRule } from "../rule-types.ts";
|
|
21
23
|
import type { DispatchAction, DispatchContext } from "../auto-dispatch.ts";
|
|
@@ -443,6 +445,79 @@ describe("RuleRegistry", () => {
|
|
|
443
445
|
}
|
|
444
446
|
});
|
|
445
447
|
|
|
448
|
+
test("failed hook completion with an artifact does not dequeue the next hook", () => {
|
|
449
|
+
const originalGsdHome = process.env.GSD_HOME;
|
|
450
|
+
const projectRoot = mkdtempSync(join(tmpdir(), "gsd-hook-failed-"));
|
|
451
|
+
const tempGsdHome = mkdtempSync(join(tmpdir(), "gsd-hook-home-"));
|
|
452
|
+
const unitId = "M001/S01/T01";
|
|
453
|
+
|
|
454
|
+
try {
|
|
455
|
+
mkdirSync(join(projectRoot, ".gsd", "milestones", "M001", "slices", "S01", "tasks"), { recursive: true });
|
|
456
|
+
writeFileSync(
|
|
457
|
+
join(projectRoot, ".gsd", "PREFERENCES.md"),
|
|
458
|
+
[
|
|
459
|
+
"---",
|
|
460
|
+
"version: 1",
|
|
461
|
+
"post_unit_hooks:",
|
|
462
|
+
" - name: review-arbiter",
|
|
463
|
+
" after: [execute-task]",
|
|
464
|
+
" prompt: Review {taskId}",
|
|
465
|
+
" artifact: REVIEW.md",
|
|
466
|
+
" max_cycles: 1",
|
|
467
|
+
" - name: follow-up-review",
|
|
468
|
+
" after: [execute-task]",
|
|
469
|
+
" prompt: Follow-up review {taskId}",
|
|
470
|
+
"---",
|
|
471
|
+
].join("\n"),
|
|
472
|
+
"utf-8",
|
|
473
|
+
);
|
|
474
|
+
process.env.GSD_HOME = tempGsdHome;
|
|
475
|
+
|
|
476
|
+
const registry = new RuleRegistry([]);
|
|
477
|
+
const firstHook = registry.evaluatePostUnit("execute-task", unitId, projectRoot);
|
|
478
|
+
assert.equal(firstHook?.hookName, "review-arbiter");
|
|
479
|
+
|
|
480
|
+
writeFileSync(
|
|
481
|
+
resolveHookArtifactPath(projectRoot, unitId, "REVIEW.md"),
|
|
482
|
+
"partial review output",
|
|
483
|
+
"utf-8",
|
|
484
|
+
);
|
|
485
|
+
emitJournalEvent(projectRoot, {
|
|
486
|
+
ts: "2026-06-03T12:00:00.000Z",
|
|
487
|
+
flowId: "flow-hook-failed",
|
|
488
|
+
seq: 3,
|
|
489
|
+
eventType: "unit-end",
|
|
490
|
+
data: {
|
|
491
|
+
unitType: "hook/review-arbiter",
|
|
492
|
+
unitId,
|
|
493
|
+
status: "cancelled",
|
|
494
|
+
artifactVerified: false,
|
|
495
|
+
errorContext: {
|
|
496
|
+
message: "Provider error: Stream ended without finish_reason",
|
|
497
|
+
category: "provider",
|
|
498
|
+
},
|
|
499
|
+
},
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
const nextHook = registry.evaluatePostUnit("hook/review-arbiter", unitId, projectRoot);
|
|
503
|
+
assert.equal(nextHook, null, "failed hook must not allow follow-up hook dispatch");
|
|
504
|
+
const failure = registry.consumeHookFailure();
|
|
505
|
+
assert.equal(failure?.hookName, "review-arbiter");
|
|
506
|
+
assert.match(failure?.reason ?? "", /status cancelled/);
|
|
507
|
+
|
|
508
|
+
const resumedRegistry = new RuleRegistry([]);
|
|
509
|
+
resumedRegistry.restoreState(projectRoot);
|
|
510
|
+
const resumedHook = resumedRegistry.evaluatePostUnit("execute-task", unitId, projectRoot);
|
|
511
|
+
assert.equal(resumedHook, null, "resumed hook evaluation must not skip failed hook artifact");
|
|
512
|
+
assert.equal(resumedRegistry.consumeHookFailure()?.hookName, "review-arbiter");
|
|
513
|
+
} finally {
|
|
514
|
+
if (originalGsdHome === undefined) delete process.env.GSD_HOME;
|
|
515
|
+
else process.env.GSD_HOME = originalGsdHome;
|
|
516
|
+
rmSync(projectRoot, { recursive: true, force: true });
|
|
517
|
+
rmSync(tempGsdHome, { recursive: true, force: true });
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
|
|
446
521
|
// ── matchedRule provenance (S02 journal support) ───────────────────
|
|
447
522
|
|
|
448
523
|
test("evaluateDispatch result includes matchedRule on dispatch match", async () => {
|
|
@@ -123,6 +123,10 @@ test("#4782 phase 3: buildRunUatPrompt inlines UAT and keeps summary/project con
|
|
|
123
123
|
// Project path is advertised on-demand; full project body is not inlined.
|
|
124
124
|
assert.match(prompt, /\.gsd\/PROJECT\.md/);
|
|
125
125
|
assert.ok(!prompt.includes("Run-UAT composer fixture project"), "run-uat should not inline full project context");
|
|
126
|
+
|
|
127
|
+
assert.match(prompt, /"toolPresentationPlanId": "run-uat\/default-v1"/);
|
|
128
|
+
assert.match(prompt, /"gsd_uat_result_save"/);
|
|
129
|
+
assert.match(prompt, /"read"/);
|
|
126
130
|
});
|
|
127
131
|
|
|
128
132
|
test("#4782 phase 3: buildRunUatPrompt omits optional slice summary when file is missing", async (t) => {
|
|
@@ -7,6 +7,7 @@ import assert from "node:assert/strict";
|
|
|
7
7
|
import { classifyFailure } from "../recovery-classification.js";
|
|
8
8
|
import { reconcileBeforeDispatch } from "../state-reconciliation.js";
|
|
9
9
|
import { compileUnitToolContract } from "../tool-contract.js";
|
|
10
|
+
import { shouldBlockAutoUnitToolCall } from "../auto-unit-tool-scope.js";
|
|
10
11
|
import type { GSDState } from "../types.js";
|
|
11
12
|
|
|
12
13
|
function makeState(overrides: Partial<GSDState> = {}): GSDState {
|
|
@@ -63,10 +64,35 @@ test("Tool Contract compiles known Unit prompt and tool policy", () => {
|
|
|
63
64
|
assert.equal(result.ok, true);
|
|
64
65
|
assert.equal(result.ok && result.contract.unitType, "execute-task");
|
|
65
66
|
assert.deepEqual(result.ok && result.contract.requiredWorkflowTools, ["gsd_task_complete"]);
|
|
67
|
+
assert.deepEqual(result.ok && result.contract.forbiddenWorkflowTools, []);
|
|
66
68
|
assert.equal(result.ok && result.contract.toolsPolicy.mode, "all");
|
|
67
69
|
assert.ok(result.ok && result.contract.validationRules.includes("closeout-tool-present"));
|
|
68
70
|
});
|
|
69
71
|
|
|
72
|
+
test("Tool Contract records high-risk cross-phase tool boundaries without single-owning every tool", () => {
|
|
73
|
+
const completeSlice = compileUnitToolContract("complete-slice");
|
|
74
|
+
const runUat = compileUnitToolContract("run-uat");
|
|
75
|
+
|
|
76
|
+
assert.equal(completeSlice.ok, true);
|
|
77
|
+
assert.ok(
|
|
78
|
+
completeSlice.ok &&
|
|
79
|
+
completeSlice.contract.forbiddenWorkflowTools.some((tool) => tool.name === "gsd_uat_result_save"),
|
|
80
|
+
"complete-slice should explicitly forbid saving UAT Assessments",
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
assert.equal(runUat.ok, true);
|
|
84
|
+
assert.ok(
|
|
85
|
+
runUat.ok &&
|
|
86
|
+
runUat.contract.requiredWorkflowTools.includes("gsd_uat_result_save"),
|
|
87
|
+
"run-uat should own the UAT result-save tool",
|
|
88
|
+
);
|
|
89
|
+
assert.ok(
|
|
90
|
+
runUat.ok &&
|
|
91
|
+
runUat.contract.forbiddenWorkflowTools.some((tool) => tool.name === "gsd_exec"),
|
|
92
|
+
"run-uat should prefer typed UAT execution over generic gsd_exec",
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
|
|
70
96
|
test("Tool Contract fails closed for unknown Units", () => {
|
|
71
97
|
const result = compileUnitToolContract("custom-step");
|
|
72
98
|
|
|
@@ -74,10 +100,20 @@ test("Tool Contract fails closed for unknown Units", () => {
|
|
|
74
100
|
assert.equal(!result.ok && result.reason, "unknown-unit-type");
|
|
75
101
|
});
|
|
76
102
|
|
|
103
|
+
test("auto Unit tool scope blocks complete-slice from saving UAT Assessment", () => {
|
|
104
|
+
const result = shouldBlockAutoUnitToolCall("complete-slice", "gsd_uat_result_save");
|
|
105
|
+
|
|
106
|
+
assert.equal(result.block, true);
|
|
107
|
+
assert.match(result.reason ?? "", /Tool Contract failure/);
|
|
108
|
+
assert.match(result.reason ?? "", /Run UAT owns persisted UAT Assessment/);
|
|
109
|
+
});
|
|
110
|
+
|
|
77
111
|
test("Recovery Classification covers ADR-015 failure families", () => {
|
|
78
112
|
const cases = [
|
|
79
113
|
["invalid tool schema enum", "tool-schema", "stop"],
|
|
114
|
+
["Tool Contract failure: complete-slice cannot use gsd_uat_result_save", "tool-contract", "stop"],
|
|
80
115
|
["deterministic policy rejection", "deterministic-policy", "stop"],
|
|
116
|
+
["cannot legally advance because required UAT Assessment artifact is missing", "lifecycle-progression", "stop"],
|
|
81
117
|
["stale worker lease", "stale-worker", "stop"],
|
|
82
118
|
["worktree root missing .git", "worktree-invalid", "stop"],
|
|
83
119
|
["verification drift in state snapshot", "verification-drift", "escalate"],
|