@opengsd/gsd-pi 1.1.1-dev.a5a2de8 → 1.1.1-dev.b2556262
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/headless-recover.js +56 -1
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +18 -2
- package/dist/resources/extensions/browser-tools/engine/selection.js +1 -1
- package/dist/resources/extensions/browser-tools/extension-manifest.json +1 -1
- package/dist/resources/extensions/browser-tools/index.js +68 -24
- package/dist/resources/extensions/browser-tools/state.js +12 -0
- package/dist/resources/extensions/browser-tools/tools/session.js +3 -2
- package/dist/resources/extensions/browser-tools/utils.js +3 -3
- package/dist/resources/extensions/browser-tools/web-app-detect.js +52 -0
- package/dist/resources/extensions/gsd/auto/loop.js +4 -2
- package/dist/resources/extensions/gsd/auto/phases.js +87 -12
- package/dist/resources/extensions/gsd/auto/session.js +22 -1
- package/dist/resources/extensions/gsd/auto/workflow-kernel.js +1 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +81 -13
- package/dist/resources/extensions/gsd/auto-model-selection.js +154 -9
- package/dist/resources/extensions/gsd/auto-post-unit.js +19 -2
- package/dist/resources/extensions/gsd/auto-prompts.js +26 -21
- package/dist/resources/extensions/gsd/auto-recovery.js +4 -2
- package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
- package/dist/resources/extensions/gsd/auto-start.js +1 -1
- package/dist/resources/extensions/gsd/auto-timers.js +24 -10
- package/dist/resources/extensions/gsd/auto.js +40 -15
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +192 -77
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
- package/dist/resources/extensions/gsd/closeout-wizard.js +32 -9
- package/dist/resources/extensions/gsd/commands/handlers/auto.js +10 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -9
- package/dist/resources/extensions/gsd/commands-maintenance.js +93 -15
- package/dist/resources/extensions/gsd/commands-mcp-status.js +1 -1
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +2 -2
- package/dist/resources/extensions/gsd/config-overlay.js +1 -0
- package/dist/resources/extensions/gsd/context-masker.js +129 -5
- package/dist/resources/extensions/gsd/db-writer.js +35 -0
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +50 -1
- package/dist/resources/extensions/gsd/gsd-db.js +480 -172
- package/dist/resources/extensions/gsd/guided-flow.js +4 -1
- package/dist/resources/extensions/gsd/markdown-renderer.js +37 -53
- package/dist/resources/extensions/gsd/md-importer.js +38 -3
- package/dist/resources/extensions/gsd/migration-auto-check.js +126 -31
- package/dist/resources/extensions/gsd/parsers-legacy.js +23 -0
- package/dist/resources/extensions/gsd/planner-handoff.js +98 -0
- package/dist/resources/extensions/gsd/planning-path-scope.js +22 -4
- package/dist/resources/extensions/gsd/pre-execution-checks.js +10 -2
- package/dist/resources/extensions/gsd/preferences-models.js +111 -43
- package/dist/resources/extensions/gsd/preferences-types.js +13 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +68 -3
- package/dist/resources/extensions/gsd/preferences.js +4 -1
- package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/run-uat.md +2 -2
- package/dist/resources/extensions/gsd/prompts/system.md +1 -1
- package/dist/resources/extensions/gsd/roadmap-slices.js +5 -1
- package/dist/resources/extensions/gsd/safety/content-validator.js +6 -4
- package/dist/resources/extensions/gsd/skill-manifest.js +12 -0
- package/dist/resources/extensions/gsd/source-observations.js +306 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +15 -8
- package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +33 -5
- package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-worker.js +34 -13
- package/dist/resources/extensions/gsd/state-reconciliation/index.js +39 -14
- package/dist/resources/extensions/gsd/state-reconciliation/spawn-gate.js +4 -4
- package/dist/resources/extensions/gsd/state.js +7 -3
- package/dist/resources/extensions/gsd/tool-contract.js +15 -1
- package/dist/resources/extensions/gsd/tool-presentation-plan.js +24 -2
- package/dist/resources/extensions/gsd/tools/complete-slice.js +28 -0
- package/dist/resources/extensions/gsd/tools/plan-slice.js +42 -11
- package/dist/resources/extensions/gsd/tools/plan-task.js +7 -1
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +62 -406
- package/dist/resources/extensions/gsd/uat-policy.js +130 -0
- package/dist/resources/extensions/gsd/uat-run.js +414 -0
- package/dist/resources/extensions/gsd/unit-context-manifest.js +3 -4
- package/dist/resources/extensions/gsd/unit-tool-contracts.js +38 -14
- package/dist/resources/extensions/gsd/verdict-parser.js +3 -8
- package/dist/resources/extensions/gsd/workflow-manifest.js +132 -5
- package/dist/resources/extensions/gsd/workflow-mcp.js +2 -3
- package/dist/resources/extensions/gsd/workflow-projections.js +8 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +26 -0
- package/dist/resources/extensions/gsd/worktree-reentry.js +96 -0
- package/dist/resources/extensions/gsd/worktree-state-projection.js +18 -17
- package/dist/resources/extensions/shared/gsd-browser-cli.js +6 -0
- package/dist/resources/extensions/subagent/agents.js +1 -0
- package/dist/resources/extensions/subagent/index.js +27 -12
- package/dist/resources/extensions/subagent/launch.js +7 -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/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/dist/web/standalone/node_modules/@gsd/native/dist/native.js +22 -0
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/package.json +4 -4
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js +21 -23
- package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +3 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +66 -12
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +18 -11
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +16 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/dist/workflow-tools.js +1 -1
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/dist/native.js +22 -0
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/image-models.generated.d.ts +30 -0
- package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/image-models.generated.js +30 -0
- package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +174 -29
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +178 -54
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/transform-messages.js +8 -1
- package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/theme/themes.js +1 -1
- package/packages/pi-coding-agent/dist/theme/themes.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/dist/utils.d.ts +11 -0
- package/packages/pi-tui/dist/utils.d.ts.map +1 -1
- package/packages/pi-tui/dist/utils.js +119 -6
- package/packages/pi-tui/dist/utils.js.map +1 -1
- package/packages/pi-tui/package.json +2 -1
- package/packages/rpc-client/package.json +2 -2
- package/pkg/dist/theme/themes.js +1 -1
- package/pkg/dist/theme/themes.js.map +1 -1
- package/pkg/package.json +1 -1
- package/scripts/install/handoff.js +16 -3
- package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +21 -2
- package/src/resources/extensions/browser-tools/engine/selection.ts +1 -1
- package/src/resources/extensions/browser-tools/extension-manifest.json +1 -1
- package/src/resources/extensions/browser-tools/index.ts +75 -27
- package/src/resources/extensions/browser-tools/state.ts +13 -0
- package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +2 -2
- package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +57 -0
- package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +37 -0
- package/src/resources/extensions/browser-tools/tests/web-app-detect.test.mjs +68 -0
- package/src/resources/extensions/browser-tools/tools/session.ts +4 -2
- package/src/resources/extensions/browser-tools/utils.ts +3 -3
- package/src/resources/extensions/browser-tools/web-app-detect.ts +63 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
- package/src/resources/extensions/gsd/auto/loop.ts +4 -2
- package/src/resources/extensions/gsd/auto/phases.ts +89 -15
- package/src/resources/extensions/gsd/auto/session.ts +24 -1
- package/src/resources/extensions/gsd/auto/workflow-kernel.ts +1 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +117 -12
- package/src/resources/extensions/gsd/auto-model-selection.ts +190 -12
- package/src/resources/extensions/gsd/auto-post-unit.ts +20 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +25 -22
- package/src/resources/extensions/gsd/auto-recovery.ts +22 -3
- package/src/resources/extensions/gsd/auto-runtime-state.ts +5 -0
- package/src/resources/extensions/gsd/auto-start.ts +1 -1
- package/src/resources/extensions/gsd/auto-timers.ts +25 -9
- package/src/resources/extensions/gsd/auto.ts +41 -14
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +250 -78
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
- package/src/resources/extensions/gsd/closeout-wizard.ts +47 -13
- package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -17
- package/src/resources/extensions/gsd/commands-maintenance.ts +124 -13
- package/src/resources/extensions/gsd/commands-mcp-status.ts +1 -1
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +2 -2
- package/src/resources/extensions/gsd/config-overlay.ts +1 -0
- package/src/resources/extensions/gsd/context-masker.ts +152 -5
- package/src/resources/extensions/gsd/db-writer.ts +38 -0
- package/src/resources/extensions/gsd/docs/preferences-reference.md +50 -1
- package/src/resources/extensions/gsd/gsd-db.ts +564 -186
- package/src/resources/extensions/gsd/guided-flow.ts +4 -1
- package/src/resources/extensions/gsd/markdown-renderer.ts +44 -66
- package/src/resources/extensions/gsd/md-importer.ts +49 -2
- package/src/resources/extensions/gsd/migration-auto-check.ts +154 -34
- package/src/resources/extensions/gsd/parsers-legacy.ts +20 -0
- package/src/resources/extensions/gsd/planner-handoff.ts +149 -0
- package/src/resources/extensions/gsd/planning-path-scope.ts +22 -4
- package/src/resources/extensions/gsd/pre-execution-checks.ts +9 -2
- package/src/resources/extensions/gsd/preferences-models.ts +113 -43
- package/src/resources/extensions/gsd/preferences-types.ts +47 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +76 -2
- package/src/resources/extensions/gsd/preferences.ts +5 -0
- package/src/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +2 -2
- package/src/resources/extensions/gsd/prompts/system.md +1 -1
- package/src/resources/extensions/gsd/roadmap-slices.ts +6 -1
- package/src/resources/extensions/gsd/safety/content-validator.ts +8 -5
- package/src/resources/extensions/gsd/skill-manifest.ts +12 -0
- package/src/resources/extensions/gsd/source-observations.ts +402 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +20 -8
- package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +44 -5
- package/src/resources/extensions/gsd/state-reconciliation/drift/stale-worker.ts +39 -11
- package/src/resources/extensions/gsd/state-reconciliation/index.ts +45 -15
- package/src/resources/extensions/gsd/state-reconciliation/spawn-gate.ts +4 -4
- package/src/resources/extensions/gsd/state.ts +7 -4
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +114 -0
- package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +66 -4
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +299 -1
- package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +75 -3
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +22 -1
- package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +4 -0
- package/src/resources/extensions/gsd/tests/before-provider-context-management.test.ts +145 -0
- package/src/resources/extensions/gsd/tests/bundled-skill-triggers.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/closeout-wizard.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/commands-dispatcher-unmerged-milestone.test.ts +26 -1
- package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +118 -0
- package/src/resources/extensions/gsd/tests/content-validator.test.ts +74 -0
- package/src/resources/extensions/gsd/tests/context-masker.test.ts +56 -1
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +17 -2
- package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +24 -0
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +1 -11
- package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +64 -0
- package/src/resources/extensions/gsd/tests/gate-storage.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +62 -1
- package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +4 -1
- package/src/resources/extensions/gsd/tests/interrupted-session-auto.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +99 -2
- package/src/resources/extensions/gsd/tests/plan-task.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/planner-handoff.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +133 -0
- package/src/resources/extensions/gsd/tests/provider-switch-observer.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +101 -1
- package/src/resources/extensions/gsd/tests/repository-registry.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +28 -0
- package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +5 -3
- package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +162 -18
- package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/source-observations.test.ts +275 -0
- package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +43 -0
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +76 -21
- package/src/resources/extensions/gsd/tests/thinking-level-resolution.test.ts +203 -0
- package/src/resources/extensions/gsd/tests/uat-policy.test.ts +170 -0
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/workflow-kernel.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +306 -1
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +77 -10
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +260 -5
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +511 -1
- package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +102 -0
- package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +44 -0
- package/src/resources/extensions/gsd/tool-contract.ts +29 -1
- package/src/resources/extensions/gsd/tool-presentation-plan.ts +41 -6
- package/src/resources/extensions/gsd/tools/complete-slice.ts +29 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +54 -12
- package/src/resources/extensions/gsd/tools/plan-task.ts +8 -1
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +71 -489
- package/src/resources/extensions/gsd/types.ts +1 -0
- package/src/resources/extensions/gsd/uat-policy.ts +191 -0
- package/src/resources/extensions/gsd/uat-run.ts +550 -0
- package/src/resources/extensions/gsd/unit-context-manifest.ts +3 -4
- package/src/resources/extensions/gsd/unit-tool-contracts.ts +38 -14
- package/src/resources/extensions/gsd/verdict-parser.ts +3 -10
- package/src/resources/extensions/gsd/workflow-manifest.ts +193 -7
- package/src/resources/extensions/gsd/workflow-mcp.ts +2 -3
- package/src/resources/extensions/gsd/workflow-projections.ts +9 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +32 -0
- package/src/resources/extensions/gsd/worktree-reentry.ts +103 -0
- package/src/resources/extensions/gsd/worktree-state-projection.ts +22 -22
- package/src/resources/extensions/shared/gsd-browser-cli.ts +6 -0
- package/src/resources/extensions/shared/tests/format-utils.test.ts +8 -3
- package/src/resources/extensions/subagent/agents.ts +4 -0
- package/src/resources/extensions/subagent/index.ts +28 -3
- package/src/resources/extensions/subagent/launch.ts +8 -0
- package/src/resources/extensions/subagent/tests/model-override.test.ts +31 -0
- /package/dist/web/standalone/.next/static/{9y3LeeR2uGr2yRj9RjY3D → tJOKQbQRO-9MiFDO8DIDS}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{9y3LeeR2uGr2yRj9RjY3D → tJOKQbQRO-9MiFDO8DIDS}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Owns the durable UAT run lifecycle behind gsd_uat_result_save.
|
|
3
|
+
|
|
4
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
5
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
hasUatBrowserToolSurface,
|
|
9
|
+
isUatType,
|
|
10
|
+
UAT_MODE_POLICIES,
|
|
11
|
+
UAT_TYPES,
|
|
12
|
+
validateUatModePolicy,
|
|
13
|
+
type UatCheckMode,
|
|
14
|
+
type UatCheckResult,
|
|
15
|
+
type UatType,
|
|
16
|
+
type UatVerdict,
|
|
17
|
+
} from "./uat-policy.js";
|
|
18
|
+
import {
|
|
19
|
+
buildRunUatPresentationForType,
|
|
20
|
+
canonicalWorkflowToolName,
|
|
21
|
+
parseMcpToolName,
|
|
22
|
+
RUN_UAT_FORBIDDEN_TOOL_NAMES,
|
|
23
|
+
RUN_UAT_TOOL_PRESENTATION_PLAN_ID,
|
|
24
|
+
RUN_UAT_WORKFLOW_TOOL_NAMES,
|
|
25
|
+
} from "./tool-presentation-plan.js";
|
|
26
|
+
import { saveFile } from "./files.js";
|
|
27
|
+
import { relSliceFile, resolveGsdPathContract } from "./paths.js";
|
|
28
|
+
import { buildManualValidationGuidance, resolveCanonicalMilestoneRoot } from "./worktree-manager.js";
|
|
29
|
+
|
|
30
|
+
export interface UatEvidenceRef {
|
|
31
|
+
kind: "gsd_uat_exec" | "gsd_exec" | "screenshot" | "log" | "url" | "browser";
|
|
32
|
+
ref: string;
|
|
33
|
+
note?: string;
|
|
34
|
+
unitType?: string;
|
|
35
|
+
tool?: string;
|
|
36
|
+
executionId?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface UatCheckResultInput {
|
|
40
|
+
id: string;
|
|
41
|
+
description: string;
|
|
42
|
+
mode: UatCheckMode;
|
|
43
|
+
result: UatCheckResult;
|
|
44
|
+
evidence?: UatEvidenceRef[];
|
|
45
|
+
notes?: string;
|
|
46
|
+
nonAutomatable?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface UatPresentationInput {
|
|
50
|
+
surface: "provider-tools" | "claude-code-sdk" | "mcp" | "hybrid";
|
|
51
|
+
model?: { provider?: string; api?: string; id?: string };
|
|
52
|
+
presentedTools: string[];
|
|
53
|
+
blockedTools: Array<{ name: string; reason: string }>;
|
|
54
|
+
aliases?: Array<{ requested: string; canonical: string }>;
|
|
55
|
+
fallbackToolsUsed?: string[];
|
|
56
|
+
toolPresentationPlanId?: string;
|
|
57
|
+
notes?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface UatResultSaveParams {
|
|
61
|
+
milestoneId: string;
|
|
62
|
+
sliceId: string;
|
|
63
|
+
uatType: UatType;
|
|
64
|
+
verdict: UatVerdict;
|
|
65
|
+
checks: UatCheckResultInput[];
|
|
66
|
+
presentation: UatPresentationInput;
|
|
67
|
+
notes?: string;
|
|
68
|
+
attempt?: number | string | "auto";
|
|
69
|
+
previousAttemptId?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface PreparedUatRun {
|
|
73
|
+
params: UatResultSaveParams;
|
|
74
|
+
runId: string;
|
|
75
|
+
attempt: number;
|
|
76
|
+
gateVerdict: "pass" | "flag";
|
|
77
|
+
gateOutcome: "pass" | "fail";
|
|
78
|
+
rationale: string;
|
|
79
|
+
assessment: string;
|
|
80
|
+
evaluatedAt: string;
|
|
81
|
+
hasHuman: boolean;
|
|
82
|
+
manualGuidance: string | null;
|
|
83
|
+
worktreeRoot: string;
|
|
84
|
+
browserToolsPresented: boolean;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface UatRunValidationError {
|
|
88
|
+
code: string;
|
|
89
|
+
message: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export type PrepareUatRunResult =
|
|
93
|
+
| { ok: true; run: PreparedUatRun }
|
|
94
|
+
| { ok: false; error: UatRunValidationError };
|
|
95
|
+
|
|
96
|
+
function isNonEmptyString(value: unknown): value is string {
|
|
97
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function mergeBlockedTools(
|
|
101
|
+
current: UatPresentationInput["blockedTools"] | undefined,
|
|
102
|
+
canonical: UatPresentationInput["blockedTools"],
|
|
103
|
+
): UatPresentationInput["blockedTools"] {
|
|
104
|
+
const merged = new Map<string, { name: string; reason: string }>();
|
|
105
|
+
for (const entry of [...(current ?? []), ...canonical]) {
|
|
106
|
+
merged.set(canonicalWorkflowToolName(parseMcpToolName(entry.name)?.tool ?? entry.name), entry);
|
|
107
|
+
}
|
|
108
|
+
return [...merged.values()];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function mergePresentedTools(current: readonly string[] | undefined, canonical: readonly string[]): string[] {
|
|
112
|
+
return [...new Set([...(current ?? []), ...canonical])];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function normalizeUatVerdict(params: UatResultSaveParams): UatResultSaveParams {
|
|
116
|
+
const raw = params as Partial<UatResultSaveParams> & Record<string, unknown>;
|
|
117
|
+
if (typeof raw.verdict === "string") {
|
|
118
|
+
return { ...params, verdict: raw.verdict.toUpperCase() as UatVerdict };
|
|
119
|
+
}
|
|
120
|
+
return params;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function supplyDefaultPresentation(params: UatResultSaveParams): UatResultSaveParams {
|
|
124
|
+
const raw = params as Partial<UatResultSaveParams> & Record<string, unknown>;
|
|
125
|
+
if (!raw.presentation) {
|
|
126
|
+
return { ...params, presentation: buildRunUatPresentationForType(params.uatType) };
|
|
127
|
+
}
|
|
128
|
+
return params;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function mergeCanonicalPresentation(params: UatResultSaveParams): UatResultSaveParams {
|
|
132
|
+
const canonicalPresentation = buildRunUatPresentationForType(params.uatType);
|
|
133
|
+
const providedPresentation = params.presentation as Partial<UatPresentationInput>;
|
|
134
|
+
return {
|
|
135
|
+
...params,
|
|
136
|
+
presentation: {
|
|
137
|
+
...providedPresentation,
|
|
138
|
+
surface: providedPresentation.surface ?? canonicalPresentation.surface,
|
|
139
|
+
presentedTools: mergePresentedTools(providedPresentation.presentedTools, canonicalPresentation.presentedTools),
|
|
140
|
+
blockedTools: mergeBlockedTools(providedPresentation.blockedTools, canonicalPresentation.blockedTools),
|
|
141
|
+
toolPresentationPlanId: RUN_UAT_TOOL_PRESENTATION_PLAN_ID,
|
|
142
|
+
} as UatPresentationInput,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function ensureUatRequiredFields(params: UatResultSaveParams): string | null {
|
|
147
|
+
if (!isNonEmptyString(params.milestoneId)) return "milestoneId is required";
|
|
148
|
+
if (!isNonEmptyString(params.sliceId)) return "sliceId is required";
|
|
149
|
+
if (!isNonEmptyString(params.uatType)) return "uatType is required";
|
|
150
|
+
if (!isUatType(params.uatType)) {
|
|
151
|
+
return `uatType must be one of: ${UAT_TYPES.join(", ")}`;
|
|
152
|
+
}
|
|
153
|
+
if (!["PASS", "FAIL", "PARTIAL"].includes(params.verdict)) return "verdict must be PASS, FAIL, or PARTIAL";
|
|
154
|
+
if (!Array.isArray(params.checks) || params.checks.length === 0) return "checks must contain at least one UAT check";
|
|
155
|
+
if (!params.presentation || !Array.isArray(params.presentation.presentedTools)) return "presentation.presentedTools is required";
|
|
156
|
+
if (!Array.isArray(params.presentation.blockedTools)) return "presentation.blockedTools is required";
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function approvedEvidenceRoots(basePath: string): string[] {
|
|
161
|
+
const contract = resolveGsdPathContract(basePath);
|
|
162
|
+
return [contract.worktreeGsd, contract.projectGsd].filter((root): root is string => typeof root === "string");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function approvedBrowserArtifactRoots(basePath: string): string[] {
|
|
166
|
+
const contract = resolveGsdPathContract(basePath);
|
|
167
|
+
const roots = [contract.workRoot, contract.projectRoot].map((root) => join(root, ".artifacts", "browser"));
|
|
168
|
+
return [...new Set(roots)];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function pathStartsWithin(parent: string, target: string): boolean {
|
|
172
|
+
const normalizedParent = parent.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
173
|
+
const normalizedTarget = target.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
174
|
+
return normalizedTarget === normalizedParent || normalizedTarget.startsWith(`${normalizedParent}/`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function pushUnique(paths: string[], candidate: string): void {
|
|
178
|
+
if (!paths.includes(candidate)) paths.push(candidate);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function execMetaPathCandidates(basePath: string, ref: string): string[] {
|
|
182
|
+
const trimmed = ref.trim();
|
|
183
|
+
const candidates: string[] = [];
|
|
184
|
+
const execDirs = approvedEvidenceRoots(basePath).map((root) => join(root, "exec"));
|
|
185
|
+
const normalizedRef = trimmed.replace(/\\/g, "/");
|
|
186
|
+
const pathLike = normalizedRef.endsWith(".meta.json") || normalizedRef.includes("/.gsd/exec/");
|
|
187
|
+
|
|
188
|
+
if (pathLike) {
|
|
189
|
+
const rawPath = isAbsolute(trimmed) ? resolve(trimmed) : resolve(basePath, trimmed);
|
|
190
|
+
pushUnique(candidates, rawPath);
|
|
191
|
+
|
|
192
|
+
const relativeExecMarker = ".gsd/exec/";
|
|
193
|
+
const markerIndex = normalizedRef.indexOf(relativeExecMarker);
|
|
194
|
+
if (markerIndex >= 0) {
|
|
195
|
+
const execRelative = normalizedRef.slice(markerIndex + relativeExecMarker.length);
|
|
196
|
+
for (const execDir of execDirs) {
|
|
197
|
+
pushUnique(candidates, join(execDir, execRelative));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return candidates.filter((candidate) =>
|
|
202
|
+
execDirs.some((execDir) => pathStartsWithin(execDir, candidate))
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
for (const execDir of execDirs) {
|
|
207
|
+
pushUnique(candidates, join(execDir, `${trimmed}.meta.json`));
|
|
208
|
+
}
|
|
209
|
+
return candidates;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function resolveExecMetaPath(basePath: string, ref: string): string | null {
|
|
213
|
+
for (const candidate of execMetaPathCandidates(basePath, ref)) {
|
|
214
|
+
if (existsSync(candidate)) return candidate;
|
|
215
|
+
}
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function evidencePathIsApproved(basePath: string, ref: string): boolean {
|
|
220
|
+
const normalizedRef = ref.replace(/\\/g, "/");
|
|
221
|
+
if (normalizedRef.startsWith(".gsd/exec/") || normalizedRef.startsWith(".gsd/uat/")) return true;
|
|
222
|
+
if (normalizedRef.startsWith(".artifacts/browser/")) {
|
|
223
|
+
const resolvedRef = resolve(basePath, ref);
|
|
224
|
+
return approvedBrowserArtifactRoots(basePath).some((root) => pathStartsWithin(root, resolvedRef));
|
|
225
|
+
}
|
|
226
|
+
const gsdEvidenceApproved = approvedEvidenceRoots(basePath).some((root) => {
|
|
227
|
+
return pathStartsWithin(join(root, "exec"), ref) || pathStartsWithin(join(root, "uat"), ref);
|
|
228
|
+
});
|
|
229
|
+
if (gsdEvidenceApproved) return true;
|
|
230
|
+
return approvedBrowserArtifactRoots(basePath).some((root) => pathStartsWithin(root, ref));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function validateEvidenceRef(basePath: string, evidence: UatEvidenceRef): string | null {
|
|
234
|
+
if (!isNonEmptyString(evidence.ref)) return "evidence.ref is required";
|
|
235
|
+
if (evidence.kind === "gsd_uat_exec" || evidence.kind === "gsd_exec") {
|
|
236
|
+
const path = resolveExecMetaPath(basePath, evidence.ref.trim());
|
|
237
|
+
if (!path) return `missing gsd_exec metadata for evidence id "${evidence.ref}"`;
|
|
238
|
+
if (evidence.kind === "gsd_uat_exec") {
|
|
239
|
+
try {
|
|
240
|
+
const meta = JSON.parse(readFileSync(path, "utf-8")) as { metadata?: { kind?: unknown } };
|
|
241
|
+
if (meta.metadata?.kind !== "uat_exec") return `evidence id "${evidence.ref}" is not typed as uat_exec`;
|
|
242
|
+
} catch {
|
|
243
|
+
return `invalid gsd_exec metadata JSON for evidence id "${evidence.ref}"`;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
if (evidence.kind === "url") {
|
|
249
|
+
try {
|
|
250
|
+
const parsed = new URL(evidence.ref);
|
|
251
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:"
|
|
252
|
+
? null
|
|
253
|
+
: `invalid URL evidence ref "${evidence.ref}"`;
|
|
254
|
+
} catch {
|
|
255
|
+
return `invalid URL evidence ref "${evidence.ref}"`;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return evidencePathIsApproved(basePath, evidence.ref)
|
|
259
|
+
? null
|
|
260
|
+
: `evidence ref "${evidence.ref}" is outside approved evidence locations`;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function validateUatChecks(basePath: string, params: UatResultSaveParams): string | null {
|
|
264
|
+
for (const check of params.checks) {
|
|
265
|
+
if (!isNonEmptyString(check.id)) return "every check must have a non-empty id";
|
|
266
|
+
if (!isNonEmptyString(check.description)) return `check ${check.id} must have a description`;
|
|
267
|
+
if (!["artifact", "runtime", "browser", "human-follow-up"].includes(check.mode)) {
|
|
268
|
+
return `check ${check.id} has invalid mode "${check.mode}"`;
|
|
269
|
+
}
|
|
270
|
+
if (!["PASS", "FAIL", "NEEDS-HUMAN"].includes(check.result)) {
|
|
271
|
+
return `check ${check.id} has invalid result "${check.result}"`;
|
|
272
|
+
}
|
|
273
|
+
if (check.result === "PASS" || check.result === "FAIL") {
|
|
274
|
+
if (!Array.isArray(check.evidence) || check.evidence.length === 0) {
|
|
275
|
+
return `check ${check.id} is ${check.result} but has no objective evidence`;
|
|
276
|
+
}
|
|
277
|
+
for (const evidence of check.evidence) {
|
|
278
|
+
const error = validateEvidenceRef(basePath, evidence);
|
|
279
|
+
if (error) return `check ${check.id}: ${error}`;
|
|
280
|
+
}
|
|
281
|
+
} else if (!isNonEmptyString(check.notes)) {
|
|
282
|
+
return `check ${check.id} is NEEDS-HUMAN but has no manual instruction or reason`;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function validateFreshUatOwnedEvidence(params: UatResultSaveParams): string | null {
|
|
289
|
+
const hasFreshUatEvidence = params.checks.some((check) =>
|
|
290
|
+
(check.evidence ?? []).some((evidence) => evidence.kind === "gsd_uat_exec")
|
|
291
|
+
);
|
|
292
|
+
return hasFreshUatEvidence
|
|
293
|
+
? null
|
|
294
|
+
: "UAT Assessment requires at least one fresh gsd_uat_exec evidence reference from run-uat";
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function quoteToolNames(toolNames: readonly string[]): string {
|
|
298
|
+
return toolNames.map((toolName) => `"${toolName}"`).join(", ");
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function validateCanonicalPresentation(params: UatResultSaveParams): string | null {
|
|
302
|
+
const aliasHints: Record<string, string> = {
|
|
303
|
+
gsd_save_summary: "gsd_summary_save",
|
|
304
|
+
gsd_complete_task: "gsd_task_complete",
|
|
305
|
+
gsd_complete_slice: "gsd_slice_complete",
|
|
306
|
+
gsd_milestone_complete: "gsd_complete_milestone",
|
|
307
|
+
};
|
|
308
|
+
const errors: string[] = [];
|
|
309
|
+
for (const toolName of params.presentation.presentedTools) {
|
|
310
|
+
const baseName = parseMcpToolName(toolName)?.tool ?? toolName;
|
|
311
|
+
const canonical = aliasHints[baseName];
|
|
312
|
+
if (canonical) errors.push(`presentation tool "${toolName}" uses an alias; use canonical "${canonical}"`);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const presentedCanonical = new Set(
|
|
316
|
+
params.presentation.presentedTools.map((toolName) =>
|
|
317
|
+
canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName)
|
|
318
|
+
),
|
|
319
|
+
);
|
|
320
|
+
const missingRequiredTools = RUN_UAT_WORKFLOW_TOOL_NAMES.filter(
|
|
321
|
+
(requiredTool) => !presentedCanonical.has(requiredTool),
|
|
322
|
+
);
|
|
323
|
+
if (missingRequiredTools.length === 1) {
|
|
324
|
+
errors.push(`presentation is missing required UAT tool "${missingRequiredTools[0]}"`);
|
|
325
|
+
} else if (missingRequiredTools.length > 1) {
|
|
326
|
+
errors.push(`presentation is missing required UAT tools ${quoteToolNames(missingRequiredTools)}`);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const forbiddenCanonical = new Set(
|
|
330
|
+
RUN_UAT_FORBIDDEN_TOOL_NAMES
|
|
331
|
+
.filter((toolName) => !toolName.includes("*"))
|
|
332
|
+
.map((toolName) => canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName)),
|
|
333
|
+
);
|
|
334
|
+
const forbiddenPresentedTools: string[] = [];
|
|
335
|
+
for (const toolName of params.presentation.presentedTools) {
|
|
336
|
+
const canonical = canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName);
|
|
337
|
+
if (toolName === "mcp__gsd-workflow__*" || forbiddenCanonical.has(canonical)) {
|
|
338
|
+
forbiddenPresentedTools.push(toolName);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
if (forbiddenPresentedTools.length === 1) {
|
|
342
|
+
errors.push(`presentation includes forbidden run-uat tool "${forbiddenPresentedTools[0]}"`);
|
|
343
|
+
} else if (forbiddenPresentedTools.length > 1) {
|
|
344
|
+
errors.push(`presentation includes forbidden run-uat tools ${quoteToolNames(forbiddenPresentedTools)}`);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const blockedCanonical = new Set(
|
|
348
|
+
params.presentation.blockedTools.map((entry) =>
|
|
349
|
+
canonicalWorkflowToolName(parseMcpToolName(entry.name)?.tool ?? entry.name)
|
|
350
|
+
),
|
|
351
|
+
);
|
|
352
|
+
const missingBlockedTools = ["gsd_exec", "gsd_summary_save", "gsd_save_gate_result"].filter(
|
|
353
|
+
(blockedTool) => !blockedCanonical.has(blockedTool),
|
|
354
|
+
);
|
|
355
|
+
if (missingBlockedTools.length === 1) {
|
|
356
|
+
errors.push(`presentation must record "${missingBlockedTools[0]}" as blocked during run-uat`);
|
|
357
|
+
} else if (missingBlockedTools.length > 1) {
|
|
358
|
+
errors.push(`presentation must record ${quoteToolNames(missingBlockedTools)} as blocked during run-uat`);
|
|
359
|
+
}
|
|
360
|
+
return errors.length > 0 ? errors.join("; ") : null;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function nextUatAttempt(basePath: string, milestoneId: string, sliceId: string): number {
|
|
364
|
+
const contract = resolveGsdPathContract(basePath);
|
|
365
|
+
const dir = join(contract.projectGsd, "uat", milestoneId, sliceId);
|
|
366
|
+
if (!existsSync(dir)) return 1;
|
|
367
|
+
let max = 0;
|
|
368
|
+
for (const entry of readdirSync(dir)) {
|
|
369
|
+
const match = /^attempt-(\d+)\.json$/.exec(entry);
|
|
370
|
+
if (match) max = Math.max(max, Number(match[1]));
|
|
371
|
+
}
|
|
372
|
+
return max + 1;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function resolveUatAttempt(basePath: string, params: UatResultSaveParams): number | UatRunValidationError {
|
|
376
|
+
if (params.attempt === "auto" || params.attempt === undefined) {
|
|
377
|
+
return nextUatAttempt(basePath, params.milestoneId, params.sliceId);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const attempt = typeof params.attempt === "string"
|
|
381
|
+
? Number.parseInt(params.attempt, 10)
|
|
382
|
+
: params.attempt;
|
|
383
|
+
if (!Number.isInteger(attempt) || attempt < 1) {
|
|
384
|
+
return {
|
|
385
|
+
code: "invalid_attempt",
|
|
386
|
+
message: "attempt must be a positive integer or auto",
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
return attempt;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function escapeMarkdownTableCell(value: unknown): string {
|
|
393
|
+
return String(value ?? "")
|
|
394
|
+
.replace(/[\\|]/g, (char) => `\\${char}`)
|
|
395
|
+
.replace(/\r?\n/g, "<br>");
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
interface UatAssessmentContext {
|
|
399
|
+
attempt: number;
|
|
400
|
+
gateVerdict: "pass" | "flag";
|
|
401
|
+
runId: string;
|
|
402
|
+
worktreeRoot: string;
|
|
403
|
+
evaluatedAt: string;
|
|
404
|
+
manualGuidance: string | null;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function renderCheckRow(check: UatCheckResultInput): string {
|
|
408
|
+
const evidence = (check.evidence ?? []).map((entry) => `${entry.kind}:${entry.ref}`).join("<br>") || "-";
|
|
409
|
+
return `| ${escapeMarkdownTableCell(check.description)} | ${escapeMarkdownTableCell(check.mode)} | ${escapeMarkdownTableCell(check.result)} | ${escapeMarkdownTableCell(evidence)} | ${escapeMarkdownTableCell(check.notes)} |`;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function renderUatAssessment(params: UatResultSaveParams, run: UatAssessmentContext): string {
|
|
413
|
+
const lines = [
|
|
414
|
+
"---",
|
|
415
|
+
`sliceId: ${params.sliceId}`,
|
|
416
|
+
`uatType: ${params.uatType}`,
|
|
417
|
+
`verdict: ${params.verdict}`,
|
|
418
|
+
`attempt: ${run.attempt}`,
|
|
419
|
+
`runId: ${run.runId}`,
|
|
420
|
+
`worktreeRoot: ${run.worktreeRoot}`,
|
|
421
|
+
`date: ${run.evaluatedAt}`,
|
|
422
|
+
"---",
|
|
423
|
+
"",
|
|
424
|
+
`# UAT Result - ${params.sliceId}`,
|
|
425
|
+
"",
|
|
426
|
+
"## Checks",
|
|
427
|
+
"",
|
|
428
|
+
"| Check | Mode | Result | Evidence | Notes |",
|
|
429
|
+
"|-------|------|--------|----------|-------|",
|
|
430
|
+
...params.checks.map(renderCheckRow),
|
|
431
|
+
"",
|
|
432
|
+
"## Overall Verdict",
|
|
433
|
+
"",
|
|
434
|
+
`${params.verdict} - ${params.notes ?? "UAT result saved."}`,
|
|
435
|
+
"",
|
|
436
|
+
"## Tool Presentation",
|
|
437
|
+
"",
|
|
438
|
+
"```json",
|
|
439
|
+
JSON.stringify(params.presentation, null, 2),
|
|
440
|
+
"```",
|
|
441
|
+
"",
|
|
442
|
+
"## Gate",
|
|
443
|
+
"",
|
|
444
|
+
`Aggregate UAT gate saved as ${run.gateVerdict}.`,
|
|
445
|
+
];
|
|
446
|
+
|
|
447
|
+
if (run.manualGuidance) {
|
|
448
|
+
lines.push(
|
|
449
|
+
"",
|
|
450
|
+
"## Manual Validation",
|
|
451
|
+
"",
|
|
452
|
+
"One or more checks are marked `NEEDS-HUMAN` and require a person to validate:",
|
|
453
|
+
"",
|
|
454
|
+
...run.manualGuidance.split("\n").map((line) => `- ${line}`),
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return `${lines.join("\n")}\n`;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
export function prepareUatRun(basePath: string, rawParams: UatResultSaveParams): PrepareUatRunResult {
|
|
462
|
+
let params = normalizeUatVerdict(rawParams);
|
|
463
|
+
params = supplyDefaultPresentation(params);
|
|
464
|
+
|
|
465
|
+
const requiredError = ensureUatRequiredFields(params);
|
|
466
|
+
if (requiredError) return { ok: false, error: { code: "invalid_params", message: requiredError } };
|
|
467
|
+
|
|
468
|
+
const presentationError = validateCanonicalPresentation(params);
|
|
469
|
+
if (presentationError) return { ok: false, error: { code: "alias_tool_name", message: presentationError } };
|
|
470
|
+
|
|
471
|
+
params = mergeCanonicalPresentation(params);
|
|
472
|
+
|
|
473
|
+
const checkError = validateUatChecks(basePath, params);
|
|
474
|
+
if (checkError) return { ok: false, error: { code: "invalid_evidence", message: checkError } };
|
|
475
|
+
|
|
476
|
+
const freshEvidenceError = validateFreshUatOwnedEvidence(params);
|
|
477
|
+
if (freshEvidenceError) {
|
|
478
|
+
return { ok: false, error: { code: "missing_fresh_uat_evidence", message: freshEvidenceError } };
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const modeError = validateUatModePolicy(params);
|
|
482
|
+
if (modeError) return { ok: false, error: { code: "uat_mode_mismatch", message: modeError } };
|
|
483
|
+
|
|
484
|
+
const attempt = resolveUatAttempt(basePath, params);
|
|
485
|
+
if (typeof attempt !== "number") return { ok: false, error: attempt };
|
|
486
|
+
|
|
487
|
+
const gateVerdict = params.verdict === "PASS" ? "pass" : "flag";
|
|
488
|
+
const gateOutcome = params.verdict === "PASS" ? "pass" : "fail";
|
|
489
|
+
const rationale = params.notes ?? `UAT ${params.verdict} for ${params.sliceId}.`;
|
|
490
|
+
const evaluatedAt = new Date().toISOString();
|
|
491
|
+
const runId = `uat:${params.milestoneId}:${params.sliceId}:attempt-${attempt}`;
|
|
492
|
+
const worktreeRoot = resolveCanonicalMilestoneRoot(basePath, params.milestoneId);
|
|
493
|
+
const hasHuman = params.checks.some((check) => check.result === "NEEDS-HUMAN");
|
|
494
|
+
const manualGuidance = hasHuman
|
|
495
|
+
? buildManualValidationGuidance(basePath, params.milestoneId, {
|
|
496
|
+
uatPath: relSliceFile(basePath, params.milestoneId, params.sliceId, "UAT"),
|
|
497
|
+
})
|
|
498
|
+
: null;
|
|
499
|
+
const browserToolsPresented = hasUatBrowserToolSurface(params.presentation.presentedTools);
|
|
500
|
+
const assessment = renderUatAssessment(params, {
|
|
501
|
+
attempt,
|
|
502
|
+
gateVerdict,
|
|
503
|
+
runId,
|
|
504
|
+
worktreeRoot,
|
|
505
|
+
evaluatedAt,
|
|
506
|
+
manualGuidance,
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
return {
|
|
510
|
+
ok: true,
|
|
511
|
+
run: {
|
|
512
|
+
params,
|
|
513
|
+
runId,
|
|
514
|
+
attempt,
|
|
515
|
+
gateVerdict,
|
|
516
|
+
gateOutcome,
|
|
517
|
+
rationale,
|
|
518
|
+
assessment,
|
|
519
|
+
evaluatedAt,
|
|
520
|
+
hasHuman,
|
|
521
|
+
manualGuidance,
|
|
522
|
+
worktreeRoot,
|
|
523
|
+
browserToolsPresented,
|
|
524
|
+
},
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
export async function saveUatAttemptArtifact(basePath: string, run: PreparedUatRun): Promise<string> {
|
|
529
|
+
const contract = resolveGsdPathContract(basePath);
|
|
530
|
+
const relativePath = `uat/${run.params.milestoneId}/${run.params.sliceId}/attempt-${run.attempt}.json`;
|
|
531
|
+
const payload = {
|
|
532
|
+
runId: run.runId,
|
|
533
|
+
attempt: run.attempt,
|
|
534
|
+
milestoneId: run.params.milestoneId,
|
|
535
|
+
sliceId: run.params.sliceId,
|
|
536
|
+
uatType: run.params.uatType,
|
|
537
|
+
verdict: run.params.verdict,
|
|
538
|
+
gateVerdict: run.gateVerdict,
|
|
539
|
+
evaluatedAt: run.evaluatedAt,
|
|
540
|
+
worktreeRoot: run.worktreeRoot,
|
|
541
|
+
browserToolsPresented: run.browserToolsPresented,
|
|
542
|
+
modePolicy: UAT_MODE_POLICIES[run.params.uatType],
|
|
543
|
+
checks: run.params.checks,
|
|
544
|
+
presentation: run.params.presentation,
|
|
545
|
+
notes: run.params.notes,
|
|
546
|
+
previousAttemptId: run.params.previousAttemptId,
|
|
547
|
+
};
|
|
548
|
+
await saveFile(join(contract.projectGsd, relativePath), `${JSON.stringify(payload, null, 2)}\n`);
|
|
549
|
+
return relativePath;
|
|
550
|
+
}
|
|
@@ -317,11 +317,10 @@ const TOOLS_PLANNING_DISPATCH_RECON: ToolsPolicy = {
|
|
|
317
317
|
mode: "planning-dispatch",
|
|
318
318
|
allowedSubagents: ["scout", "planner"],
|
|
319
319
|
};
|
|
320
|
-
// Like TOOLS_PLANNING_DISPATCH_RECON, but for
|
|
321
|
-
// verification work to review-tier specialists.
|
|
320
|
+
// Like TOOLS_PLANNING_DISPATCH_RECON, but for gate-evaluate's tester fanout.
|
|
322
321
|
const TOOLS_PLANNING_DISPATCH_REVIEW: ToolsPolicy = {
|
|
323
322
|
mode: "planning-dispatch",
|
|
324
|
-
allowedSubagents: ["
|
|
323
|
+
allowedSubagents: ["tester"],
|
|
325
324
|
};
|
|
326
325
|
const TOOLS_DOCS: ToolsPolicy = {
|
|
327
326
|
mode: "docs",
|
|
@@ -626,7 +625,7 @@ export const UNIT_MANIFESTS: Record<UnitType, UnitContextManifest> = {
|
|
|
626
625
|
// plan and report via the DB-backed gate-result tool.
|
|
627
626
|
tools: TOOLS_PLANNING_DISPATCH_REVIEW,
|
|
628
627
|
artifacts: {
|
|
629
|
-
inline: ["slice-plan"
|
|
628
|
+
inline: ["slice-plan"],
|
|
630
629
|
excerpt: [],
|
|
631
630
|
onDemand: [],
|
|
632
631
|
},
|
|
@@ -52,8 +52,14 @@ export const UNIT_TOOL_CONTRACTS: Record<string, UnitToolSurfaceContract> = {
|
|
|
52
52
|
requiredWorkflowTools: ["gsd_summary_save"],
|
|
53
53
|
},
|
|
54
54
|
"plan-milestone": {
|
|
55
|
-
allowedGsdTools: [
|
|
56
|
-
|
|
55
|
+
allowedGsdTools: [
|
|
56
|
+
"gsd_milestone_status",
|
|
57
|
+
"gsd_plan_milestone",
|
|
58
|
+
"gsd_plan_slice",
|
|
59
|
+
"gsd_decision_save",
|
|
60
|
+
"gsd_requirement_update",
|
|
61
|
+
],
|
|
62
|
+
requiredWorkflowTools: ["gsd_milestone_status", "gsd_plan_milestone", "gsd_plan_slice"],
|
|
57
63
|
},
|
|
58
64
|
"discuss-milestone": {
|
|
59
65
|
allowedGsdTools: [
|
|
@@ -77,27 +83,38 @@ export const UNIT_TOOL_CONTRACTS: Record<string, UnitToolSurfaceContract> = {
|
|
|
77
83
|
requiredWorkflowTools: ["gsd_summary_save"],
|
|
78
84
|
},
|
|
79
85
|
"validate-milestone": {
|
|
80
|
-
allowedGsdTools: ["gsd_validate_milestone", "gsd_reassess_roadmap", "subagent"],
|
|
86
|
+
allowedGsdTools: ["gsd_milestone_status", "gsd_validate_milestone", "gsd_reassess_roadmap", "subagent"],
|
|
81
87
|
requiredWorkflowTools: ["gsd_milestone_status", "gsd_validate_milestone", "gsd_reassess_roadmap"],
|
|
82
88
|
},
|
|
83
89
|
"complete-milestone": {
|
|
84
|
-
allowedGsdTools: [
|
|
85
|
-
|
|
90
|
+
allowedGsdTools: [
|
|
91
|
+
"gsd_milestone_status",
|
|
92
|
+
"gsd_requirement_update",
|
|
93
|
+
"gsd_summary_save",
|
|
94
|
+
"gsd_complete_milestone",
|
|
95
|
+
"subagent",
|
|
96
|
+
],
|
|
97
|
+
requiredWorkflowTools: [
|
|
98
|
+
"gsd_milestone_status",
|
|
99
|
+
"gsd_requirement_update",
|
|
100
|
+
"gsd_summary_save",
|
|
101
|
+
"gsd_complete_milestone",
|
|
102
|
+
],
|
|
86
103
|
},
|
|
87
104
|
"research-slice": {
|
|
88
105
|
allowedGsdTools: ["gsd_summary_save", "gsd_decision_save"],
|
|
89
106
|
requiredWorkflowTools: ["gsd_summary_save"],
|
|
90
107
|
},
|
|
91
108
|
"plan-slice": {
|
|
92
|
-
allowedGsdTools: ["gsd_plan_slice", "
|
|
93
|
-
requiredWorkflowTools: ["gsd_plan_slice"],
|
|
109
|
+
allowedGsdTools: ["gsd_plan_slice", "gsd_reassess_roadmap", "gsd_decision_save"],
|
|
110
|
+
requiredWorkflowTools: ["gsd_plan_slice", "gsd_reassess_roadmap"],
|
|
94
111
|
},
|
|
95
112
|
"refine-slice": {
|
|
96
|
-
allowedGsdTools: ["gsd_plan_slice", "
|
|
97
|
-
requiredWorkflowTools: [],
|
|
113
|
+
allowedGsdTools: ["gsd_plan_slice", "gsd_decision_save"],
|
|
114
|
+
requiredWorkflowTools: ["gsd_plan_slice"],
|
|
98
115
|
},
|
|
99
116
|
"replan-slice": {
|
|
100
|
-
allowedGsdTools: ["gsd_replan_slice", "
|
|
117
|
+
allowedGsdTools: ["gsd_replan_slice", "gsd_decision_save"],
|
|
101
118
|
requiredWorkflowTools: ["gsd_replan_slice"],
|
|
102
119
|
},
|
|
103
120
|
"complete-slice": {
|
|
@@ -107,15 +124,22 @@ export const UNIT_TOOL_CONTRACTS: Record<string, UnitToolSurfaceContract> = {
|
|
|
107
124
|
"gsd_replan_slice",
|
|
108
125
|
"gsd_decision_save",
|
|
109
126
|
"gsd_requirement_update",
|
|
127
|
+
"gsd_summary_save",
|
|
110
128
|
"subagent",
|
|
111
129
|
],
|
|
112
|
-
requiredWorkflowTools: [
|
|
130
|
+
requiredWorkflowTools: [
|
|
131
|
+
"gsd_slice_complete",
|
|
132
|
+
"gsd_task_reopen",
|
|
133
|
+
"gsd_replan_slice",
|
|
134
|
+
"gsd_requirement_update",
|
|
135
|
+
"gsd_summary_save",
|
|
136
|
+
],
|
|
113
137
|
forbiddenGsdTools: {
|
|
114
138
|
gsd_uat_result_save: "Run UAT owns persisted UAT Assessment.",
|
|
115
139
|
},
|
|
116
140
|
},
|
|
117
141
|
"reassess-roadmap": {
|
|
118
|
-
allowedGsdTools: ["gsd_reassess_roadmap"],
|
|
142
|
+
allowedGsdTools: ["gsd_milestone_status", "gsd_reassess_roadmap"],
|
|
119
143
|
requiredWorkflowTools: ["gsd_milestone_status", "gsd_reassess_roadmap"],
|
|
120
144
|
},
|
|
121
145
|
"execute-task": {
|
|
@@ -127,8 +151,8 @@ export const UNIT_TOOL_CONTRACTS: Record<string, UnitToolSurfaceContract> = {
|
|
|
127
151
|
requiredWorkflowTools: ["gsd_task_complete"],
|
|
128
152
|
},
|
|
129
153
|
"reactive-execute": {
|
|
130
|
-
allowedGsdTools: ["gsd_task_complete", "gsd_decision_save"],
|
|
131
|
-
requiredWorkflowTools: ["gsd_task_complete"],
|
|
154
|
+
allowedGsdTools: ["gsd_task_complete", "gsd_summary_save", "gsd_decision_save"],
|
|
155
|
+
requiredWorkflowTools: ["gsd_task_complete", "gsd_summary_save"],
|
|
132
156
|
},
|
|
133
157
|
"run-uat": {
|
|
134
158
|
allowedGsdTools: [...RUN_UAT_WORKFLOW_TOOL_NAMES, "subagent"],
|