@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
|
@@ -36,7 +36,7 @@ export function detectUnregisteredMilestoneDrift(_state, ctx) {
|
|
|
36
36
|
*/
|
|
37
37
|
export function repairUnregisteredMilestone(record, _ctx) {
|
|
38
38
|
throw new Error(`Milestone ${record.milestoneId} exists only as markdown projection. ` +
|
|
39
|
-
"Runtime reconciliation will not import markdown into the authoritative DB; run `/gsd recover` if this markdown should repopulate the database.");
|
|
39
|
+
"Runtime reconciliation will not import markdown into the authoritative DB; run `/gsd recover --confirm` if this markdown should repopulate the database.");
|
|
40
40
|
}
|
|
41
41
|
export const unregisteredMilestoneHandler = {
|
|
42
42
|
kind: "unregistered-milestone",
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// checkboxes) and the DB slice rows for that milestone, then re-renders the
|
|
5
5
|
// ROADMAP projection from the authoritative DB rows.
|
|
6
6
|
import { existsSync, readFileSync } from "node:fs";
|
|
7
|
-
import { getMilestone, getMilestoneSlices, isDbAvailable, } from "../../gsd-db.js";
|
|
7
|
+
import { getMilestone, getMilestoneSlices, getSliceTasks, isDbAvailable, } from "../../gsd-db.js";
|
|
8
8
|
import { renderRoadmapFromDb } from "../../markdown-renderer.js";
|
|
9
9
|
import { findMilestoneIds } from "../../milestone-ids.js";
|
|
10
10
|
import { parseRoadmap } from "../../parsers-legacy.js";
|
|
@@ -18,6 +18,15 @@ function arraysEqual(a, b) {
|
|
|
18
18
|
return false;
|
|
19
19
|
return true;
|
|
20
20
|
}
|
|
21
|
+
function getSlicesReadyForDivergenceCheck(milestoneId, dbSlices) {
|
|
22
|
+
const ready = new Set();
|
|
23
|
+
for (const slice of dbSlices) {
|
|
24
|
+
if (isClosedStatus(slice.status) || getSliceTasks(milestoneId, slice.id).length > 0) {
|
|
25
|
+
ready.add(slice.id);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return ready;
|
|
29
|
+
}
|
|
21
30
|
function milestoneHasDivergence(basePath, milestoneId) {
|
|
22
31
|
const roadmapPath = resolveMilestoneFile(basePath, milestoneId, "ROADMAP");
|
|
23
32
|
if (!roadmapPath || !existsSync(roadmapPath))
|
|
@@ -31,6 +40,10 @@ function milestoneHasDivergence(basePath, milestoneId) {
|
|
|
31
40
|
}
|
|
32
41
|
const dbSlices = getMilestoneSlices(milestoneId);
|
|
33
42
|
const dbSliceMap = new Map(dbSlices.map((s) => [s.id, s]));
|
|
43
|
+
const readySliceIds = getSlicesReadyForDivergenceCheck(milestoneId, dbSlices);
|
|
44
|
+
if (dbSlices.length > 0 && readySliceIds.size === 0) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
34
47
|
const roadmapSliceIds = new Set();
|
|
35
48
|
for (let i = 0; i < roadmap.slices.length; i++) {
|
|
36
49
|
const roadmapSlice = roadmap.slices[i];
|
|
@@ -39,6 +52,8 @@ function milestoneHasDivergence(basePath, milestoneId) {
|
|
|
39
52
|
const dbSlice = dbSliceMap.get(roadmapSlice.id);
|
|
40
53
|
if (!dbSlice)
|
|
41
54
|
return true; // Roadmap has a slice the DB doesn't.
|
|
55
|
+
if (!readySliceIds.has(dbSlice.id))
|
|
56
|
+
continue;
|
|
42
57
|
if (dbSlice.sequence !== expectedSequence)
|
|
43
58
|
return true;
|
|
44
59
|
if (!arraysEqual(dbSlice.depends, roadmapSlice.depends))
|
|
@@ -47,6 +62,8 @@ function milestoneHasDivergence(basePath, milestoneId) {
|
|
|
47
62
|
return true;
|
|
48
63
|
}
|
|
49
64
|
for (const dbSlice of dbSlices) {
|
|
65
|
+
if (!readySliceIds.has(dbSlice.id))
|
|
66
|
+
continue;
|
|
50
67
|
if (!roadmapSliceIds.has(dbSlice.id))
|
|
51
68
|
return true;
|
|
52
69
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// File Purpose: ADR-017 drift-driven State Reconciliation Module entry point.
|
|
3
3
|
// reconcileBeforeDispatch runs before every Dispatch decision and worker spawn.
|
|
4
4
|
import { deriveState as defaultDeriveState, invalidateStateCache as defaultInvalidate, } from "../state.js";
|
|
5
|
+
import { clearParseCache as defaultClearParseCache } from "../files.js";
|
|
5
6
|
import { ReconciliationFailedError, } from "./errors.js";
|
|
6
7
|
import { DRIFT_REGISTRY } from "./registry.js";
|
|
7
8
|
export { ReconciliationFailedError } from "./errors.js";
|
|
@@ -10,6 +11,7 @@ const MAX_PASSES = 2;
|
|
|
10
11
|
const defaultDeps = {
|
|
11
12
|
invalidateStateCache: defaultInvalidate,
|
|
12
13
|
deriveState: defaultDeriveState,
|
|
14
|
+
clearParseCache: defaultClearParseCache,
|
|
13
15
|
};
|
|
14
16
|
/**
|
|
15
17
|
* Drift-driven pre-dispatch reconciliation per ADR-017.
|
|
@@ -27,6 +29,7 @@ const defaultDeps = {
|
|
|
27
29
|
*/
|
|
28
30
|
export async function reconcileBeforeDispatch(basePath, deps = defaultDeps) {
|
|
29
31
|
const registry = deps.registry ?? DRIFT_REGISTRY;
|
|
32
|
+
const clearParseCache = deps.clearParseCache ?? defaultClearParseCache;
|
|
30
33
|
const repaired = [];
|
|
31
34
|
for (let pass = 0; pass < MAX_PASSES; pass++) {
|
|
32
35
|
deps.invalidateStateCache();
|
|
@@ -67,6 +70,9 @@ export async function reconcileBeforeDispatch(basePath, deps = defaultDeps) {
|
|
|
67
70
|
failures.push({ drift: record, cause });
|
|
68
71
|
}
|
|
69
72
|
}
|
|
73
|
+
if (repairedThisPass) {
|
|
74
|
+
clearParseCache();
|
|
75
|
+
}
|
|
70
76
|
if (blockers.length > 0) {
|
|
71
77
|
let blockerState = stateSnapshot;
|
|
72
78
|
if (repairedThisPass) {
|
|
@@ -35,7 +35,7 @@ function formatNeedsRemediationBlocker(milestoneId) {
|
|
|
35
35
|
return [
|
|
36
36
|
`Milestone ${milestoneId} is blocked because milestone validation returned needs-remediation, but all slices are complete.`,
|
|
37
37
|
`Fix options:`,
|
|
38
|
-
`1.
|
|
38
|
+
`1. Run \`/gsd dispatch reassess\` to add remediation slices, then run \`/gsd auto\``,
|
|
39
39
|
`2. If the finding is acceptable, override it: \`/gsd verdict pass --rationale "why this is okay"\``,
|
|
40
40
|
`3. If this should wait, defer it explicitly: \`/gsd park ${milestoneId}\``,
|
|
41
41
|
].join("\n");
|
|
@@ -204,10 +204,16 @@ export function invalidateStateCache() {
|
|
|
204
204
|
/**
|
|
205
205
|
* Returns the ID of the first incomplete milestone, or null if all are complete.
|
|
206
206
|
*/
|
|
207
|
+
function getRequestedMilestoneLock() {
|
|
208
|
+
const lock = process.env.GSD_MILESTONE_LOCK?.trim();
|
|
209
|
+
return lock || undefined;
|
|
210
|
+
}
|
|
207
211
|
export async function getActiveMilestoneId(basePath) {
|
|
208
|
-
//
|
|
209
|
-
//
|
|
210
|
-
|
|
212
|
+
// Milestone-scoped execution. Parallel workers and explicit solo commands
|
|
213
|
+
// such as `/gsd auto M002` both set GSD_MILESTONE_LOCK; state derivation must
|
|
214
|
+
// honor it so recovery/adoption sees the requested milestone, not the first
|
|
215
|
+
// open milestone in queue order.
|
|
216
|
+
const milestoneLock = getRequestedMilestoneLock();
|
|
211
217
|
if (milestoneLock) {
|
|
212
218
|
if (isDbAvailable()) {
|
|
213
219
|
const locked = getAllMilestones().find(m => m.id === milestoneLock);
|
|
@@ -573,7 +579,7 @@ function checkReplanTrigger(basePath, milestoneId, sliceId) {
|
|
|
573
579
|
export async function deriveStateFromDb(basePath, artifactReadRoot = basePath) {
|
|
574
580
|
const requirements = getRequirementCounts();
|
|
575
581
|
const allMilestones = getAllMilestones();
|
|
576
|
-
const milestoneLock =
|
|
582
|
+
const milestoneLock = getRequestedMilestoneLock();
|
|
577
583
|
const milestones = milestoneLock
|
|
578
584
|
? allMilestones.filter(m => m.id === milestoneLock)
|
|
579
585
|
: allMilestones;
|
|
@@ -718,8 +724,8 @@ export async function deriveStateFromDb(basePath, artifactReadRoot = basePath) {
|
|
|
718
724
|
}
|
|
719
725
|
}
|
|
720
726
|
// ADR-011 Phase 2: pause-on-escalation takes precedence over dispatching the
|
|
721
|
-
// next task. `awaiting_review` tasks (continueWithDefault=true)
|
|
722
|
-
//
|
|
727
|
+
// next task. `awaiting_review` tasks (continueWithDefault=true) still pause
|
|
728
|
+
// here so silence is never treated as consent.
|
|
723
729
|
//
|
|
724
730
|
// We do NOT gate this on `phases.mid_execution_escalation` — creation of
|
|
725
731
|
// new escalations is gated at the write site (tools/complete-task.ts:315),
|
|
@@ -779,13 +785,10 @@ export async function _deriveStateImpl(basePath, opts) {
|
|
|
779
785
|
const diskIds = findMilestoneIds(basePath);
|
|
780
786
|
const customOrder = loadQueueOrder(basePath);
|
|
781
787
|
const milestoneIds = sortByQueueOrder(diskIds, customOrder);
|
|
782
|
-
// ──
|
|
783
|
-
//
|
|
784
|
-
//
|
|
785
|
-
|
|
786
|
-
// don't exist). This gives each worker complete isolation without
|
|
787
|
-
// modifying any other state derivation logic.
|
|
788
|
-
const milestoneLock = process.env.GSD_PARALLEL_WORKER ? process.env.GSD_MILESTONE_LOCK : undefined;
|
|
788
|
+
// ── Milestone-scoped execution ─────────────────────────────────────────
|
|
789
|
+
// Parallel workers and explicit solo recovery both scope auto-mode to one
|
|
790
|
+
// milestone through GSD_MILESTONE_LOCK.
|
|
791
|
+
const milestoneLock = getRequestedMilestoneLock();
|
|
789
792
|
if (milestoneLock && milestoneIds.includes(milestoneLock)) {
|
|
790
793
|
milestoneIds.length = 0;
|
|
791
794
|
milestoneIds.push(milestoneLock);
|
|
@@ -132,14 +132,16 @@
|
|
|
132
132
|
Verify field rules:
|
|
133
133
|
- MUST be a mechanically executable command: `npm test`, `grep -q "pattern" file`, `test -f path`
|
|
134
134
|
- MUST NOT use shell pipes, redirects, semicolons, backticks, command substitution, output trimming, or grep regex alternation with `|`
|
|
135
|
+
- For absence checks, use `! grep -q "pattern" file` or `! rg -q "pattern" file`; do not use `grep -c` or `rg -c` to assert zero matches because count commands exit 1 when they find zero matches
|
|
135
136
|
- MUST NOT use inline `node -e` assertions for verification; put assertions in a real test file and run it with `node --test` or a package test script
|
|
136
137
|
- For content/document tasks: verify file existence, section count, YAML validity, or word count
|
|
137
138
|
NOT exact phrasing, specific formulas, or "zero TBD" aspirational criteria
|
|
138
139
|
- If no command can verify the output, write: "Manual review — file exists and is non-empty"
|
|
139
140
|
- BAD: `python3 -m pytest tests/ -q --tb=short 2>&1 | tail -5`
|
|
141
|
+
- BAD: `grep -c "old_api" src/index.ts`
|
|
140
142
|
- BAD: "Sections 3.1 and 3.2 exist with exact formulas. Zero TBD/TODO."
|
|
141
143
|
- GOOD: `python3 -m pytest tests/ -q --tb=short`
|
|
142
|
-
- GOOD: `node --test tests/verify-doc.test.js`, `grep -q "Required heading" doc.md`, `test -s doc.md`
|
|
144
|
+
- GOOD: `node --test tests/verify-doc.test.js`, `grep -q "Required heading" doc.md`, `! grep -q "old_api" src/index.ts`, `test -s doc.md`
|
|
143
145
|
|
|
144
146
|
Integration closure rule:
|
|
145
147
|
- At least one slice in any multi-boundary milestone should perform real composition/wiring, not just contract hardening
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Resolve phase-aware tool surfaces for GSD model presentations.
|
|
3
|
+
export const RUN_UAT_WORKFLOW_TOOL_NAMES = [
|
|
4
|
+
"gsd_uat_exec",
|
|
5
|
+
"gsd_uat_result_save",
|
|
6
|
+
"gsd_resume",
|
|
7
|
+
"gsd_milestone_status",
|
|
8
|
+
"gsd_journal_query",
|
|
9
|
+
];
|
|
10
|
+
export const RUN_UAT_FORBIDDEN_TOOL_NAMES = [
|
|
11
|
+
"edit",
|
|
12
|
+
"write",
|
|
13
|
+
"gsd_exec",
|
|
14
|
+
"gsd_summary_save",
|
|
15
|
+
"gsd_save_gate_result",
|
|
16
|
+
"search-the-web",
|
|
17
|
+
"WebSearch",
|
|
18
|
+
"Bash",
|
|
19
|
+
"Write",
|
|
20
|
+
"Edit",
|
|
21
|
+
"mcp__gsd-workflow__*",
|
|
22
|
+
];
|
|
23
|
+
export const RUN_UAT_CLAUDE_NATIVE_TOOL_NAMES = [
|
|
24
|
+
"Read",
|
|
25
|
+
"Glob",
|
|
26
|
+
"Grep",
|
|
27
|
+
];
|
|
28
|
+
const WORKFLOW_ALIAS_TO_CANONICAL = {
|
|
29
|
+
gsd_save_decision: "gsd_decision_save",
|
|
30
|
+
gsd_update_requirement: "gsd_requirement_update",
|
|
31
|
+
gsd_save_requirement: "gsd_requirement_save",
|
|
32
|
+
gsd_save_summary: "gsd_summary_save",
|
|
33
|
+
gsd_generate_milestone_id: "gsd_milestone_generate_id",
|
|
34
|
+
gsd_milestone_plan: "gsd_plan_milestone",
|
|
35
|
+
gsd_slice_plan: "gsd_plan_slice",
|
|
36
|
+
gsd_task_plan: "gsd_plan_task",
|
|
37
|
+
gsd_slice_replan: "gsd_replan_slice",
|
|
38
|
+
gsd_complete_slice: "gsd_slice_complete",
|
|
39
|
+
gsd_milestone_complete: "gsd_complete_milestone",
|
|
40
|
+
gsd_milestone_validate: "gsd_validate_milestone",
|
|
41
|
+
gsd_roadmap_reassess: "gsd_reassess_roadmap",
|
|
42
|
+
gsd_complete_task: "gsd_task_complete",
|
|
43
|
+
gsd_reopen_task: "gsd_task_reopen",
|
|
44
|
+
gsd_reopen_slice: "gsd_slice_reopen",
|
|
45
|
+
gsd_reopen_milestone: "gsd_milestone_reopen",
|
|
46
|
+
};
|
|
47
|
+
export function canonicalWorkflowToolName(toolName) {
|
|
48
|
+
const mcp = parseMcpToolName(toolName);
|
|
49
|
+
const baseName = mcp?.tool ?? toolName;
|
|
50
|
+
return WORKFLOW_ALIAS_TO_CANONICAL[baseName] ?? baseName;
|
|
51
|
+
}
|
|
52
|
+
export function parseMcpToolName(toolName) {
|
|
53
|
+
if (!toolName.startsWith("mcp__"))
|
|
54
|
+
return null;
|
|
55
|
+
const toolSeparator = toolName.indexOf("__", "mcp__".length);
|
|
56
|
+
if (toolSeparator < 0)
|
|
57
|
+
return null;
|
|
58
|
+
return {
|
|
59
|
+
server: toolName.slice("mcp__".length, toolSeparator),
|
|
60
|
+
tool: toolName.slice(toolSeparator + 2),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export function toWorkflowMcpToolName(serverName, toolName) {
|
|
64
|
+
return `mcp__${serverName}__${canonicalWorkflowToolName(toolName)}`;
|
|
65
|
+
}
|
|
66
|
+
function dedupe(values) {
|
|
67
|
+
return [...new Set(values)];
|
|
68
|
+
}
|
|
69
|
+
function addBlockedTool(blocked, name, reason) {
|
|
70
|
+
if (!blocked.some((entry) => entry.name === name)) {
|
|
71
|
+
blocked.push({ name, reason });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export function buildRunUatCanonicalToolNames(options = {}) {
|
|
75
|
+
return dedupe([
|
|
76
|
+
...RUN_UAT_WORKFLOW_TOOL_NAMES,
|
|
77
|
+
...(options.includeBrowserTools ?? []),
|
|
78
|
+
]);
|
|
79
|
+
}
|
|
80
|
+
export function resolveToolPresentationPlan(options) {
|
|
81
|
+
const requested = options.requestedToolNames ?? (options.phase === "run-uat"
|
|
82
|
+
? buildRunUatCanonicalToolNames({ includeBrowserTools: options.includeBrowserTools })
|
|
83
|
+
: []);
|
|
84
|
+
const available = new Set(options.availableToolNames ?? requested);
|
|
85
|
+
const aliases = [];
|
|
86
|
+
const blockedToolNames = [];
|
|
87
|
+
const allowed = [];
|
|
88
|
+
for (const name of requested) {
|
|
89
|
+
const canonical = canonicalWorkflowToolName(name);
|
|
90
|
+
if (canonical !== name)
|
|
91
|
+
aliases.push({ requested: name, canonical });
|
|
92
|
+
if (!available.has(name) && !available.has(canonical)) {
|
|
93
|
+
addBlockedTool(blockedToolNames, canonical, "not registered or provider-incompatible");
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
allowed.push(canonical);
|
|
97
|
+
}
|
|
98
|
+
const allowedToolNames = dedupe(allowed);
|
|
99
|
+
const workflowServerName = options.workflowMcpServerName || "gsd-workflow";
|
|
100
|
+
const presentedToolNames = options.surface === "claude-code-sdk" || options.surface === "mcp"
|
|
101
|
+
? allowedToolNames.map((name) => name.startsWith("gsd_") || name === "ask_user_questions"
|
|
102
|
+
? toWorkflowMcpToolName(workflowServerName, name)
|
|
103
|
+
: name)
|
|
104
|
+
: allowedToolNames;
|
|
105
|
+
if (options.phase === "run-uat") {
|
|
106
|
+
for (const forbidden of RUN_UAT_FORBIDDEN_TOOL_NAMES) {
|
|
107
|
+
addBlockedTool(blockedToolNames, forbidden, "forbidden during run-uat");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
phase: options.phase,
|
|
112
|
+
surface: options.surface,
|
|
113
|
+
model: options.model,
|
|
114
|
+
allowedToolNames,
|
|
115
|
+
presentedToolNames,
|
|
116
|
+
blockedToolNames,
|
|
117
|
+
aliases,
|
|
118
|
+
diagnostics: [],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
@@ -158,7 +158,7 @@ ${params.narrative}
|
|
|
158
158
|
|
|
159
159
|
## Verification
|
|
160
160
|
|
|
161
|
-
${params.verification}
|
|
161
|
+
${params.verification ?? ""}
|
|
162
162
|
|
|
163
163
|
## Requirements Advanced
|
|
164
164
|
|
|
@@ -351,6 +351,20 @@ export async function handleCompleteSlice(params, basePath) {
|
|
|
351
351
|
if (parsed.length > 0)
|
|
352
352
|
effectiveParams.requirementsInvalidated = parsed;
|
|
353
353
|
}
|
|
354
|
+
if (effectiveParams.verification === undefined) {
|
|
355
|
+
const headingLine = "## Verification\n\n";
|
|
356
|
+
const start = existingSummaryMd.indexOf(headingLine);
|
|
357
|
+
if (start !== -1) {
|
|
358
|
+
const contentStart = start + headingLine.length;
|
|
359
|
+
const nextHeading = existingSummaryMd.indexOf("\n\n## ", contentStart);
|
|
360
|
+
const prior = nextHeading === -1
|
|
361
|
+
? existingSummaryMd.slice(contentStart)
|
|
362
|
+
: existingSummaryMd.slice(contentStart, nextHeading);
|
|
363
|
+
const trimmed = prior.trim();
|
|
364
|
+
if (trimmed)
|
|
365
|
+
effectiveParams.verification = trimmed;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
354
368
|
}
|
|
355
369
|
// Render summary markdown
|
|
356
370
|
const summaryMd = renderSliceSummaryMarkdown(effectiveParams);
|
|
@@ -314,9 +314,18 @@ export async function handleCompleteTask(params, basePath) {
|
|
|
314
314
|
// overwrite it; gate rows are UPSERT-keyed per task and will also be
|
|
315
315
|
// overwritten. This restores the invariant that deriveState() sees a
|
|
316
316
|
// consistent "task not done" view so the loop re-dispatches the task.
|
|
317
|
+
let escalationMetadata;
|
|
317
318
|
if (validatedEscalationArtifact) {
|
|
318
319
|
try {
|
|
319
|
-
writeEscalationArtifact(artifactBasePath, validatedEscalationArtifact);
|
|
320
|
+
const escalationPath = writeEscalationArtifact(artifactBasePath, validatedEscalationArtifact);
|
|
321
|
+
escalationMetadata = {
|
|
322
|
+
artifactPath: escalationPath,
|
|
323
|
+
question: validatedEscalationArtifact.question,
|
|
324
|
+
options: validatedEscalationArtifact.options,
|
|
325
|
+
recommendation: validatedEscalationArtifact.recommendation,
|
|
326
|
+
recommendationRationale: validatedEscalationArtifact.recommendationRationale,
|
|
327
|
+
continueWithDefault: validatedEscalationArtifact.continueWithDefault,
|
|
328
|
+
};
|
|
320
329
|
}
|
|
321
330
|
catch (escalationErr) {
|
|
322
331
|
const msg = `complete-task escalation write failed for ${params.milestoneId}/${params.sliceId}/${params.taskId}: ${escalationErr.message}`;
|
|
@@ -378,6 +387,7 @@ export async function handleCompleteTask(params, basePath) {
|
|
|
378
387
|
sliceId: params.sliceId,
|
|
379
388
|
milestoneId: params.milestoneId,
|
|
380
389
|
summaryPath,
|
|
390
|
+
...(escalationMetadata ? { escalation: escalationMetadata } : {}),
|
|
381
391
|
...(projectionStale ? { stale: true } : {}),
|
|
382
392
|
};
|
|
383
393
|
}
|
|
@@ -5,6 +5,27 @@ import { realpathSync } from "node:fs";
|
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { isContextModeEnabled } from "../preferences-types.js";
|
|
7
7
|
import { contextModeDisabledResult } from "./context-mode-tool-result.js";
|
|
8
|
+
const UAT_EXEC_INTENTS = [
|
|
9
|
+
"uat-artifact-check",
|
|
10
|
+
"uat-runtime-check",
|
|
11
|
+
"uat-browser-check",
|
|
12
|
+
"uat-service-start",
|
|
13
|
+
"uat-log-inspection",
|
|
14
|
+
];
|
|
15
|
+
const UAT_EXEC_INTENT_ALIASES = {
|
|
16
|
+
artifact: "uat-artifact-check",
|
|
17
|
+
"artifact-driven": "uat-artifact-check",
|
|
18
|
+
runtime: "uat-runtime-check",
|
|
19
|
+
"runtime-executable": "uat-runtime-check",
|
|
20
|
+
"live-runtime": "uat-runtime-check",
|
|
21
|
+
browser: "uat-browser-check",
|
|
22
|
+
"browser-executable": "uat-browser-check",
|
|
23
|
+
service: "uat-service-start",
|
|
24
|
+
"service-start": "uat-service-start",
|
|
25
|
+
log: "uat-log-inspection",
|
|
26
|
+
logs: "uat-log-inspection",
|
|
27
|
+
"log-inspection": "uat-log-inspection",
|
|
28
|
+
};
|
|
8
29
|
export function buildExecOptions(baseDir, cfg, extras) {
|
|
9
30
|
const allowlist = Array.isArray(cfg?.exec_env_allowlist) ? cfg.exec_env_allowlist : EXEC_DEFAULTS.envAllowlist;
|
|
10
31
|
const stdoutCap = clampNumber(cfg?.exec_stdout_cap_bytes, EXEC_DEFAULTS.stdoutCapBytes, 4_096, 16_777_216);
|
|
@@ -73,6 +94,39 @@ function normalizeScript(params) {
|
|
|
73
94
|
}
|
|
74
95
|
return paramError("script is required and must be a non-empty string");
|
|
75
96
|
}
|
|
97
|
+
function normalizeRequiredString(value, field) {
|
|
98
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
99
|
+
return paramError(`${field} is required and must be a non-empty string`);
|
|
100
|
+
}
|
|
101
|
+
return value.trim();
|
|
102
|
+
}
|
|
103
|
+
function normalizeUatIntent(value) {
|
|
104
|
+
if (typeof value !== "string") {
|
|
105
|
+
return paramError(`intent is required and must be one of: ${UAT_EXEC_INTENTS.join(", ")}`);
|
|
106
|
+
}
|
|
107
|
+
const normalized = value.trim().toLowerCase();
|
|
108
|
+
if (UAT_EXEC_INTENTS.includes(normalized))
|
|
109
|
+
return normalized;
|
|
110
|
+
const alias = UAT_EXEC_INTENT_ALIASES[normalized];
|
|
111
|
+
if (alias)
|
|
112
|
+
return alias;
|
|
113
|
+
return paramError(`invalid intent "${value}" — must be one of: ${UAT_EXEC_INTENTS.join(", ")}`);
|
|
114
|
+
}
|
|
115
|
+
function rejectUatScript(script) {
|
|
116
|
+
const patterns = [
|
|
117
|
+
{ re: /\b(?:npm|pnpm|yarn|bun)\s+(?:i|install|add|remove|update|upgrade)\b/i, reason: "package dependency mutation is not allowed during UAT" },
|
|
118
|
+
{ re: /\b(?:pip|pip3|python\s+-m\s+pip)\s+install\b/i, reason: "package dependency mutation is not allowed during UAT" },
|
|
119
|
+
{ re: /\bgit\s+(?:add|commit|push|reset|checkout|switch|merge|rebase|clean|rm|mv|tag|branch)\b/i, reason: "git mutations are not allowed during UAT" },
|
|
120
|
+
{ re: /\brm\s+-[^\n\r;|&]*r[^\n\r;|&]*f\b/i, reason: "destructive filesystem cleanup is not allowed during UAT" },
|
|
121
|
+
{ re: /\b(?:env|printenv)\b(?:\s|$)/i, reason: "dumping environment variables is not allowed during UAT" },
|
|
122
|
+
{ re: /\bcat\s+\.env(?:\b|\.|$)/i, reason: "reading credential files is not allowed during UAT" },
|
|
123
|
+
];
|
|
124
|
+
for (const pattern of patterns) {
|
|
125
|
+
if (pattern.re.test(script))
|
|
126
|
+
return pattern.reason;
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
76
130
|
function isToolExecutionResult(value) {
|
|
77
131
|
return typeof value === "object" && value !== null && Array.isArray(value.content);
|
|
78
132
|
}
|
|
@@ -207,6 +261,7 @@ export async function executeGsdExec(params, deps) {
|
|
|
207
261
|
runtime,
|
|
208
262
|
script,
|
|
209
263
|
...(typeof params.purpose === "string" ? { purpose: params.purpose } : {}),
|
|
264
|
+
...(params.metadata && typeof params.metadata === "object" ? { metadata: params.metadata } : {}),
|
|
210
265
|
...(typeof params.timeout_ms === "number" ? { timeout_ms: params.timeout_ms } : {}),
|
|
211
266
|
}, opts);
|
|
212
267
|
return formatResult(result);
|
|
@@ -220,6 +275,60 @@ export async function executeGsdExec(params, deps) {
|
|
|
220
275
|
};
|
|
221
276
|
}
|
|
222
277
|
}
|
|
278
|
+
export async function executeUatExec(params, deps) {
|
|
279
|
+
const milestoneId = normalizeRequiredString(params.milestoneId, "milestoneId");
|
|
280
|
+
if (isToolExecutionResult(milestoneId))
|
|
281
|
+
return milestoneId;
|
|
282
|
+
const sliceId = normalizeRequiredString(params.sliceId, "sliceId");
|
|
283
|
+
if (isToolExecutionResult(sliceId))
|
|
284
|
+
return sliceId;
|
|
285
|
+
const checkId = normalizeRequiredString(params.checkId, "checkId");
|
|
286
|
+
if (isToolExecutionResult(checkId))
|
|
287
|
+
return checkId;
|
|
288
|
+
const intent = normalizeUatIntent(params.intent);
|
|
289
|
+
if (isToolExecutionResult(intent))
|
|
290
|
+
return intent;
|
|
291
|
+
const script = normalizeScript(params);
|
|
292
|
+
if (isToolExecutionResult(script))
|
|
293
|
+
return script;
|
|
294
|
+
const rejected = rejectUatScript(script);
|
|
295
|
+
if (rejected) {
|
|
296
|
+
return {
|
|
297
|
+
content: [{ type: "text", text: `Error: gsd_uat_exec blocked command — ${rejected}` }],
|
|
298
|
+
details: { operation: "gsd_uat_exec", error: "uat_exec_policy_block", reason: rejected },
|
|
299
|
+
isError: true,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
const result = await executeGsdExec({
|
|
303
|
+
...params,
|
|
304
|
+
script,
|
|
305
|
+
purpose: typeof params.purpose === "string" && params.purpose.trim().length > 0
|
|
306
|
+
? params.purpose
|
|
307
|
+
: `UAT ${milestoneId}/${sliceId}/${checkId} (${intent})`,
|
|
308
|
+
metadata: {
|
|
309
|
+
kind: "uat_exec",
|
|
310
|
+
milestoneId,
|
|
311
|
+
sliceId,
|
|
312
|
+
checkId,
|
|
313
|
+
intent,
|
|
314
|
+
...(typeof params.expected === "string" && params.expected.trim().length > 0
|
|
315
|
+
? { expected: params.expected.trim() }
|
|
316
|
+
: {}),
|
|
317
|
+
},
|
|
318
|
+
}, deps);
|
|
319
|
+
const details = result.details ?? {};
|
|
320
|
+
return {
|
|
321
|
+
...result,
|
|
322
|
+
details: {
|
|
323
|
+
...details,
|
|
324
|
+
operation: "gsd_uat_exec",
|
|
325
|
+
milestoneId,
|
|
326
|
+
sliceId,
|
|
327
|
+
checkId,
|
|
328
|
+
intent,
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
}
|
|
223
332
|
function formatResult(result) {
|
|
224
333
|
const headerLines = [
|
|
225
334
|
`gsd_exec[${result.id}] runtime=${result.runtime} exit=${formatExit(result)} duration=${result.duration_ms}ms`,
|
|
@@ -268,15 +268,9 @@ export async function handlePlanSlice(rawParams, basePath) {
|
|
|
268
268
|
for (const taskId of omittedTaskIds) {
|
|
269
269
|
deleteTask(params.milestoneId, params.sliceId, taskId);
|
|
270
270
|
}
|
|
271
|
+
const existingTaskById = new Map(existingTasks.map((task) => [task.id, task]));
|
|
271
272
|
for (const task of params.tasks) {
|
|
272
|
-
|
|
273
|
-
id: task.taskId,
|
|
274
|
-
sliceId: params.sliceId,
|
|
275
|
-
milestoneId: params.milestoneId,
|
|
276
|
-
title: task.title,
|
|
277
|
-
status: "pending",
|
|
278
|
-
});
|
|
279
|
-
upsertTaskPlanning(params.milestoneId, params.sliceId, task.taskId, {
|
|
273
|
+
const planning = {
|
|
280
274
|
title: task.title,
|
|
281
275
|
description: task.description,
|
|
282
276
|
estimate: task.estimate,
|
|
@@ -287,7 +281,18 @@ export async function handlePlanSlice(rawParams, basePath) {
|
|
|
287
281
|
observabilityImpact: task.observabilityImpact ?? "",
|
|
288
282
|
fullPlanMd: task.fullPlanMd,
|
|
289
283
|
targetRepositories: task.targetRepositories ?? params.targetRepositories ?? defaultTargets,
|
|
290
|
-
}
|
|
284
|
+
};
|
|
285
|
+
const existingTask = existingTaskById.get(task.taskId);
|
|
286
|
+
if (!existingTask || !isClosedStatus(existingTask.status)) {
|
|
287
|
+
insertTask({
|
|
288
|
+
id: task.taskId,
|
|
289
|
+
sliceId: params.sliceId,
|
|
290
|
+
milestoneId: params.milestoneId,
|
|
291
|
+
title: task.title,
|
|
292
|
+
status: "pending",
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
upsertTaskPlanning(params.milestoneId, params.sliceId, task.taskId, planning);
|
|
291
296
|
}
|
|
292
297
|
// Seed quality gate rows inside the transaction — all-or-nothing with
|
|
293
298
|
// the plan data so a crash can't leave orphaned gates without tasks.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* artifacts so the DB-filesystem reconciler does not auto-correct
|
|
8
8
|
* entities back to "complete".
|
|
9
9
|
*/
|
|
10
|
-
import { getMilestone, getMilestoneSlices, getSliceTasks,
|
|
10
|
+
import { getMilestone, getMilestoneSlices, getSliceTasks, reopenMilestoneStatus, updateSliceStatus, updateTaskStatus, transaction, } from "../gsd-db.js";
|
|
11
11
|
import { invalidateStateCache } from "../state.js";
|
|
12
12
|
import { isClosedStatus } from "../status-guards.js";
|
|
13
13
|
import { renderAllProjections } from "../workflow-projections.js";
|
|
@@ -37,7 +37,7 @@ export async function handleReopenMilestone(params, basePath) {
|
|
|
37
37
|
guardError = `milestone ${params.milestoneId} is not closed (status: ${milestone.status}) — nothing to reopen`;
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
|
-
|
|
40
|
+
reopenMilestoneStatus(params.milestoneId);
|
|
41
41
|
const slices = getMilestoneSlices(params.milestoneId);
|
|
42
42
|
slicesResetCount = slices.length;
|
|
43
43
|
for (const slice of slices) {
|
|
@@ -44,19 +44,12 @@ function getRequiredVerificationClasses(milestoneId) {
|
|
|
44
44
|
required.push("UAT");
|
|
45
45
|
return required;
|
|
46
46
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
chunks.push(artifact.full_content);
|
|
54
|
-
const assessmentPath = resolveSliceFile(basePath, milestoneId, slice.id, "ASSESSMENT");
|
|
55
|
-
const assessmentContent = assessmentPath ? await loadFile(assessmentPath) : null;
|
|
56
|
-
if (assessmentContent)
|
|
57
|
-
chunks.push(assessmentContent);
|
|
58
|
-
}
|
|
59
|
-
return chunks.join("\n\n");
|
|
47
|
+
function hasRuntimeExecutableUatEvidenceText(text) {
|
|
48
|
+
if (!/\buatType:\s*runtime-executable\b/i.test(text))
|
|
49
|
+
return false;
|
|
50
|
+
if (!/\bverdict:\s*PASS\b/i.test(text))
|
|
51
|
+
return false;
|
|
52
|
+
return /^\|\s*[^|\n]+\s*\|\s*runtime\s*\|\s*PASS\s*\|[^|\n]*\bgsd_uat_exec\b/mi.test(text);
|
|
60
53
|
}
|
|
61
54
|
async function browserEvidenceGateRequiresAttention(params, basePath) {
|
|
62
55
|
if (params.verdict !== "pass")
|
|
@@ -77,7 +70,36 @@ async function browserEvidenceGateRequiresAttention(params, basePath) {
|
|
|
77
70
|
]);
|
|
78
71
|
if (!hasBrowserRequiredText(requirementText))
|
|
79
72
|
return false;
|
|
80
|
-
|
|
73
|
+
// Collect per-slice evidence so the runtime bypass is checked independently
|
|
74
|
+
// for each slice. Concatenating all slices before checking would allow runtime
|
|
75
|
+
// evidence from one slice to cover another slice's browser requirements.
|
|
76
|
+
const sliceEvidencePairs = [];
|
|
77
|
+
for (const slice of slices) {
|
|
78
|
+
const chunks = [];
|
|
79
|
+
const artifactPath = `milestones/${params.milestoneId}/slices/${slice.id}/${slice.id}-ASSESSMENT.md`;
|
|
80
|
+
const artifact = getArtifact(artifactPath);
|
|
81
|
+
if (artifact?.full_content)
|
|
82
|
+
chunks.push(artifact.full_content);
|
|
83
|
+
const assessmentPath = resolveSliceFile(basePath, params.milestoneId, slice.id, "ASSESSMENT");
|
|
84
|
+
const assessmentContent = assessmentPath ? await loadFile(assessmentPath) : null;
|
|
85
|
+
if (assessmentContent)
|
|
86
|
+
chunks.push(assessmentContent);
|
|
87
|
+
sliceEvidencePairs.push({
|
|
88
|
+
sliceRequirementText: compactTextParts([slice.demo, slice.goal, slice.success_criteria]),
|
|
89
|
+
evidenceText: chunks.join("\n\n"),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const persistedEvidence = sliceEvidencePairs.map((s) => s.evidenceText).join("\n\n");
|
|
93
|
+
// Runtime bypass: each slice whose own requirement text has browser-observable
|
|
94
|
+
// criteria must have its own runtime-executable UAT evidence. When no individual
|
|
95
|
+
// slice has slice-level browser requirements (e.g., they come from milestone-level
|
|
96
|
+
// fields only), fall back to checking whether any slice has runtime evidence.
|
|
97
|
+
const browserRequiringSlices = sliceEvidencePairs.filter((s) => hasBrowserRequiredText(s.sliceRequirementText));
|
|
98
|
+
const runtimeBypasses = browserRequiringSlices.length > 0
|
|
99
|
+
? browserRequiringSlices.every((s) => hasRuntimeExecutableUatEvidenceText(s.evidenceText))
|
|
100
|
+
: sliceEvidencePairs.some((s) => hasRuntimeExecutableUatEvidenceText(s.evidenceText));
|
|
101
|
+
if (runtimeBypasses)
|
|
102
|
+
return false;
|
|
81
103
|
const validationEvidence = compactTextParts([
|
|
82
104
|
params.successCriteriaChecklist,
|
|
83
105
|
params.verificationClasses,
|
|
@@ -138,12 +160,20 @@ export async function handleValidateMilestone(params, basePath, opts) {
|
|
|
138
160
|
const requiredClasses = getRequiredVerificationClasses(params.milestoneId);
|
|
139
161
|
if (requiredClasses.length > 0) {
|
|
140
162
|
const verificationClasses = params.verificationClasses ?? "";
|
|
141
|
-
const
|
|
142
|
-
if (
|
|
163
|
+
const missingClasses = requiredClasses.filter((className) => !new RegExp(`\\b${className}\\b`, "i").test(verificationClasses));
|
|
164
|
+
if (missingClasses.length === 1) {
|
|
165
|
+
const missingClass = missingClasses[0];
|
|
143
166
|
return {
|
|
144
167
|
error: `verificationClasses must include canonical row "${missingClass}" because this milestone planned ${missingClass.toLowerCase()} verification`,
|
|
145
168
|
};
|
|
146
169
|
}
|
|
170
|
+
if (missingClasses.length > 1) {
|
|
171
|
+
const quotedClasses = missingClasses.map((className) => `"${className}"`).join(", ");
|
|
172
|
+
const plannedClasses = missingClasses.map((className) => className.toLowerCase()).join(", ");
|
|
173
|
+
return {
|
|
174
|
+
error: `verificationClasses must include canonical rows ${quotedClasses} because this milestone planned ${plannedClasses} verification`,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
147
177
|
}
|
|
148
178
|
const artifactBasePath = resolveCanonicalMilestoneRoot(basePath, params.milestoneId);
|
|
149
179
|
const shouldApplyBrowserEvidenceGate = !opts?.skipBrowserEvidenceGate &&
|