@opengsd/gsd-pi 1.1.1-dev.616a1a1 → 1.1.1-dev.9bb7453
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/claude-code-cli/stream-adapter.js +167 -16
- package/dist/resources/extensions/gsd/auto/orchestrator.js +0 -1
- package/dist/resources/extensions/gsd/auto/phases.js +4 -3
- package/dist/resources/extensions/gsd/auto-dashboard.js +92 -17
- package/dist/resources/extensions/gsd/auto-dispatch.js +44 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +134 -10
- package/dist/resources/extensions/gsd/auto-prompts.js +68 -22
- package/dist/resources/extensions/gsd/auto-recovery.js +4 -4
- package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
- package/dist/resources/extensions/gsd/auto-start.js +94 -15
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
- package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +2 -1
- package/dist/resources/extensions/gsd/auto.js +31 -6
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +83 -4
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +43 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +39 -14
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +16 -10
- package/dist/resources/extensions/gsd/browser-evidence.js +29 -2
- package/dist/resources/extensions/gsd/commands/catalog.js +6 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +6 -2
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +9 -5
- package/dist/resources/extensions/gsd/commands-handlers.js +76 -11
- package/dist/resources/extensions/gsd/commands-maintenance.js +172 -2
- package/dist/resources/extensions/gsd/commands-mcp-status.js +109 -60
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +3 -1
- package/dist/resources/extensions/gsd/commands-verdict.js +1 -1
- package/dist/resources/extensions/gsd/config-overlay.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/error-classifier.js +2 -1
- package/dist/resources/extensions/gsd/escalation.js +4 -4
- package/dist/resources/extensions/gsd/exec-sandbox.js +2 -0
- package/dist/resources/extensions/gsd/forensics.js +74 -2
- package/dist/resources/extensions/gsd/gsd-db.js +42 -6
- package/dist/resources/extensions/gsd/guided-flow.js +30 -69
- package/dist/resources/extensions/gsd/mcp-filter.js +3 -0
- package/dist/resources/extensions/gsd/mcp-project-config.js +76 -84
- package/dist/resources/extensions/gsd/memory-store.js +4 -1
- package/dist/resources/extensions/gsd/migration-auto-check.js +2 -2
- 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 +48 -24
- package/dist/resources/extensions/gsd/prompts/system.md +3 -1
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
- package/dist/resources/extensions/gsd/rule-registry.js +428 -52
- package/dist/resources/extensions/gsd/safety/destructive-guard.js +3 -0
- package/dist/resources/extensions/gsd/skill-activation.js +20 -3
- package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +4 -2
- package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +1 -1
- package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +18 -1
- package/dist/resources/extensions/gsd/state-reconciliation/index.js +6 -0
- package/dist/resources/extensions/gsd/state.js +17 -14
- package/dist/resources/extensions/gsd/templates/plan.md +3 -1
- package/dist/resources/extensions/gsd/tool-presentation-plan.js +120 -0
- 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/exec-tool.js +109 -0
- package/dist/resources/extensions/gsd/tools/plan-slice.js +14 -9
- package/dist/resources/extensions/gsd/tools/reopen-milestone.js +2 -2
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +46 -16
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +403 -3
- package/dist/resources/extensions/gsd/unit-context-manifest.js +8 -3
- package/dist/resources/extensions/gsd/validation-block-guard.js +2 -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-auto-prep.js +3 -1
- package/dist/resources/extensions/gsd/workflow-mcp.js +5 -1
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +24 -0
- package/dist/resources/extensions/mcp-client/manager.js +31 -1
- 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 +6 -6
- 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 +6 -6
- 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 +5 -3
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/dist/workflow.d.ts +14 -0
- package/packages/contracts/dist/workflow.d.ts.map +1 -1
- package/packages/contracts/dist/workflow.js +16 -0
- package/packages/contracts/dist/workflow.js.map +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/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/settings-selector.d.ts +2 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js +10 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts +1 -0
- 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 +92 -31
- 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/dist/modes/interactive/interactive-extension-dialogs.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-dialogs.js +2 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-dialogs.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js +5 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.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.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +84 -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 +38 -0
- 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/types.d.ts +3 -0
- 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/image-models.generated.d.ts +15 -0
- package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/image-models.generated.js +15 -0
- package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +406 -17
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +484 -116
- 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/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/settings-manager.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +11 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/dist/terminal.d.ts +1 -0
- package/packages/pi-tui/dist/terminal.d.ts.map +1 -1
- package/packages/pi-tui/dist/terminal.js +8 -4
- package/packages/pi-tui/dist/terminal.js.map +1 -1
- 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/claude-code-cli/stream-adapter.ts +196 -16
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +239 -63
- package/src/resources/extensions/gsd/auto/orchestrator.ts +0 -1
- package/src/resources/extensions/gsd/auto/phases.ts +5 -3
- package/src/resources/extensions/gsd/auto-dashboard.ts +98 -18
- package/src/resources/extensions/gsd/auto-dispatch.ts +53 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +166 -9
- package/src/resources/extensions/gsd/auto-prompts.ts +102 -15
- package/src/resources/extensions/gsd/auto-recovery.ts +4 -4
- package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -0
- package/src/resources/extensions/gsd/auto-start.ts +112 -17
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
- package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +2 -1
- package/src/resources/extensions/gsd/auto.ts +47 -5
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +90 -4
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +51 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +60 -19
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +21 -10
- package/src/resources/extensions/gsd/browser-evidence.ts +26 -2
- package/src/resources/extensions/gsd/commands/catalog.ts +6 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +6 -2
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +9 -5
- package/src/resources/extensions/gsd/commands-handlers.ts +76 -11
- package/src/resources/extensions/gsd/commands-maintenance.ts +197 -2
- package/src/resources/extensions/gsd/commands-mcp-status.ts +136 -58
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +4 -1
- package/src/resources/extensions/gsd/commands-verdict.ts +1 -1
- package/src/resources/extensions/gsd/config-overlay.ts +3 -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/error-classifier.ts +2 -1
- package/src/resources/extensions/gsd/escalation.ts +4 -4
- package/src/resources/extensions/gsd/exec-sandbox.ts +4 -0
- package/src/resources/extensions/gsd/forensics.ts +99 -5
- package/src/resources/extensions/gsd/gsd-db.ts +46 -8
- package/src/resources/extensions/gsd/guided-flow.ts +91 -83
- package/src/resources/extensions/gsd/mcp-filter.ts +3 -0
- package/src/resources/extensions/gsd/mcp-project-config.ts +105 -88
- package/src/resources/extensions/gsd/memory-store.ts +4 -1
- package/src/resources/extensions/gsd/migration-auto-check.ts +2 -2
- package/src/resources/extensions/gsd/post-unit-hooks.ts +14 -1
- package/src/resources/extensions/gsd/preferences-types.ts +1 -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 +48 -24
- package/src/resources/extensions/gsd/prompts/system.md +3 -1
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
- 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/safety/destructive-guard.ts +3 -0
- package/src/resources/extensions/gsd/skill-activation.ts +20 -2
- package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +4 -2
- package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +1 -1
- package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +20 -0
- package/src/resources/extensions/gsd/state-reconciliation/index.ts +6 -0
- package/src/resources/extensions/gsd/state-reconciliation/types.ts +1 -0
- package/src/resources/extensions/gsd/state.ts +18 -14
- package/src/resources/extensions/gsd/templates/plan.md +3 -1
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +156 -4
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +123 -0
- package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +143 -2
- package/src/resources/extensions/gsd/tests/auto-start-project-milestone-reconcile.test.ts +24 -2
- package/src/resources/extensions/gsd/tests/browser-evidence.test.ts +142 -0
- package/src/resources/extensions/gsd/tests/commands-dispatcher-validation-block.test.ts +38 -3
- package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +6 -2
- 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/derive-state-db.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +50 -13
- package/src/resources/extensions/gsd/tests/discuss-milestone-structured-questions.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +60 -0
- 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/exec-sandbox.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/exec-tool.test.ts +69 -0
- 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/gsd-rebuild.test.ts +199 -0
- package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +75 -0
- 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.test.ts +12 -9
- package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +66 -10
- package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +13 -6
- package/src/resources/extensions/gsd/tests/mcp-filter.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +179 -0
- package/src/resources/extensions/gsd/tests/memory-maintenance.test.ts +39 -8
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/new-milestone-discuss-routing.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +54 -7
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +39 -1
- 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 +53 -1
- package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +18 -1
- package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +7 -8
- package/src/resources/extensions/gsd/tests/reactive-executor.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +75 -0
- package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +6 -2
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +191 -0
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +84 -10
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +12 -2
- package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/tui-header-lifecycle.test.ts +29 -6
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +29 -6
- 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/validation-block-guard.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +17 -2
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +213 -0
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +25 -0
- package/src/resources/extensions/gsd/tool-presentation-plan.ts +167 -0
- 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/exec-tool.ts +130 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +14 -9
- package/src/resources/extensions/gsd/tools/reopen-milestone.ts +2 -2
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +46 -15
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +489 -3
- package/src/resources/extensions/gsd/types.ts +69 -5
- package/src/resources/extensions/gsd/unit-context-manifest.ts +14 -5
- package/src/resources/extensions/gsd/validation-block-guard.ts +2 -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-auto-prep.ts +2 -1
- package/src/resources/extensions/gsd/workflow-mcp.ts +5 -1
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +26 -0
- package/src/resources/extensions/mcp-client/manager.ts +33 -1
- package/src/resources/extensions/mcp-client/tests/manager.test.ts +35 -0
- package/src/resources/extensions/shared/gsd-browser-cli.ts +172 -0
- /package/dist/web/standalone/.next/static/{L9N5SPFi7f-Ne4u2uXzCe → jBtwT9v1u2lUA3UEOy_ZH}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{L9N5SPFi7f-Ne4u2uXzCe → jBtwT9v1u2lUA3UEOy_ZH}/_ssgManifest.js +0 -0
|
@@ -22,23 +22,31 @@ import { tmpdir } from "node:os";
|
|
|
22
22
|
|
|
23
23
|
import { loadSkills } from "@gsd/pi-coding-agent";
|
|
24
24
|
import {
|
|
25
|
-
|
|
25
|
+
buildCompleteSlicePrompt,
|
|
26
26
|
buildParallelResearchSlicesPrompt,
|
|
27
|
+
buildResearchSlicePrompt,
|
|
27
28
|
} from "../auto-prompts.ts";
|
|
28
29
|
|
|
29
30
|
const SKILL_NAME = "testskill";
|
|
31
|
+
const COMPLETE_SLICE_SKILL_NAME = "complete-slice-policies";
|
|
30
32
|
const SKILL_ACTIVATION_SUBSTRING = `Call Skill({ skill: '${SKILL_NAME}' })`;
|
|
33
|
+
const COMPLETE_SLICE_SKILL_ACTIVATION_SUBSTRING = `Call Skill({ skill: '${COMPLETE_SLICE_SKILL_NAME}' })`;
|
|
31
34
|
|
|
32
35
|
const tmpDirs: string[] = [];
|
|
33
36
|
let savedCwd: string | undefined;
|
|
34
37
|
|
|
35
|
-
function setupProjectWithSkill(
|
|
38
|
+
function setupProjectWithSkill(options: {
|
|
39
|
+
skillName?: string;
|
|
40
|
+
preferencesLines?: string[];
|
|
41
|
+
} = {}): string {
|
|
42
|
+
const skillName = options.skillName ?? SKILL_NAME;
|
|
36
43
|
const base = mkdtempSync(join(tmpdir(), "gsd-worker-skill-int-"));
|
|
37
44
|
tmpDirs.push(base);
|
|
38
45
|
|
|
39
46
|
// Milestone roadmap — buildResearchSlicePrompt inlines the roadmap excerpt.
|
|
40
47
|
const milestoneDir = join(base, ".gsd", "milestones", "M001");
|
|
41
|
-
|
|
48
|
+
const sliceOneDir = join(milestoneDir, "slices", "S01");
|
|
49
|
+
mkdirSync(join(sliceOneDir, "tasks"), { recursive: true });
|
|
42
50
|
mkdirSync(join(milestoneDir, "slices", "S02"), { recursive: true });
|
|
43
51
|
writeFileSync(
|
|
44
52
|
join(milestoneDir, "M001-ROADMAP.md"),
|
|
@@ -55,27 +63,41 @@ function setupProjectWithSkill(): string {
|
|
|
55
63
|
].join("\n"),
|
|
56
64
|
"utf-8",
|
|
57
65
|
);
|
|
66
|
+
writeFileSync(
|
|
67
|
+
join(sliceOneDir, "S01-PLAN.md"),
|
|
68
|
+
[
|
|
69
|
+
"# S01: Alpha",
|
|
70
|
+
"",
|
|
71
|
+
"**Goal:** Verify worker x skill prompt plumbing.",
|
|
72
|
+
"**Demo:** Rendered prompts include the skill activation block.",
|
|
73
|
+
"",
|
|
74
|
+
"## Tasks",
|
|
75
|
+
"- [x] **T01: Task** `est:10m`",
|
|
76
|
+
"",
|
|
77
|
+
].join("\n"),
|
|
78
|
+
"utf-8",
|
|
79
|
+
);
|
|
58
80
|
|
|
59
81
|
// Project preferences — buildSkillActivationBlock picks these up via
|
|
60
82
|
// loadEffectiveGSDPreferences(), which reads from `${cwd}/.gsd/PREFERENCES.md`.
|
|
61
83
|
writeFileSync(
|
|
62
84
|
join(base, ".gsd", "PREFERENCES.md"),
|
|
63
|
-
["---", `always_use_skills:`, ` - ${
|
|
85
|
+
["---", ...(options.preferencesLines ?? [`always_use_skills:`, ` - ${skillName}`]), "---", ""].join("\n"),
|
|
64
86
|
"utf-8",
|
|
65
87
|
);
|
|
66
88
|
|
|
67
89
|
// Project-scoped skill — resolveSkillReference scans `${cwd}/.agents/skills/`.
|
|
68
|
-
const skillDir = join(base, ".agents", "skills",
|
|
90
|
+
const skillDir = join(base, ".agents", "skills", skillName);
|
|
69
91
|
mkdirSync(skillDir, { recursive: true });
|
|
70
92
|
writeFileSync(
|
|
71
93
|
join(skillDir, "SKILL.md"),
|
|
72
94
|
[
|
|
73
95
|
"---",
|
|
74
|
-
`name: ${
|
|
96
|
+
`name: ${skillName}`,
|
|
75
97
|
`description: Integration-test skill for worker × skill prompt plumbing.`,
|
|
76
98
|
"---",
|
|
77
99
|
"",
|
|
78
|
-
`# ${
|
|
100
|
+
`# ${skillName}`,
|
|
79
101
|
"",
|
|
80
102
|
"Test skill body.",
|
|
81
103
|
].join("\n"),
|
|
@@ -122,6 +144,31 @@ test("worker prompt (buildResearchSlicePrompt) includes <skill_activation> from
|
|
|
122
144
|
);
|
|
123
145
|
});
|
|
124
146
|
|
|
147
|
+
test("complete-slice prompt includes <skill_activation> from unit-specific skill_rules", async () => {
|
|
148
|
+
const base = setupProjectWithSkill({
|
|
149
|
+
skillName: COMPLETE_SLICE_SKILL_NAME,
|
|
150
|
+
preferencesLines: [
|
|
151
|
+
"skill_rules:",
|
|
152
|
+
" - when: complete-slice",
|
|
153
|
+
" use:",
|
|
154
|
+
` - ${COMPLETE_SLICE_SKILL_NAME}`,
|
|
155
|
+
],
|
|
156
|
+
});
|
|
157
|
+
savedCwd = process.cwd();
|
|
158
|
+
process.chdir(base);
|
|
159
|
+
|
|
160
|
+
const prompt = await buildCompleteSlicePrompt("M001", "Test Milestone", "S01", "Alpha", base, "minimal");
|
|
161
|
+
|
|
162
|
+
assert.ok(
|
|
163
|
+
prompt.includes("<skill_activation>"),
|
|
164
|
+
"complete-slice prompt should contain a <skill_activation> block",
|
|
165
|
+
);
|
|
166
|
+
assert.ok(
|
|
167
|
+
prompt.includes(COMPLETE_SLICE_SKILL_ACTIVATION_SUBSTRING),
|
|
168
|
+
`complete-slice prompt should reference the skill-rule skill '${COMPLETE_SLICE_SKILL_NAME}'`,
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
|
|
125
172
|
test("subagent dispatch prompt (buildParallelResearchSlicesPrompt) carries <skill_activation> into each embedded per-slice section", async () => {
|
|
126
173
|
const base = setupProjectWithSkill();
|
|
127
174
|
savedCwd = process.cwd();
|
|
@@ -83,6 +83,15 @@ test("plan-slice prompt: compact planning gates survive template substitution",
|
|
|
83
83
|
assert.ok(!result.includes("{{"));
|
|
84
84
|
});
|
|
85
85
|
|
|
86
|
+
test("plan-slice prompt: absence checks use negated quiet searches", () => {
|
|
87
|
+
const result = loadPrompt("plan-slice", { ...BASE_VARS, commitInstruction: "Do not commit." });
|
|
88
|
+
assert.ok(result.includes("For absence checks"));
|
|
89
|
+
assert.ok(result.includes("`! grep -q 'pattern' file`"));
|
|
90
|
+
assert.ok(result.includes("`! rg -q 'pattern' file`"));
|
|
91
|
+
assert.ok(result.includes("do not use `grep -c` or `rg -c`"));
|
|
92
|
+
assert.ok(result.includes("count commands exit 1 when they find zero matches"));
|
|
93
|
+
});
|
|
94
|
+
|
|
86
95
|
test("plan-slice prompt: footer references gsd_plan_slice tool, not direct write", () => {
|
|
87
96
|
const result = loadPrompt("plan-slice", { ...BASE_VARS, commitInstruction: "Do not commit." });
|
|
88
97
|
assert.ok(
|
|
@@ -6,7 +6,7 @@ import { mkdtempSync, mkdirSync, rmSync, readFileSync, existsSync, writeFileSync
|
|
|
6
6
|
import { join } from 'node:path';
|
|
7
7
|
import { tmpdir } from 'node:os';
|
|
8
8
|
|
|
9
|
-
import { openDatabase, closeDatabase, insertMilestone, insertSlice, getSlice, getSliceTasks, getTask, getGateResults, updateTaskStatus } from '../gsd-db.ts';
|
|
9
|
+
import { openDatabase, closeDatabase, insertMilestone, insertSlice, insertTask, getSlice, getSliceTasks, getTask, getGateResults, updateTaskStatus } from '../gsd-db.ts';
|
|
10
10
|
import { handlePlanSlice } from '../tools/plan-slice.ts';
|
|
11
11
|
import { parsePlan } from '../parsers-legacy.ts';
|
|
12
12
|
import { parseTaskPlanFile } from '../files.ts';
|
|
@@ -265,6 +265,44 @@ test('handlePlanSlice renders plan artifacts under worktree-local .gsd while usi
|
|
|
265
265
|
}
|
|
266
266
|
});
|
|
267
267
|
|
|
268
|
+
test('handlePlanSlice preserves completed task closeout state when replanning the same task', async () => {
|
|
269
|
+
const base = makeTmpBase();
|
|
270
|
+
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
271
|
+
|
|
272
|
+
try {
|
|
273
|
+
seedParentSlice();
|
|
274
|
+
insertTask({
|
|
275
|
+
id: 'T01',
|
|
276
|
+
sliceId: 'S02',
|
|
277
|
+
milestoneId: 'M001',
|
|
278
|
+
title: 'Completed implementation',
|
|
279
|
+
status: 'complete',
|
|
280
|
+
oneLiner: 'Completed implementation',
|
|
281
|
+
narrative: 'Already finished.',
|
|
282
|
+
verificationResult: 'passed',
|
|
283
|
+
fullSummaryMd: '# T01 Summary\n\nAlready finished.\n',
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
const before = getTask('M001', 'S02', 'T01');
|
|
287
|
+
assert.equal(before?.status, 'complete');
|
|
288
|
+
assert.ok(before?.completed_at, 'completed task should have a completion timestamp');
|
|
289
|
+
assert.equal(before?.full_summary_md, '# T01 Summary\n\nAlready finished.\n');
|
|
290
|
+
|
|
291
|
+
const result = await handlePlanSlice(validParams(), base);
|
|
292
|
+
assert.ok(!('error' in result), `unexpected error: ${'error' in result ? result.error : ''}`);
|
|
293
|
+
|
|
294
|
+
const after = getTask('M001', 'S02', 'T01');
|
|
295
|
+
assert.equal(after?.status, 'complete', 'replanning must not reset completed task status to pending');
|
|
296
|
+
assert.equal(after?.completed_at, before?.completed_at, 'replanning must not clear completed_at');
|
|
297
|
+
assert.equal(after?.full_summary_md, before?.full_summary_md, 'replanning must not clear task summary content');
|
|
298
|
+
assert.equal(after?.verification_result, 'passed', 'replanning must not clear closeout verification');
|
|
299
|
+
assert.equal(after?.description, 'Implement the slice planning handler.', 'planning fields should still refresh');
|
|
300
|
+
assert.equal(getTask('M001', 'S02', 'T02')?.status, 'pending', 'new tasks are still inserted as pending');
|
|
301
|
+
} finally {
|
|
302
|
+
cleanup(base);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
|
|
268
306
|
test('handlePlanSlice advances DB-derived state out of planning immediately', async () => {
|
|
269
307
|
const base = makeTmpBase();
|
|
270
308
|
openDatabase(join(base, '.gsd', 'gsd.db'));
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
resetHookState,
|
|
12
12
|
isRetryPending,
|
|
13
13
|
consumeRetryTrigger,
|
|
14
|
+
consumeGateBlock,
|
|
14
15
|
resolveHookArtifactPath,
|
|
15
16
|
runPreDispatchHooks,
|
|
16
17
|
persistHookState,
|
|
@@ -20,6 +21,7 @@ import {
|
|
|
20
21
|
formatHookStatus,
|
|
21
22
|
triggerHookManually,
|
|
22
23
|
} from "../post-unit-hooks.ts";
|
|
24
|
+
import { invalidateAllCaches } from "../cache.ts";
|
|
23
25
|
|
|
24
26
|
// ─── Fixture Helpers ───────────────────────────────────────────────────────
|
|
25
27
|
|
|
@@ -29,6 +31,11 @@ function createFixtureBase(): string {
|
|
|
29
31
|
return base;
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
function writeHookPreferences(base: string, hookYaml: string): void {
|
|
35
|
+
writeFileSync(join(base, ".gsd", "PREFERENCES.md"), `---\npost_unit_hooks:\n${hookYaml}\n---\n`, "utf-8");
|
|
36
|
+
invalidateAllCaches();
|
|
37
|
+
}
|
|
38
|
+
|
|
32
39
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
33
40
|
// Phase 1: Post-Unit Hook Tests
|
|
34
41
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -104,6 +111,156 @@ test('consumeRetryTrigger clears state', () => {
|
|
|
104
111
|
assert.ok(!isRetryPending(), "no retry initially");
|
|
105
112
|
});
|
|
106
113
|
|
|
114
|
+
test('Advisory hook keeps artifact idempotency without verdict frontmatter', () => {
|
|
115
|
+
resetHookState();
|
|
116
|
+
const base = createFixtureBase();
|
|
117
|
+
try {
|
|
118
|
+
writeHookPreferences(base, ` - name: docs-hint
|
|
119
|
+
after:
|
|
120
|
+
- execute-task
|
|
121
|
+
prompt: Review docs
|
|
122
|
+
artifact: DOCS-HINT.md
|
|
123
|
+
`);
|
|
124
|
+
writeFileSync(resolveHookArtifactPath(base, "M001/S01/T01", "DOCS-HINT.md"), "plain advisory note", "utf-8");
|
|
125
|
+
|
|
126
|
+
const result = checkPostUnitHooks("execute-task", "M001/S01/T01", base);
|
|
127
|
+
assert.deepStrictEqual(result, null, "existing advisory artifact remains idempotent");
|
|
128
|
+
assert.deepStrictEqual(consumeGateBlock(), null, "advisory hook does not create gate block");
|
|
129
|
+
} finally {
|
|
130
|
+
resetHookState();
|
|
131
|
+
invalidateAllCaches();
|
|
132
|
+
rmSync(base, { recursive: true, force: true });
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('Blocking hook skips only after passing frontmatter verdict', () => {
|
|
137
|
+
resetHookState();
|
|
138
|
+
const base = createFixtureBase();
|
|
139
|
+
try {
|
|
140
|
+
writeHookPreferences(base, ` - name: security-review
|
|
141
|
+
after:
|
|
142
|
+
- execute-task
|
|
143
|
+
prompt: Review security
|
|
144
|
+
artifact: SECURITY-REVIEW.md
|
|
145
|
+
criticality: blocking
|
|
146
|
+
`);
|
|
147
|
+
writeFileSync(
|
|
148
|
+
resolveHookArtifactPath(base, "M001/S01/T01", "SECURITY-REVIEW.md"),
|
|
149
|
+
"---\nverdict: pass\n---\n\nNo blocking findings.\n",
|
|
150
|
+
"utf-8",
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const result = checkPostUnitHooks("execute-task", "M001/S01/T01", base);
|
|
154
|
+
assert.deepStrictEqual(result, null, "passing gate artifact is idempotent");
|
|
155
|
+
assert.deepStrictEqual(consumeGateBlock(), null, "passing gate does not block");
|
|
156
|
+
} finally {
|
|
157
|
+
resetHookState();
|
|
158
|
+
invalidateAllCaches();
|
|
159
|
+
rmSync(base, { recursive: true, force: true });
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('Blocking hook reruns invalid artifact once then blocks at cycle budget', () => {
|
|
164
|
+
resetHookState();
|
|
165
|
+
const base = createFixtureBase();
|
|
166
|
+
try {
|
|
167
|
+
writeHookPreferences(base, ` - name: security-review
|
|
168
|
+
after:
|
|
169
|
+
- execute-task
|
|
170
|
+
prompt: Review security
|
|
171
|
+
artifact: SECURITY-REVIEW.md
|
|
172
|
+
criticality: blocking
|
|
173
|
+
`);
|
|
174
|
+
writeFileSync(resolveHookArtifactPath(base, "M001/S01/T01", "SECURITY-REVIEW.md"), "partial output", "utf-8");
|
|
175
|
+
|
|
176
|
+
const dispatch = checkPostUnitHooks("execute-task", "M001/S01/T01", base);
|
|
177
|
+
assert.ok(dispatch, "invalid gate artifact dispatches the blocking hook");
|
|
178
|
+
assert.equal(dispatch.unitType, "hook/security-review");
|
|
179
|
+
|
|
180
|
+
const afterHook = checkPostUnitHooks("hook/security-review", "M001/S01/T01", base);
|
|
181
|
+
assert.deepStrictEqual(afterHook, null, "no further hook dispatch after max_cycles=1");
|
|
182
|
+
const block = consumeGateBlock();
|
|
183
|
+
assert.ok(block, "gate block is recorded");
|
|
184
|
+
assert.equal(block.hookName, "security-review");
|
|
185
|
+
assert.match(block.reason, /missing frontmatter verdict/);
|
|
186
|
+
} finally {
|
|
187
|
+
resetHookState();
|
|
188
|
+
invalidateAllCaches();
|
|
189
|
+
rmSync(base, { recursive: true, force: true });
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test('Blocking hook restored from disk does not trust artifact without clean hook completion', () => {
|
|
194
|
+
resetHookState();
|
|
195
|
+
const base = createFixtureBase();
|
|
196
|
+
try {
|
|
197
|
+
writeHookPreferences(base, ` - name: security-review
|
|
198
|
+
after:
|
|
199
|
+
- execute-task
|
|
200
|
+
prompt: Review security
|
|
201
|
+
artifact: SECURITY-REVIEW.md
|
|
202
|
+
criticality: blocking
|
|
203
|
+
max_cycles: 2
|
|
204
|
+
`);
|
|
205
|
+
const firstDispatch = checkPostUnitHooks("execute-task", "M001/S01/T01", base);
|
|
206
|
+
assert.ok(firstDispatch, "gate dispatches first cycle");
|
|
207
|
+
persistHookState(base);
|
|
208
|
+
|
|
209
|
+
writeFileSync(
|
|
210
|
+
resolveHookArtifactPath(base, "M001/S01/T01", "SECURITY-REVIEW.md"),
|
|
211
|
+
"---\noutcome:\n verdict: pass\n---\n",
|
|
212
|
+
"utf-8",
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
resetHookState();
|
|
216
|
+
restoreHookState(base);
|
|
217
|
+
|
|
218
|
+
const resumed = checkPostUnitHooks("execute-task", "M001/S01/T01", base);
|
|
219
|
+
assert.ok(resumed, "persisted active gate reruns when clean hook completion was not observed");
|
|
220
|
+
assert.equal(resumed.unitType, "hook/security-review");
|
|
221
|
+
} finally {
|
|
222
|
+
resetHookState();
|
|
223
|
+
invalidateAllCaches();
|
|
224
|
+
rmSync(base, { recursive: true, force: true });
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test('Blocking hook needs-rework verdict requests trigger unit retry', () => {
|
|
229
|
+
resetHookState();
|
|
230
|
+
const base = createFixtureBase();
|
|
231
|
+
try {
|
|
232
|
+
writeHookPreferences(base, ` - name: review-arbiter
|
|
233
|
+
after:
|
|
234
|
+
- execute-task
|
|
235
|
+
prompt: Review task
|
|
236
|
+
artifact: REVIEW-DEBATE.md
|
|
237
|
+
criticality: blocking
|
|
238
|
+
max_cycles: 2
|
|
239
|
+
on_block:
|
|
240
|
+
action: retry-unit
|
|
241
|
+
`);
|
|
242
|
+
const dispatch = checkPostUnitHooks("execute-task", "M001/S01/T01", base);
|
|
243
|
+
assert.ok(dispatch, "gate dispatches");
|
|
244
|
+
writeFileSync(
|
|
245
|
+
resolveHookArtifactPath(base, "M001/S01/T01", "REVIEW-DEBATE.md"),
|
|
246
|
+
"---\nverdict: needs-rework\n---\n\nRework required.\n",
|
|
247
|
+
"utf-8",
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
const afterHook = checkPostUnitHooks("hook/review-arbiter", "M001/S01/T01", base);
|
|
251
|
+
assert.deepStrictEqual(afterHook, null, "needs-rework routes via retry signal");
|
|
252
|
+
assert.ok(isRetryPending(), "retry is pending");
|
|
253
|
+
assert.deepStrictEqual(consumeRetryTrigger(), {
|
|
254
|
+
unitType: "execute-task",
|
|
255
|
+
unitId: "M001/S01/T01",
|
|
256
|
+
});
|
|
257
|
+
} finally {
|
|
258
|
+
resetHookState();
|
|
259
|
+
invalidateAllCaches();
|
|
260
|
+
rmSync(base, { recursive: true, force: true });
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
|
|
107
264
|
// ─── Variable substitution in prompts ──────────────────────────────────────
|
|
108
265
|
test('Variable substitution', () => {
|
|
109
266
|
const base = "/project";
|
|
@@ -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,7 @@ 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 { RUN_UAT_WORKFLOW_TOOL_NAMES } from "../tool-presentation-plan.ts";
|
|
5
6
|
|
|
6
7
|
const promptsDir = join(process.cwd(), "src/resources/extensions/gsd/prompts");
|
|
7
8
|
const templatesDir = join(process.cwd(), "src/resources/extensions/gsd/templates");
|
|
@@ -25,11 +26,41 @@ test("reactive-execute prompt keeps task summaries with subagents and avoids bat
|
|
|
25
26
|
test("run-uat prompt branches on dynamic UAT mode and supports runtime evidence", () => {
|
|
26
27
|
const prompt = readPrompt("run-uat");
|
|
27
28
|
assert.match(prompt, /\*\*Detected UAT mode:\*\*\s*`\{\{uatType\}\}`/);
|
|
28
|
-
assert.match(prompt, /uatType:\s
|
|
29
|
+
assert.match(prompt, /uatType:\s*"\{\{uatType\}\}"/);
|
|
30
|
+
assert.match(prompt, /gsd_uat_result_save/);
|
|
31
|
+
assert.match(prompt, /presentedTools/);
|
|
32
|
+
assert.match(prompt, /blockedTools/);
|
|
29
33
|
assert.match(prompt, /live-runtime/);
|
|
30
34
|
assert.match(prompt, /browser\/runtime\/network/i);
|
|
31
35
|
assert.match(prompt, /NEEDS-HUMAN/);
|
|
32
36
|
assert.doesNotMatch(prompt, /uatType:\s*artifact-driven/);
|
|
37
|
+
assert.doesNotMatch(prompt, /Call `gsd_summary_save`/);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("run-uat prompt lists canonical gsd_uat_exec intent values", () => {
|
|
41
|
+
const prompt = readPrompt("run-uat");
|
|
42
|
+
assert.match(prompt, /`uat-artifact-check`/);
|
|
43
|
+
assert.match(prompt, /`uat-runtime-check`/);
|
|
44
|
+
assert.match(prompt, /`uat-browser-check`/);
|
|
45
|
+
assert.match(prompt, /`uat-service-start`/);
|
|
46
|
+
assert.match(prompt, /`uat-log-inspection`/);
|
|
47
|
+
assert.match(prompt, /do not use `artifact`, `runtime`, or `human-follow-up` as `intent`/i);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("run-uat prompt gives the complete UAT result-save presentation contract", () => {
|
|
51
|
+
const prompt = readPrompt("run-uat");
|
|
52
|
+
assert.match(prompt, /Call `gsd_uat_result_save` once after all checks are complete/);
|
|
53
|
+
assert.doesNotMatch(prompt, /Call `gsd_summary_save` with `artifact_type: "ASSESSMENT"`/);
|
|
54
|
+
|
|
55
|
+
for (const toolName of RUN_UAT_WORKFLOW_TOOL_NAMES) {
|
|
56
|
+
assert.ok(prompt.includes(`"${toolName}"`), `prompt should include required presented tool ${toolName}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
for (const toolName of ["gsd_exec", "gsd_summary_save", "gsd_save_gate_result"] as const) {
|
|
60
|
+
assert.ok(prompt.includes(`name: "${toolName}"`), `prompt should include blocked tool ${toolName}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
assert.ok(prompt.includes("forbidden during run-uat"), "prompt should explain blocked run-uat tools");
|
|
33
64
|
});
|
|
34
65
|
|
|
35
66
|
test("workflow-start prompt defaults to autonomy instead of per-phase confirmation", () => {
|
|
@@ -465,6 +496,27 @@ test("reactive-execute prompt references tool calls instead of checkbox updates"
|
|
|
465
496
|
assert.match(prompt, /completion tool calls/);
|
|
466
497
|
});
|
|
467
498
|
|
|
499
|
+
test("parallel subagent prompts forbid serialized tasks arrays", () => {
|
|
500
|
+
const expectations = [
|
|
501
|
+
{ name: "reactive-execute", agent: "worker" },
|
|
502
|
+
{ name: "parallel-research-slices", agent: "scout" },
|
|
503
|
+
{ name: "gate-evaluate", agent: "tester" },
|
|
504
|
+
] as const;
|
|
505
|
+
|
|
506
|
+
for (const { name, agent } of expectations) {
|
|
507
|
+
const prompt = readPrompt(name);
|
|
508
|
+
assert.match(prompt, /tasks:\s*\[\.\.\.\]/, `${name} must show the native array placeholder`);
|
|
509
|
+
assert.match(prompt, /native JSON array/i, `${name} must explicitly require a native JSON array`);
|
|
510
|
+
assert.match(prompt, /Do NOT JSON\.stringify/i, `${name} must forbid JSON.stringify on tasks`);
|
|
511
|
+
assert.match(prompt, /must be array/i, `${name} must mention the subagent validation failure`);
|
|
512
|
+
assert.match(
|
|
513
|
+
prompt,
|
|
514
|
+
new RegExp(`tasks:\\s*\\[\\{\\s*agent:\\s*"${agent}"`, "i"),
|
|
515
|
+
`${name} must show the concrete ${agent} task call shape`,
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
|
|
468
520
|
// ─── Project-shape classifier + 3-or-4-options-with-Other-hatch contract ──
|
|
469
521
|
|
|
470
522
|
test("guided-discuss-project classifies project shape and persists the verdict to PROJECT.md", () => {
|