@opengsd/gsd-pi 1.1.1-dev.9bb7453 → 1.1.1-dev.9f86580
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/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 +29 -2
- package/dist/resources/extensions/browser-tools/web-app-detect.js +52 -0
- package/dist/resources/extensions/gsd/auto/phases.js +45 -3
- package/dist/resources/extensions/gsd/auto/session.js +2 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +21 -2
- package/dist/resources/extensions/gsd/auto-model-selection.js +26 -0
- package/dist/resources/extensions/gsd/auto-prompts.js +4 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +3 -4
- package/dist/resources/extensions/gsd/auto-timers.js +24 -10
- package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +18 -66
- package/dist/resources/extensions/gsd/auto-worktree.js +18 -5
- package/dist/resources/extensions/gsd/auto.js +26 -4
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +16 -10
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +48 -29
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +18 -29
- package/dist/resources/extensions/gsd/closeout-consistency-gate.js +61 -0
- package/dist/resources/extensions/gsd/commands/handlers/auto.js +10 -0
- package/dist/resources/extensions/gsd/commands-mcp-status.js +1 -1
- 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/guided-flow.js +93 -108
- package/dist/resources/extensions/gsd/milestone-closeout.js +3 -1
- package/dist/resources/extensions/gsd/pending-auto-start.js +0 -1
- package/dist/resources/extensions/gsd/planner-handoff.js +98 -0
- package/dist/resources/extensions/gsd/preferences-models.js +1 -0
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/run-uat.md +5 -19
- package/dist/resources/extensions/gsd/prompts/system.md +1 -1
- package/dist/resources/extensions/gsd/recovery-classification.js +20 -0
- package/dist/resources/extensions/gsd/skill-manifest.js +12 -0
- package/dist/resources/extensions/gsd/tool-contract.js +6 -1
- package/dist/resources/extensions/gsd/tool-presentation-plan.js +47 -7
- package/dist/resources/extensions/gsd/tools/complete-slice.js +28 -1
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +113 -8
- package/dist/resources/extensions/gsd/unit-tool-contracts.js +193 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +5 -78
- package/dist/resources/extensions/gsd/worktree-manager.js +26 -0
- package/dist/resources/extensions/gsd/worktree-reentry.js +96 -0
- package/dist/resources/extensions/shared/gsd-browser-cli.js +6 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +5 -5
- 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 +5 -5
- package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- 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/tool-execution.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +5 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +4 -3
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/dist/harness/agent-harness.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/harness/agent-harness.js +3 -1
- package/packages/pi-agent-core/dist/harness/agent-harness.js.map +1 -1
- package/packages/pi-agent-core/dist/harness/types.d.ts +1 -0
- package/packages/pi-agent-core/dist/harness/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/harness/types.js.map +1 -1
- package/packages/pi-agent-core/dist/types.d.ts +3 -1
- package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/types.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +157 -18
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +159 -36
- 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/extensions/extension-upstream-types.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.js +2 -2
- package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.js +3 -2
- package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/render-utils.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/tools/render-utils.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/render-utils.js +6 -0
- package/packages/pi-coding-agent/dist/core/tools/render-utils.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.js +3 -2
- package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +1 -1
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/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 +36 -5
- package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +2 -2
- 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/web-app-detect.ts +63 -0
- package/src/resources/extensions/gsd/auto/phases.ts +48 -6
- package/src/resources/extensions/gsd/auto/session.ts +2 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +48 -2
- package/src/resources/extensions/gsd/auto-model-selection.ts +26 -0
- package/src/resources/extensions/gsd/auto-prompts.ts +4 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +3 -3
- package/src/resources/extensions/gsd/auto-timers.ts +25 -9
- package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +43 -74
- package/src/resources/extensions/gsd/auto-worktree.ts +23 -5
- package/src/resources/extensions/gsd/auto.ts +28 -4
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +16 -10
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +63 -29
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +50 -54
- package/src/resources/extensions/gsd/closeout-consistency-gate.ts +137 -0
- package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -0
- package/src/resources/extensions/gsd/commands-mcp-status.ts +1 -1
- 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/guided-flow.ts +128 -135
- package/src/resources/extensions/gsd/milestone-closeout.ts +3 -1
- package/src/resources/extensions/gsd/pending-auto-start.ts +0 -2
- package/src/resources/extensions/gsd/planner-handoff.ts +149 -0
- package/src/resources/extensions/gsd/preferences-models.ts +1 -0
- package/src/resources/extensions/gsd/preferences-types.ts +8 -0
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +5 -19
- package/src/resources/extensions/gsd/prompts/system.md +1 -1
- package/src/resources/extensions/gsd/recovery-classification.ts +20 -0
- package/src/resources/extensions/gsd/skill-manifest.ts +12 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +66 -4
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +10 -2
- package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +4 -1
- package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +4 -0
- package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +12 -2
- package/src/resources/extensions/gsd/tests/bundled-skill-triggers.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/check-auto-start-pending-gate.test.ts +9 -15
- package/src/resources/extensions/gsd/tests/check-auto-start-ready-guard.test.ts +26 -16
- package/src/resources/extensions/gsd/tests/commands-dispatcher-unmerged-milestone.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +118 -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 +1 -0
- package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +40 -1
- package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +24 -0
- package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +31 -79
- package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +5 -3
- package/src/resources/extensions/gsd/tests/guided-flow-state-rebuild.test.ts +40 -4
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/interrupted-session-auto.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +1 -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/merge-closeout-consistency-gate.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +10 -1
- package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +9 -1
- package/src/resources/extensions/gsd/tests/planner-handoff.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +147 -5
- 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 +44 -0
- package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +77 -10
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +409 -0
- package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +102 -0
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +15 -0
- package/src/resources/extensions/gsd/tool-contract.ts +7 -1
- package/src/resources/extensions/gsd/tool-presentation-plan.ts +82 -7
- package/src/resources/extensions/gsd/tools/complete-slice.ts +29 -1
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +146 -9
- package/src/resources/extensions/gsd/unit-tool-contracts.ts +210 -0
- package/src/resources/extensions/gsd/workflow-mcp.ts +5 -78
- package/src/resources/extensions/gsd/worktree-manager.ts +32 -0
- package/src/resources/extensions/gsd/worktree-reentry.ts +103 -0
- package/src/resources/extensions/shared/gsd-browser-cli.ts +6 -0
- package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound-corrections.test.ts +0 -246
- package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound.test.ts +0 -218
- /package/dist/web/standalone/.next/static/{jBtwT9v1u2lUA3UEOy_ZH → zzYMrKpPGfRQRxSFO32Jr}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{jBtwT9v1u2lUA3UEOy_ZH → zzYMrKpPGfRQRxSFO32Jr}/_ssgManifest.js +0 -0
|
@@ -101,7 +101,7 @@ test("buildMinimalAutoGsdToolSet keeps unit-specific completion tools without al
|
|
|
101
101
|
assert.ok(!result.includes("gsd_complete_slice"));
|
|
102
102
|
});
|
|
103
103
|
|
|
104
|
-
test("buildMinimalAutoGsdToolSet scopes run-uat to UAT-specific tools", () => {
|
|
104
|
+
test("buildMinimalAutoGsdToolSet scopes run-uat to UAT-specific and read-only tools", () => {
|
|
105
105
|
const active = ["ask_user_questions", "bash", "read", "edit", "write", "gsd_summary_save"];
|
|
106
106
|
const registered = [
|
|
107
107
|
...active,
|
|
@@ -123,11 +123,11 @@ test("buildMinimalAutoGsdToolSet scopes run-uat to UAT-specific tools", () => {
|
|
|
123
123
|
assert.ok(result.includes("gsd_resume"));
|
|
124
124
|
assert.ok(result.includes("gsd_milestone_status"));
|
|
125
125
|
assert.ok(result.includes("gsd_journal_query"));
|
|
126
|
+
assert.ok(result.includes("read"));
|
|
126
127
|
assert.ok(result.includes("browser_navigate"), "run-uat needs browser_navigate");
|
|
127
128
|
assert.ok(result.includes("browser_click"), "run-uat needs browser_click");
|
|
128
129
|
assert.ok(!result.includes("ToolSearch"));
|
|
129
130
|
assert.ok(!result.includes("bash"));
|
|
130
|
-
assert.ok(!result.includes("read"));
|
|
131
131
|
assert.ok(!result.includes("edit"));
|
|
132
132
|
assert.ok(!result.includes("write"));
|
|
133
133
|
assert.ok(!result.includes("gsd_exec"));
|
|
@@ -230,9 +230,9 @@ test("buildMinimalAutoGsdToolSet preserves compatible browser add-ons for run-ua
|
|
|
230
230
|
assert.ok(result.includes("gsd_uat_exec"));
|
|
231
231
|
assert.ok(result.includes("gsd_uat_result_save"));
|
|
232
232
|
assert.ok(result.includes("subagent"));
|
|
233
|
+
assert.ok(result.includes("read"));
|
|
233
234
|
assert.ok(!result.includes("ToolSearch"));
|
|
234
235
|
assert.ok(!result.includes("bash"));
|
|
235
|
-
assert.ok(!result.includes("read"));
|
|
236
236
|
assert.ok(!result.includes("edit"));
|
|
237
237
|
assert.ok(!result.includes("write"));
|
|
238
238
|
assert.ok(!result.includes("gsd_exec"));
|
|
@@ -281,12 +281,12 @@ test("buildMinimalAutoGsdToolSet honors provider-compatible registered tools for
|
|
|
281
281
|
|
|
282
282
|
assert.ok(result.includes("gsd_uat_exec"));
|
|
283
283
|
assert.ok(result.includes("gsd_uat_result_save"));
|
|
284
|
+
assert.ok(result.includes("read"));
|
|
284
285
|
assert.ok(result.includes("browser_navigate"));
|
|
285
286
|
assert.ok(result.includes("browser_click"));
|
|
286
287
|
assert.ok(!result.includes("browser_screenshot"), "provider-filtered screenshot tool must stay filtered");
|
|
287
288
|
assert.ok(!result.includes("ToolSearch"));
|
|
288
289
|
assert.ok(!result.includes("bash"));
|
|
289
|
-
assert.ok(!result.includes("read"));
|
|
290
290
|
assert.ok(!result.includes("gsd_exec"));
|
|
291
291
|
assert.ok(!result.includes("gsd_summary_save"));
|
|
292
292
|
assert.ok(!result.includes("gsd_save_gate_result"));
|
|
@@ -48,12 +48,52 @@ test("auto execute-task requires canonical task completion tool", () => {
|
|
|
48
48
|
assert.deepEqual(getRequiredWorkflowToolsForAutoUnit("execute-task"), ["gsd_task_complete"]);
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
+
test("plan-slice requires planning and roadmap reassessment tools", () => {
|
|
52
|
+
const expected = ["gsd_plan_slice", "gsd_reassess_roadmap"];
|
|
53
|
+
assert.deepEqual(getRequiredWorkflowToolsForGuidedUnit("plan-slice"), expected);
|
|
54
|
+
assert.deepEqual(getRequiredWorkflowToolsForAutoUnit("plan-slice"), expected);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("plan-milestone requires status, roadmap, and single-slice planning tools", () => {
|
|
58
|
+
const expected = ["gsd_milestone_status", "gsd_plan_milestone", "gsd_plan_slice"];
|
|
59
|
+
assert.deepEqual(getRequiredWorkflowToolsForGuidedUnit("plan-milestone"), expected);
|
|
60
|
+
assert.deepEqual(getRequiredWorkflowToolsForAutoUnit("plan-milestone"), expected);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("refine-slice requires canonical slice planning tool", () => {
|
|
64
|
+
assert.deepEqual(getRequiredWorkflowToolsForGuidedUnit("refine-slice"), ["gsd_plan_slice"]);
|
|
65
|
+
assert.deepEqual(getRequiredWorkflowToolsForAutoUnit("refine-slice"), ["gsd_plan_slice"]);
|
|
66
|
+
});
|
|
67
|
+
|
|
51
68
|
test("complete-slice requires closeout and execution handoff tools", () => {
|
|
52
|
-
const expected = [
|
|
69
|
+
const expected = [
|
|
70
|
+
"gsd_slice_complete",
|
|
71
|
+
"gsd_task_reopen",
|
|
72
|
+
"gsd_replan_slice",
|
|
73
|
+
"gsd_requirement_update",
|
|
74
|
+
"gsd_summary_save",
|
|
75
|
+
];
|
|
53
76
|
assert.deepEqual(getRequiredWorkflowToolsForGuidedUnit("complete-slice"), expected);
|
|
54
77
|
assert.deepEqual(getRequiredWorkflowToolsForAutoUnit("complete-slice"), expected);
|
|
55
78
|
});
|
|
56
79
|
|
|
80
|
+
test("complete-milestone requires status, requirement, project refresh, and closeout tools", () => {
|
|
81
|
+
const expected = [
|
|
82
|
+
"gsd_milestone_status",
|
|
83
|
+
"gsd_requirement_update",
|
|
84
|
+
"gsd_summary_save",
|
|
85
|
+
"gsd_complete_milestone",
|
|
86
|
+
];
|
|
87
|
+
assert.deepEqual(getRequiredWorkflowToolsForGuidedUnit("complete-milestone"), expected);
|
|
88
|
+
assert.deepEqual(getRequiredWorkflowToolsForAutoUnit("complete-milestone"), expected);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("reactive-execute requires task completion and failed-task summary tools", () => {
|
|
92
|
+
const expected = ["gsd_task_complete", "gsd_summary_save"];
|
|
93
|
+
assert.deepEqual(getRequiredWorkflowToolsForGuidedUnit("reactive-execute"), expected);
|
|
94
|
+
assert.deepEqual(getRequiredWorkflowToolsForAutoUnit("reactive-execute"), expected);
|
|
95
|
+
});
|
|
96
|
+
|
|
57
97
|
test("workflow MCP capability surface includes native legacy gsd aliases", () => {
|
|
58
98
|
const err = getWorkflowTransportSupportError(
|
|
59
99
|
"claude-code",
|
|
@@ -679,7 +719,7 @@ test("transport compatibility ignores API-backed providers", () => {
|
|
|
679
719
|
test("transport compatibility now allows plan-slice over workflow MCP surface", () => {
|
|
680
720
|
const error = getWorkflowTransportSupportError(
|
|
681
721
|
"claude-code",
|
|
682
|
-
|
|
722
|
+
getRequiredWorkflowToolsForAutoUnit("plan-slice"),
|
|
683
723
|
{
|
|
684
724
|
projectRoot: "/tmp/project",
|
|
685
725
|
env: { GSD_WORKFLOW_MCP_COMMAND: "node" },
|
|
@@ -696,7 +736,7 @@ test("transport compatibility now allows plan-slice over workflow MCP surface",
|
|
|
696
736
|
test("transport compatibility now allows complete-slice over workflow MCP surface", () => {
|
|
697
737
|
const error = getWorkflowTransportSupportError(
|
|
698
738
|
"claude-code",
|
|
699
|
-
|
|
739
|
+
getRequiredWorkflowToolsForAutoUnit("complete-slice"),
|
|
700
740
|
{
|
|
701
741
|
projectRoot: "/tmp/project",
|
|
702
742
|
env: { GSD_WORKFLOW_MCP_COMMAND: "node" },
|
|
@@ -747,7 +787,7 @@ test("transport compatibility now allows gate-evaluate over workflow MCP surface
|
|
|
747
787
|
test("transport compatibility now allows validate-milestone over workflow MCP surface", () => {
|
|
748
788
|
const error = getWorkflowTransportSupportError(
|
|
749
789
|
"claude-code",
|
|
750
|
-
|
|
790
|
+
getRequiredWorkflowToolsForAutoUnit("validate-milestone"),
|
|
751
791
|
{
|
|
752
792
|
projectRoot: "/tmp/project",
|
|
753
793
|
env: { GSD_WORKFLOW_MCP_COMMAND: "node" },
|
|
@@ -764,7 +804,7 @@ test("transport compatibility now allows validate-milestone over workflow MCP su
|
|
|
764
804
|
test("transport compatibility now allows complete-milestone over workflow MCP surface", () => {
|
|
765
805
|
const error = getWorkflowTransportSupportError(
|
|
766
806
|
"claude-code",
|
|
767
|
-
|
|
807
|
+
getRequiredWorkflowToolsForAutoUnit("complete-milestone"),
|
|
768
808
|
{
|
|
769
809
|
projectRoot: "/tmp/project",
|
|
770
810
|
env: { GSD_WORKFLOW_MCP_COMMAND: "node" },
|
|
@@ -795,7 +835,7 @@ test("transport compatibility now allows replan-slice over workflow MCP surface"
|
|
|
795
835
|
assert.equal(error, null);
|
|
796
836
|
});
|
|
797
837
|
|
|
798
|
-
test("transport compatibility
|
|
838
|
+
test("transport compatibility rejects MCP tools not connected in active tool surface", () => {
|
|
799
839
|
const error = getWorkflowTransportSupportError(
|
|
800
840
|
"claude-code",
|
|
801
841
|
["gsd_summary_save"],
|
|
@@ -810,10 +850,10 @@ test("transport compatibility accepts workflow MCP tools absent from parent acti
|
|
|
810
850
|
},
|
|
811
851
|
);
|
|
812
852
|
|
|
813
|
-
assert.
|
|
853
|
+
assert.match(error ?? "", /requires gsd_summary_save/);
|
|
814
854
|
});
|
|
815
855
|
|
|
816
|
-
test("transport compatibility
|
|
856
|
+
test("transport compatibility checks all required tools against active tool surface", () => {
|
|
817
857
|
const error = getWorkflowTransportSupportError(
|
|
818
858
|
"claude-code",
|
|
819
859
|
["gsd_summary_save", "secure_env_collect"],
|
|
@@ -828,8 +868,9 @@ test("transport compatibility still checks non-MCP tools against parent active t
|
|
|
828
868
|
},
|
|
829
869
|
);
|
|
830
870
|
|
|
831
|
-
assert.match(error ?? "", /requires
|
|
832
|
-
assert.
|
|
871
|
+
assert.match(error ?? "", /requires.*(?:gsd_summary_save|secure_env_collect)/);
|
|
872
|
+
assert.match(error ?? "", /gsd_summary_save/);
|
|
873
|
+
assert.match(error ?? "", /secure_env_collect/);
|
|
833
874
|
});
|
|
834
875
|
|
|
835
876
|
test("transport compatibility still blocks units whose MCP tools are not exposed", () => {
|
|
@@ -850,6 +891,32 @@ test("transport compatibility still blocks units whose MCP tools are not exposed
|
|
|
850
891
|
assert.match(error ?? "", /currently exposes only/);
|
|
851
892
|
});
|
|
852
893
|
|
|
894
|
+
test("discuss-milestone guided flow does not abort when all required tools are on MCP surface (regression #469)", () => {
|
|
895
|
+
// Guided flow starts the workflow MCP server as part of dispatch, so the
|
|
896
|
+
// parent session active-tool list is not authoritative for MCP tools.
|
|
897
|
+
const discussMilestoneTools = [
|
|
898
|
+
"gsd_summary_save",
|
|
899
|
+
"gsd_requirement_save",
|
|
900
|
+
"gsd_requirement_update",
|
|
901
|
+
"gsd_plan_milestone",
|
|
902
|
+
"gsd_milestone_generate_id",
|
|
903
|
+
];
|
|
904
|
+
const error = getWorkflowTransportSupportError(
|
|
905
|
+
"claude-code",
|
|
906
|
+
discussMilestoneTools,
|
|
907
|
+
{
|
|
908
|
+
projectRoot: "/tmp/project",
|
|
909
|
+
env: { GSD_WORKFLOW_MCP_COMMAND: "node" },
|
|
910
|
+
surface: "guided flow",
|
|
911
|
+
unitType: "discuss-milestone",
|
|
912
|
+
authMode: "externalCli",
|
|
913
|
+
baseUrl: "local://claude-code",
|
|
914
|
+
},
|
|
915
|
+
);
|
|
916
|
+
|
|
917
|
+
assert.equal(error, null);
|
|
918
|
+
});
|
|
919
|
+
|
|
853
920
|
test("transport compatibility accepts MCP-namespaced runtime tools", () => {
|
|
854
921
|
const error = getWorkflowTransportSupportError(
|
|
855
922
|
"claude-code",
|
|
@@ -75,6 +75,11 @@ test("closeout executors reject phase escalation from the wrong active auto unit
|
|
|
75
75
|
const milestone = await executeCompleteMilestone({} as Parameters<typeof executeCompleteMilestone>[0], "/tmp/project");
|
|
76
76
|
assert.equal(milestone.isError, true);
|
|
77
77
|
assert.match(String(milestone.details.error), /complete_milestone may only run from complete-milestone/);
|
|
78
|
+
|
|
79
|
+
const uat = await executeUatResultSave({} as Parameters<typeof executeUatResultSave>[0], "/tmp/project");
|
|
80
|
+
assert.equal(uat.isError, true);
|
|
81
|
+
assert.match(String(uat.details.error), /save_uat_result may only run from run-uat/);
|
|
82
|
+
assert.match(String(uat.details.error), /Tool Contract failure/);
|
|
78
83
|
} finally {
|
|
79
84
|
autoSession.reset();
|
|
80
85
|
}
|
|
@@ -643,6 +648,398 @@ test("executeUatResultSave accepts gsd_uat_exec evidence written in a milestone
|
|
|
643
648
|
}
|
|
644
649
|
});
|
|
645
650
|
|
|
651
|
+
test("executeUatResultSave supplies canonical presentation and normalizes verdict casing", async () => {
|
|
652
|
+
const base = makeTmpBase();
|
|
653
|
+
const worktree = join(base, ".gsd", "worktrees", "M001");
|
|
654
|
+
const worktreeExecDir = join(worktree, ".gsd", "exec");
|
|
655
|
+
const evidenceId = "uat-lowercase-verdict";
|
|
656
|
+
try {
|
|
657
|
+
openTestDb(base);
|
|
658
|
+
seedMilestone("M001", "Milestone One");
|
|
659
|
+
seedSlice("M001", "S03", "complete");
|
|
660
|
+
mkdirSync(worktreeExecDir, { recursive: true });
|
|
661
|
+
writeFileSync(
|
|
662
|
+
join(worktreeExecDir, `${evidenceId}.meta.json`),
|
|
663
|
+
JSON.stringify({
|
|
664
|
+
id: evidenceId,
|
|
665
|
+
metadata: {
|
|
666
|
+
kind: "uat_exec",
|
|
667
|
+
milestoneId: "M001",
|
|
668
|
+
sliceId: "S03",
|
|
669
|
+
checkId: "UAT-01",
|
|
670
|
+
intent: "uat-artifact-check",
|
|
671
|
+
},
|
|
672
|
+
}),
|
|
673
|
+
"utf-8",
|
|
674
|
+
);
|
|
675
|
+
|
|
676
|
+
const result = await inProjectDir(worktree, () => executeUatResultSave({
|
|
677
|
+
milestoneId: "M001",
|
|
678
|
+
sliceId: "S03",
|
|
679
|
+
uatType: "artifact-driven",
|
|
680
|
+
verdict: "pass",
|
|
681
|
+
checks: [{
|
|
682
|
+
id: "UAT-01",
|
|
683
|
+
description: "Static artifact contract passes",
|
|
684
|
+
mode: "artifact",
|
|
685
|
+
result: "PASS",
|
|
686
|
+
evidence: [{ kind: "gsd_uat_exec", ref: evidenceId }],
|
|
687
|
+
notes: "Artifact check passed.",
|
|
688
|
+
}],
|
|
689
|
+
notes: "UAT passed with canonical presentation supplied by the executor.",
|
|
690
|
+
} as unknown as Parameters<typeof executeUatResultSave>[0], worktree));
|
|
691
|
+
|
|
692
|
+
assert.equal(result.isError, undefined);
|
|
693
|
+
assert.equal(result.details.verdict, "PASS");
|
|
694
|
+
|
|
695
|
+
const attempt = JSON.parse(readFileSync(
|
|
696
|
+
join(base, ".gsd", "uat", "M001", "S03", "attempt-1.json"),
|
|
697
|
+
"utf-8",
|
|
698
|
+
)) as { presentation?: { toolPresentationPlanId?: string; presentedTools?: string[] } };
|
|
699
|
+
assert.equal(attempt.presentation?.toolPresentationPlanId, "run-uat/default-v1");
|
|
700
|
+
assert.ok(attempt.presentation?.presentedTools?.includes("gsd_uat_result_save"));
|
|
701
|
+
assert.ok(attempt.presentation?.presentedTools?.includes("read"));
|
|
702
|
+
} finally {
|
|
703
|
+
closeDatabase();
|
|
704
|
+
cleanup(base);
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
test("executeUatResultSave supplies direct browser tools for browser-executable UAT", async () => {
|
|
709
|
+
const base = makeTmpBase();
|
|
710
|
+
const worktree = join(base, ".gsd", "worktrees", "M001");
|
|
711
|
+
const worktreeExecDir = join(worktree, ".gsd", "exec");
|
|
712
|
+
const evidenceId = "uat-direct-browser-evidence";
|
|
713
|
+
try {
|
|
714
|
+
openTestDb(base);
|
|
715
|
+
seedMilestone("M001", "Milestone One");
|
|
716
|
+
seedSlice("M001", "S06", "complete");
|
|
717
|
+
mkdirSync(worktreeExecDir, { recursive: true });
|
|
718
|
+
writeFileSync(
|
|
719
|
+
join(worktreeExecDir, `${evidenceId}.meta.json`),
|
|
720
|
+
JSON.stringify({
|
|
721
|
+
id: evidenceId,
|
|
722
|
+
metadata: {
|
|
723
|
+
kind: "uat_exec",
|
|
724
|
+
milestoneId: "M001",
|
|
725
|
+
sliceId: "S06",
|
|
726
|
+
checkId: "UAT-01",
|
|
727
|
+
intent: "uat-browser-check",
|
|
728
|
+
},
|
|
729
|
+
}),
|
|
730
|
+
"utf-8",
|
|
731
|
+
);
|
|
732
|
+
|
|
733
|
+
const result = await inProjectDir(worktree, () => executeUatResultSave({
|
|
734
|
+
milestoneId: "M001",
|
|
735
|
+
sliceId: "S06",
|
|
736
|
+
uatType: "browser-executable",
|
|
737
|
+
verdict: "PASS",
|
|
738
|
+
checks: [{
|
|
739
|
+
id: "UAT-01",
|
|
740
|
+
description: "Browser flow used browser tools",
|
|
741
|
+
mode: "browser",
|
|
742
|
+
result: "PASS",
|
|
743
|
+
evidence: [{ kind: "gsd_uat_exec", ref: evidenceId }],
|
|
744
|
+
notes: "Browser check passed.",
|
|
745
|
+
}],
|
|
746
|
+
notes: "UAT passed with browser evidence.",
|
|
747
|
+
} as unknown as Parameters<typeof executeUatResultSave>[0], worktree));
|
|
748
|
+
|
|
749
|
+
assert.equal(result.isError, undefined);
|
|
750
|
+
const attempt = JSON.parse(readFileSync(
|
|
751
|
+
join(base, ".gsd", "uat", "M001", "S06", "attempt-1.json"),
|
|
752
|
+
"utf-8",
|
|
753
|
+
)) as { presentation?: { presentedTools?: string[] } };
|
|
754
|
+
|
|
755
|
+
assert.ok(attempt.presentation?.presentedTools?.includes("browser_navigate"));
|
|
756
|
+
assert.ok(attempt.presentation?.presentedTools?.includes("browser_assert"));
|
|
757
|
+
assert.equal(
|
|
758
|
+
attempt.presentation?.presentedTools?.some((toolName) => toolName.startsWith("mcp__gsd-browser__")),
|
|
759
|
+
false,
|
|
760
|
+
);
|
|
761
|
+
} finally {
|
|
762
|
+
closeDatabase();
|
|
763
|
+
cleanup(base);
|
|
764
|
+
}
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
test("executeUatResultSave merges canonical plan ID and read-only tools when presentation lacks plan ID", async () => {
|
|
768
|
+
const base = makeTmpBase();
|
|
769
|
+
const worktree = join(base, ".gsd", "worktrees", "M001");
|
|
770
|
+
const worktreeExecDir = join(worktree, ".gsd", "exec");
|
|
771
|
+
const evidenceId = "uat-no-plan-id-evidence";
|
|
772
|
+
try {
|
|
773
|
+
openTestDb(base);
|
|
774
|
+
seedMilestone("M001", "Milestone One");
|
|
775
|
+
seedSlice("M001", "S05", "complete");
|
|
776
|
+
mkdirSync(worktreeExecDir, { recursive: true });
|
|
777
|
+
writeFileSync(
|
|
778
|
+
join(worktreeExecDir, `${evidenceId}.meta.json`),
|
|
779
|
+
JSON.stringify({
|
|
780
|
+
id: evidenceId,
|
|
781
|
+
metadata: {
|
|
782
|
+
kind: "uat_exec",
|
|
783
|
+
milestoneId: "M001",
|
|
784
|
+
sliceId: "S05",
|
|
785
|
+
checkId: "UAT-01",
|
|
786
|
+
intent: "uat-artifact-check",
|
|
787
|
+
},
|
|
788
|
+
}),
|
|
789
|
+
"utf-8",
|
|
790
|
+
);
|
|
791
|
+
|
|
792
|
+
const result = await inProjectDir(worktree, () => executeUatResultSave({
|
|
793
|
+
milestoneId: "M001",
|
|
794
|
+
sliceId: "S05",
|
|
795
|
+
uatType: "artifact-driven",
|
|
796
|
+
verdict: "PASS",
|
|
797
|
+
checks: [{
|
|
798
|
+
id: "UAT-01",
|
|
799
|
+
description: "Presentation plan ID absent from provider call",
|
|
800
|
+
mode: "artifact",
|
|
801
|
+
result: "PASS",
|
|
802
|
+
evidence: [{ kind: "gsd_uat_exec", ref: evidenceId }],
|
|
803
|
+
notes: "Canonical merge should apply even when toolPresentationPlanId is absent.",
|
|
804
|
+
}],
|
|
805
|
+
presentation: {
|
|
806
|
+
surface: "mcp",
|
|
807
|
+
presentedTools: [
|
|
808
|
+
"gsd_uat_exec",
|
|
809
|
+
"gsd_uat_result_save",
|
|
810
|
+
"gsd_resume",
|
|
811
|
+
"gsd_milestone_status",
|
|
812
|
+
"gsd_journal_query",
|
|
813
|
+
],
|
|
814
|
+
blockedTools: [
|
|
815
|
+
{ name: "gsd_exec", reason: "forbidden during run-uat" },
|
|
816
|
+
{ name: "gsd_summary_save", reason: "forbidden during run-uat" },
|
|
817
|
+
{ name: "gsd_save_gate_result", reason: "forbidden during run-uat" },
|
|
818
|
+
],
|
|
819
|
+
},
|
|
820
|
+
notes: "Provider omitted toolPresentationPlanId; executor must canonicalize.",
|
|
821
|
+
} as unknown as Parameters<typeof executeUatResultSave>[0], worktree));
|
|
822
|
+
|
|
823
|
+
assert.equal(result.isError, undefined);
|
|
824
|
+
assert.equal(result.details.verdict, "PASS");
|
|
825
|
+
|
|
826
|
+
const attempt = JSON.parse(readFileSync(
|
|
827
|
+
join(base, ".gsd", "uat", "M001", "S05", "attempt-1.json"),
|
|
828
|
+
"utf-8",
|
|
829
|
+
)) as { presentation?: { toolPresentationPlanId?: string; presentedTools?: string[] } };
|
|
830
|
+
assert.equal(attempt.presentation?.toolPresentationPlanId, "run-uat/default-v1");
|
|
831
|
+
assert.ok(attempt.presentation?.presentedTools?.includes("read"), "read-only tool must be merged in");
|
|
832
|
+
assert.ok(attempt.presentation?.presentedTools?.includes("gsd_uat_result_save"));
|
|
833
|
+
} finally {
|
|
834
|
+
closeDatabase();
|
|
835
|
+
cleanup(base);
|
|
836
|
+
}
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
test("executeUatResultSave surfaces the worktree validation path for NEEDS-HUMAN checks", async () => {
|
|
840
|
+
const base = makeTmpBase();
|
|
841
|
+
const worktree = join(base, ".gsd", "worktrees", "M001");
|
|
842
|
+
const worktreeExecDir = join(worktree, ".gsd", "exec");
|
|
843
|
+
const evidenceId = "uat-human-validation-evidence";
|
|
844
|
+
try {
|
|
845
|
+
openTestDb(base);
|
|
846
|
+
seedMilestone("M001", "Milestone One");
|
|
847
|
+
seedSlice("M001", "S07", "complete");
|
|
848
|
+
mkdirSync(worktreeExecDir, { recursive: true });
|
|
849
|
+
writeFileSync(
|
|
850
|
+
join(worktreeExecDir, `${evidenceId}.meta.json`),
|
|
851
|
+
JSON.stringify({
|
|
852
|
+
id: evidenceId,
|
|
853
|
+
metadata: {
|
|
854
|
+
kind: "uat_exec",
|
|
855
|
+
milestoneId: "M001",
|
|
856
|
+
sliceId: "S07",
|
|
857
|
+
checkId: "UAT-01",
|
|
858
|
+
intent: "uat-runtime-check",
|
|
859
|
+
},
|
|
860
|
+
}),
|
|
861
|
+
"utf-8",
|
|
862
|
+
);
|
|
863
|
+
|
|
864
|
+
const result = await inProjectDir(worktree, () => executeUatResultSave({
|
|
865
|
+
milestoneId: "M001",
|
|
866
|
+
sliceId: "S07",
|
|
867
|
+
uatType: "human-experience",
|
|
868
|
+
verdict: "PASS",
|
|
869
|
+
checks: [
|
|
870
|
+
{
|
|
871
|
+
id: "UAT-01",
|
|
872
|
+
description: "Service boots and renders the dashboard",
|
|
873
|
+
mode: "runtime",
|
|
874
|
+
result: "PASS",
|
|
875
|
+
evidence: [{ kind: "gsd_uat_exec", ref: evidenceId }],
|
|
876
|
+
notes: "Boot check passed.",
|
|
877
|
+
},
|
|
878
|
+
{
|
|
879
|
+
id: "UAT-02",
|
|
880
|
+
description: "Dashboard layout feels balanced",
|
|
881
|
+
mode: "human-follow-up",
|
|
882
|
+
result: "NEEDS-HUMAN",
|
|
883
|
+
nonAutomatable: true,
|
|
884
|
+
notes: "Open the app and eyeball the spacing.",
|
|
885
|
+
},
|
|
886
|
+
],
|
|
887
|
+
notes: "Automatable checks passed; layout taste needs a human.",
|
|
888
|
+
} as unknown as Parameters<typeof executeUatResultSave>[0], worktree));
|
|
889
|
+
|
|
890
|
+
assert.equal(result.isError, undefined);
|
|
891
|
+
assert.equal(result.details.verdict, "PASS");
|
|
892
|
+
// The reviewer needs the buried worktree checkout path, not just the file.
|
|
893
|
+
assert.equal(result.details.manualValidationPath, worktree);
|
|
894
|
+
const returnedText = (result.content[0] as { text: string }).text;
|
|
895
|
+
assert.match(returnedText, /Manual validation needed/);
|
|
896
|
+
assert.ok(returnedText.includes(worktree), "tool return should include the worktree path");
|
|
897
|
+
|
|
898
|
+
const assessment = readFileSync(
|
|
899
|
+
join(base, ".gsd", "milestones", "M001", "slices", "S07", "S07-ASSESSMENT.md"),
|
|
900
|
+
"utf-8",
|
|
901
|
+
);
|
|
902
|
+
assert.match(assessment, /## Manual Validation/);
|
|
903
|
+
assert.ok(assessment.includes(worktree), "assessment should include the worktree checkout path");
|
|
904
|
+
assert.match(assessment, /git worktree/);
|
|
905
|
+
} finally {
|
|
906
|
+
closeDatabase();
|
|
907
|
+
cleanup(base);
|
|
908
|
+
}
|
|
909
|
+
});
|
|
910
|
+
|
|
911
|
+
test("executeUatResultSave omits manual-validation guidance when no human checks remain", async () => {
|
|
912
|
+
const base = makeTmpBase();
|
|
913
|
+
const worktree = join(base, ".gsd", "worktrees", "M001");
|
|
914
|
+
const worktreeExecDir = join(worktree, ".gsd", "exec");
|
|
915
|
+
const evidenceId = "uat-no-human-evidence";
|
|
916
|
+
try {
|
|
917
|
+
openTestDb(base);
|
|
918
|
+
seedMilestone("M001", "Milestone One");
|
|
919
|
+
seedSlice("M001", "S08", "complete");
|
|
920
|
+
mkdirSync(worktreeExecDir, { recursive: true });
|
|
921
|
+
writeFileSync(
|
|
922
|
+
join(worktreeExecDir, `${evidenceId}.meta.json`),
|
|
923
|
+
JSON.stringify({
|
|
924
|
+
id: evidenceId,
|
|
925
|
+
metadata: {
|
|
926
|
+
kind: "uat_exec",
|
|
927
|
+
milestoneId: "M001",
|
|
928
|
+
sliceId: "S08",
|
|
929
|
+
checkId: "UAT-01",
|
|
930
|
+
intent: "uat-artifact-check",
|
|
931
|
+
},
|
|
932
|
+
}),
|
|
933
|
+
"utf-8",
|
|
934
|
+
);
|
|
935
|
+
|
|
936
|
+
const result = await inProjectDir(worktree, () => executeUatResultSave({
|
|
937
|
+
milestoneId: "M001",
|
|
938
|
+
sliceId: "S08",
|
|
939
|
+
uatType: "artifact-driven",
|
|
940
|
+
verdict: "PASS",
|
|
941
|
+
checks: [{
|
|
942
|
+
id: "UAT-01",
|
|
943
|
+
description: "Config file exists",
|
|
944
|
+
mode: "artifact",
|
|
945
|
+
result: "PASS",
|
|
946
|
+
evidence: [{ kind: "gsd_uat_exec", ref: evidenceId }],
|
|
947
|
+
notes: "Artifact present.",
|
|
948
|
+
}],
|
|
949
|
+
notes: "Fully automated pass.",
|
|
950
|
+
} as unknown as Parameters<typeof executeUatResultSave>[0], worktree));
|
|
951
|
+
|
|
952
|
+
assert.equal(result.isError, undefined);
|
|
953
|
+
assert.equal(result.details.manualValidationPath, undefined);
|
|
954
|
+
const returnedText = (result.content[0] as { text: string }).text;
|
|
955
|
+
assert.equal(returnedText.includes("Manual validation needed"), false);
|
|
956
|
+
|
|
957
|
+
const assessment = readFileSync(
|
|
958
|
+
join(base, ".gsd", "milestones", "M001", "slices", "S08", "S08-ASSESSMENT.md"),
|
|
959
|
+
"utf-8",
|
|
960
|
+
);
|
|
961
|
+
assert.equal(assessment.includes("## Manual Validation"), false);
|
|
962
|
+
} finally {
|
|
963
|
+
closeDatabase();
|
|
964
|
+
cleanup(base);
|
|
965
|
+
}
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
test("executeUatResultSave rejects saved UAT without fresh UAT-owned evidence", async () => {
|
|
969
|
+
const base = makeTmpBase();
|
|
970
|
+
const worktree = join(base, ".gsd", "worktrees", "M001");
|
|
971
|
+
const worktreeExecDir = join(worktree, ".gsd", "exec");
|
|
972
|
+
const evidenceId = "generic-exec-evidence";
|
|
973
|
+
try {
|
|
974
|
+
openTestDb(base);
|
|
975
|
+
seedMilestone("M001", "Milestone One");
|
|
976
|
+
seedSlice("M001", "S04", "complete");
|
|
977
|
+
mkdirSync(worktreeExecDir, { recursive: true });
|
|
978
|
+
writeFileSync(
|
|
979
|
+
join(worktreeExecDir, `${evidenceId}.meta.json`),
|
|
980
|
+
JSON.stringify({
|
|
981
|
+
id: evidenceId,
|
|
982
|
+
metadata: { kind: "exec" },
|
|
983
|
+
}),
|
|
984
|
+
"utf-8",
|
|
985
|
+
);
|
|
986
|
+
|
|
987
|
+
const result = await inProjectDir(worktree, () => executeUatResultSave({
|
|
988
|
+
milestoneId: "M001",
|
|
989
|
+
sliceId: "S04",
|
|
990
|
+
uatType: "artifact-driven",
|
|
991
|
+
verdict: "PASS",
|
|
992
|
+
checks: [{
|
|
993
|
+
id: "UAT-01",
|
|
994
|
+
description: "Static artifact contract passes",
|
|
995
|
+
mode: "artifact",
|
|
996
|
+
result: "PASS",
|
|
997
|
+
evidence: [{ kind: "gsd_exec", ref: evidenceId }],
|
|
998
|
+
notes: "Generic evidence should not satisfy fresh UAT evidence.",
|
|
999
|
+
}],
|
|
1000
|
+
notes: "UAT should not pass without fresh UAT-owned evidence.",
|
|
1001
|
+
} as unknown as Parameters<typeof executeUatResultSave>[0], worktree));
|
|
1002
|
+
|
|
1003
|
+
assert.equal(result.isError, true);
|
|
1004
|
+
assert.match(String(result.content[0]?.text), /fresh gsd_uat_exec evidence/);
|
|
1005
|
+
} finally {
|
|
1006
|
+
closeDatabase();
|
|
1007
|
+
cleanup(base);
|
|
1008
|
+
}
|
|
1009
|
+
});
|
|
1010
|
+
|
|
1011
|
+
test("executeUatResultSave rejects an unrecognized uatType", async () => {
|
|
1012
|
+
const base = makeTmpBase();
|
|
1013
|
+
const worktree = join(base, ".gsd", "worktrees", "M001");
|
|
1014
|
+
try {
|
|
1015
|
+
openTestDb(base);
|
|
1016
|
+
mkdirSync(worktree, { recursive: true });
|
|
1017
|
+
seedMilestone("M001", "Milestone One");
|
|
1018
|
+
seedSlice("M001", "S06", "complete");
|
|
1019
|
+
|
|
1020
|
+
const result = await inProjectDir(worktree, () => executeUatResultSave({
|
|
1021
|
+
milestoneId: "M001",
|
|
1022
|
+
sliceId: "S06",
|
|
1023
|
+
uatType: "hallucinated-mode",
|
|
1024
|
+
verdict: "PASS",
|
|
1025
|
+
checks: [{
|
|
1026
|
+
id: "UAT-01",
|
|
1027
|
+
description: "Static artifact contract passes",
|
|
1028
|
+
mode: "artifact",
|
|
1029
|
+
result: "PASS",
|
|
1030
|
+
evidence: [{ kind: "gsd_uat_exec", ref: "some-ref" }],
|
|
1031
|
+
}],
|
|
1032
|
+
notes: "Should fail before evidence validation.",
|
|
1033
|
+
} as unknown as Parameters<typeof executeUatResultSave>[0], worktree));
|
|
1034
|
+
|
|
1035
|
+
assert.equal(result.isError, true);
|
|
1036
|
+
assert.match(String(result.content[0]?.text), /uatType must be one of/);
|
|
1037
|
+
} finally {
|
|
1038
|
+
closeDatabase();
|
|
1039
|
+
cleanup(base);
|
|
1040
|
+
}
|
|
1041
|
+
});
|
|
1042
|
+
|
|
646
1043
|
test("executeUatResultSave rejects artifact-driven PASS with human follow-up checks", async () => {
|
|
647
1044
|
const base = makeTmpBase();
|
|
648
1045
|
const worktree = join(base, ".gsd", "worktrees", "M001");
|
|
@@ -1409,6 +1806,10 @@ test("executeSummarySave blocks final root artifacts while approval gate is pend
|
|
|
1409
1806
|
|
|
1410
1807
|
assert.equal(result.isError, true);
|
|
1411
1808
|
assert.equal(result.details.error, "root_artifact_write_blocked");
|
|
1809
|
+
assert.equal(
|
|
1810
|
+
result.details.displayReason,
|
|
1811
|
+
"Approval confirmation required before saving final project setup artifacts.",
|
|
1812
|
+
);
|
|
1412
1813
|
assert.match(result.content[0].text, /has not been confirmed/);
|
|
1413
1814
|
assert.equal(existsSync(join(base, ".gsd", "REQUIREMENTS.md")), false);
|
|
1414
1815
|
|
|
@@ -1451,6 +1852,10 @@ test("executeSummarySave requires verified root approval in deep mode", async ()
|
|
|
1451
1852
|
|
|
1452
1853
|
assert.equal(blocked.isError, true);
|
|
1453
1854
|
assert.equal(blocked.details.error, "root_artifact_write_blocked");
|
|
1855
|
+
assert.equal(
|
|
1856
|
+
blocked.details.displayReason,
|
|
1857
|
+
"Approval confirmation required before saving final project setup artifacts.",
|
|
1858
|
+
);
|
|
1454
1859
|
assert.match(blocked.content[0].text, /fail-closed/);
|
|
1455
1860
|
assert.equal(existsSync(join(base, ".gsd", "PROJECT.md")), false);
|
|
1456
1861
|
|
|
@@ -1667,6 +2072,10 @@ test("executeSummarySave CONTEXT HARD BLOCK clears after write-gate state file i
|
|
|
1667
2072
|
content: "# Context\n\ncontent",
|
|
1668
2073
|
}, base));
|
|
1669
2074
|
assert.equal(blocked.isError, true, "should be blocked without depth verification");
|
|
2075
|
+
assert.equal(
|
|
2076
|
+
blocked.details.displayReason,
|
|
2077
|
+
"Depth check required before writing milestone context.",
|
|
2078
|
+
);
|
|
1670
2079
|
assert.match(
|
|
1671
2080
|
blocked.content[0].text,
|
|
1672
2081
|
/HARD BLOCK/,
|