@opengsd/gsd-pi 1.1.1-dev.9f86580 → 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/index.js +39 -22
- 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/gsd/auto/loop.js +4 -2
- package/dist/resources/extensions/gsd/auto/phases.js +43 -10
- package/dist/resources/extensions/gsd/auto/session.js +20 -1
- package/dist/resources/extensions/gsd/auto/workflow-kernel.js +1 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +72 -12
- package/dist/resources/extensions/gsd/auto-model-selection.js +128 -9
- package/dist/resources/extensions/gsd/auto-post-unit.js +19 -2
- package/dist/resources/extensions/gsd/auto-prompts.js +24 -19
- 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.js +14 -11
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +172 -65
- package/dist/resources/extensions/gsd/closeout-wizard.js +32 -9
- 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-prefs-wizard.js +2 -2
- 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/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/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 +110 -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-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/refine-slice.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/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 +14 -0
- package/dist/resources/extensions/gsd/tool-presentation-plan.js +1 -9
- package/dist/resources/extensions/gsd/tools/complete-slice.js +7 -6
- 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 +57 -429
- 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/verdict-parser.js +3 -8
- package/dist/resources/extensions/gsd/workflow-manifest.js +132 -5
- package/dist/resources/extensions/gsd/workflow-projections.js +8 -0
- package/dist/resources/extensions/gsd/worktree-state-projection.js +18 -17
- 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 +8 -8
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
- package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/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 +23 -17
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +25 -24
- package/packages/pi-ai/dist/models.generated.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/src/resources/extensions/browser-tools/index.ts +39 -22
- package/src/resources/extensions/browser-tools/state.ts +13 -0
- package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +57 -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/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 +42 -10
- package/src/resources/extensions/gsd/auto/session.ts +22 -1
- package/src/resources/extensions/gsd/auto/workflow-kernel.ts +1 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +85 -12
- package/src/resources/extensions/gsd/auto-model-selection.ts +164 -12
- package/src/resources/extensions/gsd/auto-post-unit.ts +20 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +23 -20
- 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.ts +13 -10
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +225 -72
- package/src/resources/extensions/gsd/closeout-wizard.ts +47 -13
- 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-prefs-wizard.ts +2 -2
- 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/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/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 +112 -43
- package/src/resources/extensions/gsd/preferences-types.ts +39 -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-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/refine-slice.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/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 +15 -0
- 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/before-provider-context-management.test.ts +145 -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/content-validator.test.ts +74 -0
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +16 -2
- 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/journal-integration.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +42 -0
- 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/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 +9 -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 +8 -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/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-tool-executors.test.ts +73 -6
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +511 -1
- package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +44 -0
- package/src/resources/extensions/gsd/tool-contract.ts +28 -0
- package/src/resources/extensions/gsd/tool-presentation-plan.ts +1 -11
- package/src/resources/extensions/gsd/tools/complete-slice.ts +7 -6
- 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 +66 -526
- 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/verdict-parser.ts +3 -10
- package/src/resources/extensions/gsd/workflow-manifest.ts +193 -7
- package/src/resources/extensions/gsd/workflow-projections.ts +9 -0
- package/src/resources/extensions/gsd/worktree-state-projection.ts +22 -22
- 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/{zzYMrKpPGfRQRxSFO32Jr → tJOKQbQRO-9MiFDO8DIDS}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{zzYMrKpPGfRQRxSFO32Jr → tJOKQbQRO-9MiFDO8DIDS}/_ssgManifest.js +0 -0
|
@@ -3,14 +3,13 @@
|
|
|
3
3
|
import { ensureDbOpen } from "../bootstrap/dynamic-tools.js";
|
|
4
4
|
import { sanitizeCompleteMilestoneParams } from "../bootstrap/sanitize-complete-milestone.js";
|
|
5
5
|
import { loadWriteGateSnapshot, shouldBlockContextArtifactSaveInSnapshot, shouldBlockRootArtifactSaveInSnapshot } from "../bootstrap/write-gate.js";
|
|
6
|
-
import { getActiveRequirements, insertMilestone, getMilestone, getSliceStatusSummary, getSliceTaskCounts, insertGateRun, readTransaction, saveGateResult, upsertQualityGate, } from "../gsd-db.js";
|
|
6
|
+
import { getActiveRequirements, insertMilestone, getMilestone, getSliceStatusSummary, getSliceTaskCounts, insertAssessment, insertGateRun, readTransaction, saveGateResult, upsertQualityGate, } from "../gsd-db.js";
|
|
7
7
|
import { GATE_REGISTRY } from "../gate-registry.js";
|
|
8
8
|
import { generateRequirementsMd, saveArtifactToDb } from "../db-writer.js";
|
|
9
9
|
import { clearPathCache, relSliceFile, resolveGsdPathContract, resolveMilestoneFile, resolveSliceFile } from "../paths.js";
|
|
10
10
|
import { saveFile, clearParseCache } from "../files.js";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { isAbsolute, join, resolve } from "node:path";
|
|
11
|
+
import { unlinkSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
14
13
|
import { handleCompleteMilestone } from "./complete-milestone.js";
|
|
15
14
|
import { handleCompleteTask } from "./complete-task.js";
|
|
16
15
|
import { handleCompleteSlice } from "./complete-slice.js";
|
|
@@ -27,7 +26,8 @@ import { invalidateStateCache } from "../state.js";
|
|
|
27
26
|
import { loadEffectiveGSDPreferences } from "../preferences.js";
|
|
28
27
|
import { parseProject } from "../schemas/parsers.js";
|
|
29
28
|
import { getAutoRuntimeSnapshot } from "../auto-runtime-state.js";
|
|
30
|
-
import {
|
|
29
|
+
import { renderPlanFromDb } from "../markdown-renderer.js";
|
|
30
|
+
import { prepareUatRun, saveUatAttemptArtifact, } from "../uat-run.js";
|
|
31
31
|
export const SUPPORTED_SUMMARY_ARTIFACT_TYPES = [
|
|
32
32
|
"SUMMARY",
|
|
33
33
|
"RESEARCH",
|
|
@@ -791,6 +791,7 @@ export async function executeSaveGateResult(params, basePath = process.cwd()) {
|
|
|
791
791
|
rationale: params.rationale,
|
|
792
792
|
findings: params.findings ?? "",
|
|
793
793
|
});
|
|
794
|
+
await renderPlanFromDb(basePath, params.milestoneId, params.sliceId);
|
|
794
795
|
invalidateStateCache();
|
|
795
796
|
return {
|
|
796
797
|
content: [{ type: "text", text: `Gate ${params.gateId} result saved: verdict=${params.verdict}` }],
|
|
@@ -814,462 +815,89 @@ function errorResult(operation, message, error) {
|
|
|
814
815
|
isError: true,
|
|
815
816
|
};
|
|
816
817
|
}
|
|
817
|
-
function isNonEmptyString(value) {
|
|
818
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
819
|
-
}
|
|
820
|
-
function mergeBlockedTools(current, canonical) {
|
|
821
|
-
const merged = new Map();
|
|
822
|
-
for (const entry of [...(current ?? []), ...canonical]) {
|
|
823
|
-
merged.set(canonicalWorkflowToolName(parseMcpToolName(entry.name)?.tool ?? entry.name), entry);
|
|
824
|
-
}
|
|
825
|
-
return [...merged.values()];
|
|
826
|
-
}
|
|
827
|
-
function mergePresentedTools(current, canonical) {
|
|
828
|
-
return [...new Set([...(current ?? []), ...canonical])];
|
|
829
|
-
}
|
|
830
|
-
function normalizeUatVerdict(params) {
|
|
831
|
-
const raw = params;
|
|
832
|
-
if (typeof raw.verdict === "string") {
|
|
833
|
-
return { ...params, verdict: raw.verdict.toUpperCase() };
|
|
834
|
-
}
|
|
835
|
-
return params;
|
|
836
|
-
}
|
|
837
|
-
function supplyDefaultPresentation(params) {
|
|
838
|
-
const raw = params;
|
|
839
|
-
if (!raw.presentation) {
|
|
840
|
-
return { ...params, presentation: buildRunUatPresentationForType(params.uatType) };
|
|
841
|
-
}
|
|
842
|
-
return params;
|
|
843
|
-
}
|
|
844
|
-
function mergeCanonicalPresentation(params) {
|
|
845
|
-
const canonicalPresentation = buildRunUatPresentationForType(params.uatType);
|
|
846
|
-
const providedPresentation = params.presentation;
|
|
847
|
-
return {
|
|
848
|
-
...params,
|
|
849
|
-
presentation: {
|
|
850
|
-
...providedPresentation,
|
|
851
|
-
surface: providedPresentation.surface ?? canonicalPresentation.surface,
|
|
852
|
-
presentedTools: mergePresentedTools(providedPresentation.presentedTools, canonicalPresentation.presentedTools),
|
|
853
|
-
blockedTools: mergeBlockedTools(providedPresentation.blockedTools, canonicalPresentation.blockedTools),
|
|
854
|
-
toolPresentationPlanId: RUN_UAT_TOOL_PRESENTATION_PLAN_ID,
|
|
855
|
-
},
|
|
856
|
-
};
|
|
857
|
-
}
|
|
858
|
-
const VALID_UAT_TYPES = [
|
|
859
|
-
"artifact-driven",
|
|
860
|
-
"browser-executable",
|
|
861
|
-
"runtime-executable",
|
|
862
|
-
"live-runtime",
|
|
863
|
-
"mixed",
|
|
864
|
-
"human-experience",
|
|
865
|
-
];
|
|
866
|
-
function ensureUatRequiredFields(params) {
|
|
867
|
-
if (!isNonEmptyString(params.milestoneId))
|
|
868
|
-
return "milestoneId is required";
|
|
869
|
-
if (!isNonEmptyString(params.sliceId))
|
|
870
|
-
return "sliceId is required";
|
|
871
|
-
if (!isNonEmptyString(params.uatType))
|
|
872
|
-
return "uatType is required";
|
|
873
|
-
if (!VALID_UAT_TYPES.includes(params.uatType)) {
|
|
874
|
-
return `uatType must be one of: ${VALID_UAT_TYPES.join(", ")}`;
|
|
875
|
-
}
|
|
876
|
-
if (!["PASS", "FAIL", "PARTIAL"].includes(params.verdict))
|
|
877
|
-
return "verdict must be PASS, FAIL, or PARTIAL";
|
|
878
|
-
if (!Array.isArray(params.checks) || params.checks.length === 0)
|
|
879
|
-
return "checks must contain at least one UAT check";
|
|
880
|
-
if (!params.presentation || !Array.isArray(params.presentation.presentedTools))
|
|
881
|
-
return "presentation.presentedTools is required";
|
|
882
|
-
if (!Array.isArray(params.presentation.blockedTools))
|
|
883
|
-
return "presentation.blockedTools is required";
|
|
884
|
-
return null;
|
|
885
|
-
}
|
|
886
|
-
function approvedEvidenceRoots(basePath) {
|
|
887
|
-
const contract = resolveGsdPathContract(basePath);
|
|
888
|
-
return [contract.worktreeGsd, contract.projectGsd].filter((root) => typeof root === "string");
|
|
889
|
-
}
|
|
890
|
-
function approvedBrowserArtifactRoots(basePath) {
|
|
891
|
-
const contract = resolveGsdPathContract(basePath);
|
|
892
|
-
const roots = [contract.workRoot, contract.projectRoot].map((root) => join(root, ".artifacts", "browser"));
|
|
893
|
-
return [...new Set(roots)];
|
|
894
|
-
}
|
|
895
|
-
function pathStartsWithin(parent, target) {
|
|
896
|
-
const normalizedParent = parent.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
897
|
-
const normalizedTarget = target.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
898
|
-
return normalizedTarget === normalizedParent || normalizedTarget.startsWith(`${normalizedParent}/`);
|
|
899
|
-
}
|
|
900
|
-
function pushUnique(paths, candidate) {
|
|
901
|
-
if (!paths.includes(candidate))
|
|
902
|
-
paths.push(candidate);
|
|
903
|
-
}
|
|
904
|
-
function execMetaPathCandidates(basePath, ref) {
|
|
905
|
-
const trimmed = ref.trim();
|
|
906
|
-
const candidates = [];
|
|
907
|
-
const execDirs = approvedEvidenceRoots(basePath).map((root) => join(root, "exec"));
|
|
908
|
-
const normalizedRef = trimmed.replace(/\\/g, "/");
|
|
909
|
-
const pathLike = normalizedRef.endsWith(".meta.json") || normalizedRef.includes("/.gsd/exec/");
|
|
910
|
-
if (pathLike) {
|
|
911
|
-
const rawPath = isAbsolute(trimmed) ? resolve(trimmed) : resolve(basePath, trimmed);
|
|
912
|
-
pushUnique(candidates, rawPath);
|
|
913
|
-
const relativeExecMarker = ".gsd/exec/";
|
|
914
|
-
const markerIndex = normalizedRef.indexOf(relativeExecMarker);
|
|
915
|
-
if (markerIndex >= 0) {
|
|
916
|
-
const execRelative = normalizedRef.slice(markerIndex + relativeExecMarker.length);
|
|
917
|
-
for (const execDir of execDirs) {
|
|
918
|
-
pushUnique(candidates, join(execDir, execRelative));
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
return candidates.filter((candidate) => execDirs.some((execDir) => pathStartsWithin(execDir, candidate)));
|
|
922
|
-
}
|
|
923
|
-
for (const execDir of execDirs) {
|
|
924
|
-
pushUnique(candidates, join(execDir, `${trimmed}.meta.json`));
|
|
925
|
-
}
|
|
926
|
-
return candidates;
|
|
927
|
-
}
|
|
928
|
-
function resolveExecMetaPath(basePath, ref) {
|
|
929
|
-
for (const candidate of execMetaPathCandidates(basePath, ref)) {
|
|
930
|
-
if (existsSync(candidate))
|
|
931
|
-
return candidate;
|
|
932
|
-
}
|
|
933
|
-
return null;
|
|
934
|
-
}
|
|
935
|
-
function evidencePathIsApproved(basePath, ref) {
|
|
936
|
-
const normalizedRef = ref.replace(/\\/g, "/");
|
|
937
|
-
if (normalizedRef.startsWith(".gsd/exec/") || normalizedRef.startsWith(".gsd/uat/"))
|
|
938
|
-
return true;
|
|
939
|
-
if (normalizedRef.startsWith(".artifacts/browser/")) {
|
|
940
|
-
const resolvedRef = resolve(basePath, ref);
|
|
941
|
-
return approvedBrowserArtifactRoots(basePath).some((root) => pathStartsWithin(root, resolvedRef));
|
|
942
|
-
}
|
|
943
|
-
const gsdEvidenceApproved = approvedEvidenceRoots(basePath).some((root) => {
|
|
944
|
-
return pathStartsWithin(join(root, "exec"), ref) || pathStartsWithin(join(root, "uat"), ref);
|
|
945
|
-
});
|
|
946
|
-
if (gsdEvidenceApproved)
|
|
947
|
-
return true;
|
|
948
|
-
return approvedBrowserArtifactRoots(basePath).some((root) => pathStartsWithin(root, ref));
|
|
949
|
-
}
|
|
950
|
-
function validateEvidenceRef(basePath, evidence) {
|
|
951
|
-
if (!isNonEmptyString(evidence.ref))
|
|
952
|
-
return "evidence.ref is required";
|
|
953
|
-
if (evidence.kind === "gsd_uat_exec" || evidence.kind === "gsd_exec") {
|
|
954
|
-
const path = resolveExecMetaPath(basePath, evidence.ref.trim());
|
|
955
|
-
if (!path)
|
|
956
|
-
return `missing gsd_exec metadata for evidence id "${evidence.ref}"`;
|
|
957
|
-
if (evidence.kind === "gsd_uat_exec") {
|
|
958
|
-
try {
|
|
959
|
-
const meta = JSON.parse(readFileSync(path, "utf-8"));
|
|
960
|
-
if (meta.metadata?.kind !== "uat_exec")
|
|
961
|
-
return `evidence id "${evidence.ref}" is not typed as uat_exec`;
|
|
962
|
-
}
|
|
963
|
-
catch {
|
|
964
|
-
return `invalid gsd_exec metadata JSON for evidence id "${evidence.ref}"`;
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
return null;
|
|
968
|
-
}
|
|
969
|
-
if (evidence.kind === "url") {
|
|
970
|
-
try {
|
|
971
|
-
const parsed = new URL(evidence.ref);
|
|
972
|
-
return parsed.protocol === "http:" || parsed.protocol === "https:"
|
|
973
|
-
? null
|
|
974
|
-
: `invalid URL evidence ref "${evidence.ref}"`;
|
|
975
|
-
}
|
|
976
|
-
catch {
|
|
977
|
-
return `invalid URL evidence ref "${evidence.ref}"`;
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
return evidencePathIsApproved(basePath, evidence.ref)
|
|
981
|
-
? null
|
|
982
|
-
: `evidence ref "${evidence.ref}" is outside approved evidence locations`;
|
|
983
|
-
}
|
|
984
|
-
function validateUatChecks(basePath, params) {
|
|
985
|
-
for (const check of params.checks) {
|
|
986
|
-
if (!isNonEmptyString(check.id))
|
|
987
|
-
return "every check must have a non-empty id";
|
|
988
|
-
if (!isNonEmptyString(check.description))
|
|
989
|
-
return `check ${check.id} must have a description`;
|
|
990
|
-
if (!["artifact", "runtime", "browser", "human-follow-up"].includes(check.mode)) {
|
|
991
|
-
return `check ${check.id} has invalid mode "${check.mode}"`;
|
|
992
|
-
}
|
|
993
|
-
if (!["PASS", "FAIL", "NEEDS-HUMAN"].includes(check.result)) {
|
|
994
|
-
return `check ${check.id} has invalid result "${check.result}"`;
|
|
995
|
-
}
|
|
996
|
-
if (check.result === "PASS" || check.result === "FAIL") {
|
|
997
|
-
if (!Array.isArray(check.evidence) || check.evidence.length === 0) {
|
|
998
|
-
return `check ${check.id} is ${check.result} but has no objective evidence`;
|
|
999
|
-
}
|
|
1000
|
-
for (const evidence of check.evidence) {
|
|
1001
|
-
const error = validateEvidenceRef(basePath, evidence);
|
|
1002
|
-
if (error)
|
|
1003
|
-
return `check ${check.id}: ${error}`;
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
else if (!isNonEmptyString(check.notes)) {
|
|
1007
|
-
return `check ${check.id} is NEEDS-HUMAN but has no manual instruction or reason`;
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
return null;
|
|
1011
|
-
}
|
|
1012
|
-
function validateFreshUatOwnedEvidence(params) {
|
|
1013
|
-
const hasFreshUatEvidence = params.checks.some((check) => (check.evidence ?? []).some((evidence) => evidence.kind === "gsd_uat_exec"));
|
|
1014
|
-
return hasFreshUatEvidence
|
|
1015
|
-
? null
|
|
1016
|
-
: "UAT Assessment requires at least one fresh gsd_uat_exec evidence reference from run-uat";
|
|
1017
|
-
}
|
|
1018
|
-
function validateUatMode(params) {
|
|
1019
|
-
const modes = new Set(params.checks.map((check) => check.mode));
|
|
1020
|
-
const hasHuman = params.checks.some((check) => check.result === "NEEDS-HUMAN");
|
|
1021
|
-
if (params.uatType === "artifact-driven" && hasHuman && params.verdict === "PASS") {
|
|
1022
|
-
return "artifact-driven UAT cannot PASS with human-only checks";
|
|
1023
|
-
}
|
|
1024
|
-
if (hasHuman &&
|
|
1025
|
-
params.verdict === "PASS" &&
|
|
1026
|
-
!["human-experience", "mixed", "live-runtime"].includes(params.uatType) &&
|
|
1027
|
-
!params.checks.every((check) => check.result !== "NEEDS-HUMAN" || check.nonAutomatable === true)) {
|
|
1028
|
-
return "NEEDS-HUMAN checks can only coexist with PASS for human-experience, mixed, live-runtime, or explicitly non-automatable checks";
|
|
1029
|
-
}
|
|
1030
|
-
if (params.uatType === "runtime-executable" && !modes.has("runtime")) {
|
|
1031
|
-
return "runtime-executable UAT requires at least one runtime check";
|
|
1032
|
-
}
|
|
1033
|
-
if (params.uatType === "browser-executable" && !modes.has("browser")) {
|
|
1034
|
-
return "browser-executable UAT requires at least one browser check";
|
|
1035
|
-
}
|
|
1036
|
-
if (params.uatType === "live-runtime" && !modes.has("runtime") && !modes.has("browser")) {
|
|
1037
|
-
return "live-runtime UAT requires runtime or browser evidence";
|
|
1038
|
-
}
|
|
1039
|
-
return null;
|
|
1040
|
-
}
|
|
1041
|
-
function quoteToolNames(toolNames) {
|
|
1042
|
-
return toolNames.map((toolName) => `"${toolName}"`).join(", ");
|
|
1043
|
-
}
|
|
1044
|
-
function validateCanonicalPresentation(params) {
|
|
1045
|
-
const aliasHints = {
|
|
1046
|
-
gsd_save_summary: "gsd_summary_save",
|
|
1047
|
-
gsd_complete_task: "gsd_task_complete",
|
|
1048
|
-
gsd_complete_slice: "gsd_slice_complete",
|
|
1049
|
-
gsd_milestone_complete: "gsd_complete_milestone",
|
|
1050
|
-
};
|
|
1051
|
-
const errors = [];
|
|
1052
|
-
for (const toolName of params.presentation.presentedTools) {
|
|
1053
|
-
const baseName = parseMcpToolName(toolName)?.tool ?? toolName;
|
|
1054
|
-
const canonical = aliasHints[baseName];
|
|
1055
|
-
if (canonical)
|
|
1056
|
-
errors.push(`presentation tool "${toolName}" uses an alias; use canonical "${canonical}"`);
|
|
1057
|
-
}
|
|
1058
|
-
const presentedCanonical = new Set(params.presentation.presentedTools.map((toolName) => canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName)));
|
|
1059
|
-
const missingRequiredTools = RUN_UAT_WORKFLOW_TOOL_NAMES.filter((requiredTool) => !presentedCanonical.has(requiredTool));
|
|
1060
|
-
if (missingRequiredTools.length === 1) {
|
|
1061
|
-
errors.push(`presentation is missing required UAT tool "${missingRequiredTools[0]}"`);
|
|
1062
|
-
}
|
|
1063
|
-
else if (missingRequiredTools.length > 1) {
|
|
1064
|
-
errors.push(`presentation is missing required UAT tools ${quoteToolNames(missingRequiredTools)}`);
|
|
1065
|
-
}
|
|
1066
|
-
const forbiddenCanonical = new Set(RUN_UAT_FORBIDDEN_TOOL_NAMES
|
|
1067
|
-
.filter((toolName) => !toolName.includes("*"))
|
|
1068
|
-
.map((toolName) => canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName)));
|
|
1069
|
-
const forbiddenPresentedTools = [];
|
|
1070
|
-
for (const toolName of params.presentation.presentedTools) {
|
|
1071
|
-
const canonical = canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName);
|
|
1072
|
-
if (toolName === "mcp__gsd-workflow__*" || forbiddenCanonical.has(canonical)) {
|
|
1073
|
-
forbiddenPresentedTools.push(toolName);
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1076
|
-
if (forbiddenPresentedTools.length === 1) {
|
|
1077
|
-
errors.push(`presentation includes forbidden run-uat tool "${forbiddenPresentedTools[0]}"`);
|
|
1078
|
-
}
|
|
1079
|
-
else if (forbiddenPresentedTools.length > 1) {
|
|
1080
|
-
errors.push(`presentation includes forbidden run-uat tools ${quoteToolNames(forbiddenPresentedTools)}`);
|
|
1081
|
-
}
|
|
1082
|
-
const blockedCanonical = new Set(params.presentation.blockedTools.map((entry) => canonicalWorkflowToolName(parseMcpToolName(entry.name)?.tool ?? entry.name)));
|
|
1083
|
-
const missingBlockedTools = ["gsd_exec", "gsd_summary_save", "gsd_save_gate_result"].filter((blockedTool) => !blockedCanonical.has(blockedTool));
|
|
1084
|
-
if (missingBlockedTools.length === 1) {
|
|
1085
|
-
errors.push(`presentation must record "${missingBlockedTools[0]}" as blocked during run-uat`);
|
|
1086
|
-
}
|
|
1087
|
-
else if (missingBlockedTools.length > 1) {
|
|
1088
|
-
errors.push(`presentation must record ${quoteToolNames(missingBlockedTools)} as blocked during run-uat`);
|
|
1089
|
-
}
|
|
1090
|
-
return errors.length > 0 ? errors.join("; ") : null;
|
|
1091
|
-
}
|
|
1092
|
-
function nextUatAttempt(basePath, milestoneId, sliceId) {
|
|
1093
|
-
const contract = resolveGsdPathContract(basePath);
|
|
1094
|
-
const dir = join(contract.projectGsd, "uat", milestoneId, sliceId);
|
|
1095
|
-
if (!existsSync(dir))
|
|
1096
|
-
return 1;
|
|
1097
|
-
let max = 0;
|
|
1098
|
-
for (const entry of readdirSync(dir)) {
|
|
1099
|
-
const match = /^attempt-(\d+)\.json$/.exec(entry);
|
|
1100
|
-
if (match)
|
|
1101
|
-
max = Math.max(max, Number(match[1]));
|
|
1102
|
-
}
|
|
1103
|
-
return max + 1;
|
|
1104
|
-
}
|
|
1105
|
-
function escapeMarkdownTableCell(value) {
|
|
1106
|
-
return String(value ?? "")
|
|
1107
|
-
.replace(/[\\|]/g, (char) => `\\${char}`)
|
|
1108
|
-
.replace(/\r?\n/g, "<br>");
|
|
1109
|
-
}
|
|
1110
|
-
function renderUatAssessment(params, attempt, gateVerdict, basePath) {
|
|
1111
|
-
const lines = [
|
|
1112
|
-
"---",
|
|
1113
|
-
`sliceId: ${params.sliceId}`,
|
|
1114
|
-
`uatType: ${params.uatType}`,
|
|
1115
|
-
`verdict: ${params.verdict}`,
|
|
1116
|
-
`attempt: ${attempt}`,
|
|
1117
|
-
`date: ${new Date().toISOString()}`,
|
|
1118
|
-
"---",
|
|
1119
|
-
"",
|
|
1120
|
-
`# UAT Result - ${params.sliceId}`,
|
|
1121
|
-
"",
|
|
1122
|
-
"## Checks",
|
|
1123
|
-
"",
|
|
1124
|
-
"| Check | Mode | Result | Evidence | Notes |",
|
|
1125
|
-
"|-------|------|--------|----------|-------|",
|
|
1126
|
-
...params.checks.map((check) => {
|
|
1127
|
-
const evidence = (check.evidence ?? []).map((entry) => `${entry.kind}:${entry.ref}`).join("<br>") || "-";
|
|
1128
|
-
return `| ${escapeMarkdownTableCell(check.description)} | ${escapeMarkdownTableCell(check.mode)} | ${escapeMarkdownTableCell(check.result)} | ${escapeMarkdownTableCell(evidence)} | ${escapeMarkdownTableCell(check.notes)} |`;
|
|
1129
|
-
}),
|
|
1130
|
-
"",
|
|
1131
|
-
"## Overall Verdict",
|
|
1132
|
-
"",
|
|
1133
|
-
`${params.verdict} - ${params.notes ?? "UAT result saved."}`,
|
|
1134
|
-
"",
|
|
1135
|
-
"## Tool Presentation",
|
|
1136
|
-
"",
|
|
1137
|
-
"```json",
|
|
1138
|
-
JSON.stringify(params.presentation, null, 2),
|
|
1139
|
-
"```",
|
|
1140
|
-
"",
|
|
1141
|
-
"## Gate",
|
|
1142
|
-
"",
|
|
1143
|
-
`Aggregate UAT gate saved as ${gateVerdict}.`,
|
|
1144
|
-
];
|
|
1145
|
-
// When any check still needs a human, point them at the exact checkout to
|
|
1146
|
-
// validate — critical for worktree milestones whose code sits under a hidden
|
|
1147
|
-
// `.gsd/worktrees/` path the reviewer would otherwise have to hunt for.
|
|
1148
|
-
const hasHuman = params.checks.some((check) => check.result === "NEEDS-HUMAN");
|
|
1149
|
-
if (hasHuman) {
|
|
1150
|
-
const guidance = buildManualValidationGuidance(basePath, params.milestoneId, {
|
|
1151
|
-
uatPath: relSliceFile(basePath, params.milestoneId, params.sliceId, "UAT"),
|
|
1152
|
-
});
|
|
1153
|
-
if (guidance) {
|
|
1154
|
-
lines.push("", "## Manual Validation", "", "One or more checks are marked `NEEDS-HUMAN` and require a person to validate:", "", ...guidance.split("\n").map((line) => `- ${line}`));
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
return `${lines.join("\n")}\n`;
|
|
1158
|
-
}
|
|
1159
|
-
async function saveUatAttemptArtifact(basePath, params, attempt) {
|
|
1160
|
-
const contract = resolveGsdPathContract(basePath);
|
|
1161
|
-
const relativePath = `uat/${params.milestoneId}/${params.sliceId}/attempt-${attempt}.json`;
|
|
1162
|
-
await saveFile(join(contract.projectGsd, relativePath), `${JSON.stringify({ ...params, attempt }, null, 2)}\n`);
|
|
1163
|
-
return relativePath;
|
|
1164
|
-
}
|
|
1165
818
|
export async function executeUatResultSave(params, basePath = process.cwd()) {
|
|
1166
819
|
const unitGuard = blockIfWrongAutoUnit("run-uat", "save_uat_result");
|
|
1167
820
|
if (unitGuard)
|
|
1168
821
|
return unitGuard;
|
|
1169
|
-
// Phase 1: normalize verdict and supply the canonical presentation when none was provided.
|
|
1170
|
-
params = normalizeUatVerdict(params);
|
|
1171
|
-
params = supplyDefaultPresentation(params);
|
|
1172
822
|
const dbAvailable = await ensureDbOpen(basePath);
|
|
1173
823
|
if (!dbAvailable)
|
|
1174
824
|
return errorResult("save_uat_result", "GSD database is not available.", "db_unavailable");
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
const presentationError = validateCanonicalPresentation(params);
|
|
1181
|
-
if (presentationError)
|
|
1182
|
-
return errorResult("save_uat_result", presentationError, "alias_tool_name");
|
|
1183
|
-
// Phase 3: merge in the canonical plan ID and read-only audit tools so the persisted
|
|
1184
|
-
// artifact always carries the full audit surface even when the provider omitted them.
|
|
1185
|
-
params = mergeCanonicalPresentation(params);
|
|
1186
|
-
const checkError = validateUatChecks(basePath, params);
|
|
1187
|
-
if (checkError)
|
|
1188
|
-
return errorResult("save_uat_result", checkError, "invalid_evidence");
|
|
1189
|
-
const freshEvidenceError = validateFreshUatOwnedEvidence(params);
|
|
1190
|
-
if (freshEvidenceError)
|
|
1191
|
-
return errorResult("save_uat_result", freshEvidenceError, "missing_fresh_uat_evidence");
|
|
1192
|
-
const modeError = validateUatMode(params);
|
|
1193
|
-
if (modeError)
|
|
1194
|
-
return errorResult("save_uat_result", modeError, "uat_mode_mismatch");
|
|
825
|
+
const prepared = prepareUatRun(basePath, params);
|
|
826
|
+
if (!prepared.ok) {
|
|
827
|
+
return errorResult("save_uat_result", prepared.error.message, prepared.error.code);
|
|
828
|
+
}
|
|
829
|
+
const { run } = prepared;
|
|
1195
830
|
try {
|
|
1196
|
-
const attempt = params.attempt === "auto" || params.attempt === undefined
|
|
1197
|
-
? nextUatAttempt(basePath, params.milestoneId, params.sliceId)
|
|
1198
|
-
: typeof params.attempt === "string"
|
|
1199
|
-
? Number.parseInt(params.attempt, 10)
|
|
1200
|
-
: params.attempt;
|
|
1201
|
-
if (!Number.isInteger(attempt) || attempt < 1) {
|
|
1202
|
-
return errorResult("save_uat_result", "attempt must be a positive integer or auto", "invalid_attempt");
|
|
1203
|
-
}
|
|
1204
|
-
const gateVerdict = params.verdict === "PASS" ? "pass" : "flag";
|
|
1205
|
-
const rationale = params.notes ?? `UAT ${params.verdict} for ${params.sliceId}.`;
|
|
1206
|
-
const assessment = renderUatAssessment(params, attempt, gateVerdict, basePath);
|
|
1207
831
|
const summary = await executeSummarySave({
|
|
1208
|
-
milestone_id: params.milestoneId,
|
|
1209
|
-
slice_id: params.sliceId,
|
|
832
|
+
milestone_id: run.params.milestoneId,
|
|
833
|
+
slice_id: run.params.sliceId,
|
|
1210
834
|
artifact_type: "ASSESSMENT",
|
|
1211
|
-
content: assessment,
|
|
835
|
+
content: run.assessment,
|
|
1212
836
|
}, basePath);
|
|
1213
837
|
if (summary.isError)
|
|
1214
838
|
return summary;
|
|
1215
|
-
const
|
|
1216
|
-
|
|
839
|
+
const assessmentPath = relSliceFile(basePath, run.params.milestoneId, run.params.sliceId, "ASSESSMENT");
|
|
840
|
+
insertAssessment({
|
|
841
|
+
path: assessmentPath,
|
|
842
|
+
milestoneId: run.params.milestoneId,
|
|
843
|
+
sliceId: run.params.sliceId,
|
|
844
|
+
taskId: null,
|
|
845
|
+
status: run.params.verdict.toLowerCase(),
|
|
846
|
+
scope: "run-uat",
|
|
847
|
+
fullContent: run.assessment,
|
|
848
|
+
});
|
|
849
|
+
const attemptPath = await saveUatAttemptArtifact(basePath, run);
|
|
1217
850
|
upsertQualityGate({
|
|
1218
|
-
milestoneId: params.milestoneId,
|
|
1219
|
-
sliceId: params.sliceId,
|
|
851
|
+
milestoneId: run.params.milestoneId,
|
|
852
|
+
sliceId: run.params.sliceId,
|
|
1220
853
|
gateId: "UAT",
|
|
1221
854
|
scope: "slice",
|
|
1222
855
|
taskId: "",
|
|
1223
856
|
status: "complete",
|
|
1224
|
-
verdict: gateVerdict,
|
|
1225
|
-
rationale,
|
|
1226
|
-
findings: assessment,
|
|
1227
|
-
evaluatedAt,
|
|
857
|
+
verdict: run.gateVerdict,
|
|
858
|
+
rationale: run.rationale,
|
|
859
|
+
findings: run.assessment,
|
|
860
|
+
evaluatedAt: run.evaluatedAt,
|
|
1228
861
|
});
|
|
1229
862
|
insertGateRun({
|
|
1230
|
-
traceId: `uat:${params.milestoneId}:${params.sliceId}`,
|
|
1231
|
-
turnId:
|
|
863
|
+
traceId: `uat:${run.params.milestoneId}:${run.params.sliceId}`,
|
|
864
|
+
turnId: run.runId,
|
|
1232
865
|
gateId: "UAT",
|
|
1233
866
|
gateType: "uat",
|
|
1234
867
|
unitType: "run-uat",
|
|
1235
|
-
unitId: `run-uat:${params.milestoneId}/${params.sliceId}`,
|
|
1236
|
-
milestoneId: params.milestoneId,
|
|
1237
|
-
sliceId: params.sliceId,
|
|
1238
|
-
outcome:
|
|
1239
|
-
failureClass: params.verdict === "PASS" ? "none" : "verification",
|
|
1240
|
-
rationale,
|
|
1241
|
-
findings: assessment,
|
|
1242
|
-
attempt,
|
|
1243
|
-
maxAttempts: attempt,
|
|
1244
|
-
retryable: params.verdict !== "PASS",
|
|
1245
|
-
evaluatedAt,
|
|
868
|
+
unitId: `run-uat:${run.params.milestoneId}/${run.params.sliceId}`,
|
|
869
|
+
milestoneId: run.params.milestoneId,
|
|
870
|
+
sliceId: run.params.sliceId,
|
|
871
|
+
outcome: run.gateOutcome,
|
|
872
|
+
failureClass: run.params.verdict === "PASS" ? "none" : "verification",
|
|
873
|
+
rationale: run.rationale,
|
|
874
|
+
findings: run.assessment,
|
|
875
|
+
attempt: run.attempt,
|
|
876
|
+
maxAttempts: run.attempt,
|
|
877
|
+
retryable: run.params.verdict !== "PASS",
|
|
878
|
+
evaluatedAt: run.evaluatedAt,
|
|
1246
879
|
});
|
|
1247
880
|
invalidateStateCache();
|
|
1248
|
-
|
|
1249
|
-
// (often a buried worktree checkout) reaches the reviewer, not just the file.
|
|
1250
|
-
const hasHuman = params.checks.some((check) => check.result === "NEEDS-HUMAN");
|
|
1251
|
-
const manualGuidance = hasHuman
|
|
1252
|
-
? buildManualValidationGuidance(basePath, params.milestoneId, {
|
|
1253
|
-
uatPath: relSliceFile(basePath, params.milestoneId, params.sliceId, "UAT"),
|
|
1254
|
-
})
|
|
1255
|
-
: null;
|
|
1256
|
-
const savedText = `UAT result saved for ${params.milestoneId}/${params.sliceId}: ${params.verdict}`;
|
|
881
|
+
const savedText = `UAT result saved for ${run.params.milestoneId}/${run.params.sliceId}: ${run.params.verdict}`;
|
|
1257
882
|
return {
|
|
1258
883
|
content: [{
|
|
1259
884
|
type: "text",
|
|
1260
|
-
text: manualGuidance ? `${savedText}\n\nManual validation needed:\n${manualGuidance}` : savedText,
|
|
885
|
+
text: run.manualGuidance ? `${savedText}\n\nManual validation needed:\n${run.manualGuidance}` : savedText,
|
|
1261
886
|
}],
|
|
1262
887
|
details: {
|
|
1263
888
|
operation: "save_uat_result",
|
|
1264
|
-
milestoneId: params.milestoneId,
|
|
1265
|
-
sliceId: params.sliceId,
|
|
1266
|
-
verdict: params.verdict,
|
|
1267
|
-
gateVerdict,
|
|
1268
|
-
attempt,
|
|
889
|
+
milestoneId: run.params.milestoneId,
|
|
890
|
+
sliceId: run.params.sliceId,
|
|
891
|
+
verdict: run.params.verdict,
|
|
892
|
+
gateVerdict: run.gateVerdict,
|
|
893
|
+
attempt: run.attempt,
|
|
1269
894
|
attemptPath,
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
895
|
+
runId: run.runId,
|
|
896
|
+
worktreeRoot: run.worktreeRoot,
|
|
897
|
+
browserToolsPresented: run.browserToolsPresented,
|
|
898
|
+
recommendedNextUnit: run.params.verdict === "PASS" ? null : "reactive-execute",
|
|
899
|
+
...(run.hasHuman
|
|
900
|
+
? { manualValidationPath: run.worktreeRoot }
|
|
1273
901
|
: {}),
|
|
1274
902
|
},
|
|
1275
903
|
};
|