@opengsd/gsd-pi 1.1.1-dev.2034b16 → 1.1.1-dev.2de7ea0

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.
Files changed (142) hide show
  1. package/dist/cli.js +3 -2
  2. package/dist/help-text.js +10 -6
  3. package/dist/resources/.managed-resources-content-hash +1 -1
  4. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +495 -0
  5. package/dist/resources/extensions/browser-tools/engine/selection.js +16 -0
  6. package/dist/resources/extensions/browser-tools/extension-manifest.json +2 -2
  7. package/dist/resources/extensions/browser-tools/index.js +57 -9
  8. package/dist/resources/extensions/browser-tools/package.json +5 -1
  9. package/dist/resources/extensions/gsd/auto-post-unit.js +21 -3
  10. package/dist/resources/extensions/gsd/auto-prompts.js +15 -6
  11. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +2 -2
  12. package/dist/resources/extensions/gsd/browser-evidence.js +29 -2
  13. package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -2
  14. package/dist/resources/extensions/gsd/commands-handlers.js +76 -11
  15. package/dist/resources/extensions/gsd/commands-mcp-status.js +2 -1
  16. package/dist/resources/extensions/gsd/docs/preferences-reference.md +8 -0
  17. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +2 -2
  18. package/dist/resources/extensions/gsd/mcp-project-config.js +9 -76
  19. package/dist/resources/extensions/gsd/post-unit-hooks.js +9 -0
  20. package/dist/resources/extensions/gsd/preferences-validation.js +39 -0
  21. package/dist/resources/extensions/gsd/prompt-loader.js +7 -0
  22. package/dist/resources/extensions/gsd/prompts/run-uat.md +40 -22
  23. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
  24. package/dist/resources/extensions/gsd/rule-registry.js +428 -52
  25. package/dist/resources/extensions/gsd/tools/validate-milestone.js +46 -16
  26. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +29 -14
  27. package/dist/resources/extensions/gsd/verdict-parser.js +59 -15
  28. package/dist/resources/extensions/shared/gsd-browser-cli.js +145 -0
  29. package/dist/rtk.d.ts +7 -1
  30. package/dist/rtk.js +27 -11
  31. package/dist/update-check.d.ts +15 -1
  32. package/dist/update-check.js +87 -12
  33. package/dist/update-cmd.d.ts +1 -0
  34. package/dist/update-cmd.js +53 -2
  35. package/dist/web/standalone/.next/BUILD_ID +1 -1
  36. package/dist/web/standalone/.next/app-path-routes-manifest.json +6 -6
  37. package/dist/web/standalone/.next/build-manifest.json +2 -2
  38. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  39. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  40. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  48. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  56. package/dist/web/standalone/.next/server/app/index.html +1 -1
  57. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app-paths-manifest.json +6 -6
  64. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  65. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  66. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  67. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  68. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  69. package/package.json +3 -2
  70. package/packages/cloud-mcp-gateway/package.json +2 -2
  71. package/packages/contracts/package.json +1 -1
  72. package/packages/daemon/package.json +4 -4
  73. package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts +2 -0
  74. package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts.map +1 -1
  75. package/packages/gsd-agent-core/dist/session/agent-session-compaction.js +8 -2
  76. package/packages/gsd-agent-core/dist/session/agent-session-compaction.js.map +1 -1
  77. package/packages/gsd-agent-core/package.json +5 -5
  78. package/packages/gsd-agent-modes/package.json +7 -7
  79. package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -1
  80. package/packages/mcp-server/dist/remote-questions.js +23 -9
  81. package/packages/mcp-server/dist/remote-questions.js.map +1 -1
  82. package/packages/mcp-server/dist/workflow-tools.js +1 -1
  83. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  84. package/packages/mcp-server/package.json +3 -3
  85. package/packages/native/package.json +1 -1
  86. package/packages/pi-agent-core/package.json +1 -1
  87. package/packages/pi-ai/dist/models.generated.d.ts +17 -0
  88. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  89. package/packages/pi-ai/dist/models.generated.js +19 -2
  90. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  91. package/packages/pi-ai/package.json +1 -1
  92. package/packages/pi-coding-agent/package.json +7 -7
  93. package/packages/pi-tui/package.json +1 -1
  94. package/packages/rpc-client/package.json +2 -2
  95. package/pkg/package.json +1 -1
  96. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +579 -0
  97. package/src/resources/extensions/browser-tools/engine/selection.ts +19 -0
  98. package/src/resources/extensions/browser-tools/extension-manifest.json +2 -2
  99. package/src/resources/extensions/browser-tools/index.ts +60 -9
  100. package/src/resources/extensions/browser-tools/package.json +5 -1
  101. package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +35 -0
  102. package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +33 -0
  103. package/src/resources/extensions/gsd/auto-post-unit.ts +28 -2
  104. package/src/resources/extensions/gsd/auto-prompts.ts +16 -6
  105. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +2 -2
  106. package/src/resources/extensions/gsd/browser-evidence.ts +26 -2
  107. package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -2
  108. package/src/resources/extensions/gsd/commands-handlers.ts +76 -11
  109. package/src/resources/extensions/gsd/commands-mcp-status.ts +2 -1
  110. package/src/resources/extensions/gsd/docs/preferences-reference.md +8 -0
  111. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +2 -2
  112. package/src/resources/extensions/gsd/mcp-project-config.ts +13 -78
  113. package/src/resources/extensions/gsd/post-unit-hooks.ts +14 -1
  114. package/src/resources/extensions/gsd/preferences-validation.ts +36 -0
  115. package/src/resources/extensions/gsd/prompt-loader.ts +8 -0
  116. package/src/resources/extensions/gsd/prompts/run-uat.md +40 -22
  117. package/src/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
  118. package/src/resources/extensions/gsd/rule-registry.ts +558 -58
  119. package/src/resources/extensions/gsd/rule-types.ts +2 -0
  120. package/src/resources/extensions/gsd/tests/browser-evidence.test.ts +142 -0
  121. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +30 -0
  122. package/src/resources/extensions/gsd/tests/doctor-runtime-checks.test.ts +27 -0
  123. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +4 -4
  124. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +66 -10
  125. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +32 -0
  126. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +2 -0
  127. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +157 -0
  128. package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +179 -0
  129. package/src/resources/extensions/gsd/tests/preferences.test.ts +29 -0
  130. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +22 -1
  131. package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +14 -0
  132. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +75 -0
  133. package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +6 -3
  134. package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +133 -0
  135. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +74 -0
  136. package/src/resources/extensions/gsd/tools/validate-milestone.ts +46 -15
  137. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +31 -14
  138. package/src/resources/extensions/gsd/types.ts +63 -0
  139. package/src/resources/extensions/gsd/verdict-parser.ts +54 -13
  140. package/src/resources/extensions/shared/gsd-browser-cli.ts +172 -0
  141. /package/dist/web/standalone/.next/static/{StOMnvtgGiBHrBOZJZ1Gr → JdwzU6IGLVBZPf84PIaJQ}/_buildManifest.js +0 -0
  142. /package/dist/web/standalone/.next/static/{StOMnvtgGiBHrBOZJZ1Gr → JdwzU6IGLVBZPf84PIaJQ}/_ssgManifest.js +0 -0
@@ -1,14 +1,17 @@
1
- import { createHash } from "node:crypto";
2
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
- import { createRequire } from "node:module";
4
- import { basename, resolve } from "node:path";
2
+ import { resolve } from "node:path";
5
3
  import { fileURLToPath } from "node:url";
6
4
 
5
+ import {
6
+ GSD_BROWSER_MCP_SERVER_NAME,
7
+ resolveBundledGsdBrowserCliPath,
8
+ resolveGsdBrowserMcpLaunchConfig,
9
+ } from "../shared/gsd-browser-cli.js";
7
10
  import { assertSafeDirectory } from "./validate-directory.js";
8
11
  import { detectWorkflowMcpLaunchConfig } from "./workflow-mcp.js";
9
12
 
10
13
  export const GSD_WORKFLOW_MCP_SERVER_NAME = "gsd-workflow";
11
- export const GSD_BROWSER_MCP_SERVER_NAME = "gsd-browser";
14
+ export { GSD_BROWSER_MCP_SERVER_NAME, resolveBundledGsdBrowserCliPath };
12
15
 
13
16
  export interface ProjectMcpServerConfig {
14
17
  command?: string;
@@ -59,31 +62,6 @@ export function resolveBundledGsdCliPath(env: NodeJS.ProcessEnv = process.env):
59
62
  return null;
60
63
  }
61
64
 
62
- export function resolveBundledGsdBrowserCliPath(env: NodeJS.ProcessEnv = process.env): string | null {
63
- const explicit = env.GSD_BROWSER_CLI_PATH?.trim() || env.GSD_BROWSER_BIN_PATH?.trim();
64
- if (explicit) return explicit;
65
-
66
- try {
67
- const requireFromHere = createRequire(import.meta.url);
68
- const packageJsonPath = requireFromHere.resolve("@opengsd/gsd-browser/package.json");
69
- const candidate = resolve(packageJsonPath, "..", "bin", "gsd-browser");
70
- if (existsSync(candidate)) return candidate;
71
- } catch {
72
- // Fall through to path candidates for source/dist layouts.
73
- }
74
-
75
- const candidates = [
76
- resolve(fileURLToPath(new URL("../../../../node_modules/@opengsd/gsd-browser/bin/gsd-browser", import.meta.url))),
77
- resolve(fileURLToPath(new URL("../../../../node_modules/.bin/gsd-browser", import.meta.url))),
78
- ];
79
-
80
- for (const candidate of candidates) {
81
- if (existsSync(candidate)) return candidate;
82
- }
83
-
84
- return null;
85
- }
86
-
87
65
  export function buildProjectWorkflowMcpServerConfig(
88
66
  projectRoot: string,
89
67
  env: NodeJS.ProcessEnv = process.env,
@@ -119,31 +97,12 @@ function buildProjectWorkflowMcpServerSpec(
119
97
  };
120
98
  }
121
99
 
122
- function parseJsonEnv<T>(env: NodeJS.ProcessEnv, name: string): T | undefined {
123
- const raw = env[name];
124
- if (!raw) return undefined;
125
- try {
126
- return JSON.parse(raw) as T;
127
- } catch {
128
- throw new Error(`Invalid JSON in ${name}`);
129
- }
130
- }
131
-
132
100
  function isEnvDisabled(value: string | undefined): boolean {
133
101
  if (!value) return false;
134
102
  const normalized = value.trim().toLowerCase();
135
103
  return normalized === "0" || normalized === "false" || normalized === "off";
136
104
  }
137
105
 
138
- function buildBrowserSessionName(projectRoot: string): string {
139
- const resolvedProjectRoot = resolve(projectRoot);
140
- const base = basename(resolvedProjectRoot)
141
- .replace(/[^a-zA-Z0-9._-]+/g, "-")
142
- .replace(/^-+|-+$/g, "") || "project";
143
- const hash = createHash("sha1").update(resolvedProjectRoot).digest("hex").slice(0, 8);
144
- return `gsd-${base}-${hash}`;
145
- }
146
-
147
106
  export function buildProjectBrowserMcpServerConfig(
148
107
  projectRoot: string,
149
108
  env: NodeJS.ProcessEnv = process.env,
@@ -157,39 +116,15 @@ function buildProjectBrowserMcpServerSpec(
157
116
  ): ProjectMcpServerSpec | null {
158
117
  if (isEnvDisabled(env.GSD_BROWSER_MCP_ENABLED)) return null;
159
118
 
160
- const resolvedProjectRoot = resolve(projectRoot);
161
- const serverName = env.GSD_BROWSER_MCP_NAME?.trim() || GSD_BROWSER_MCP_SERVER_NAME;
162
- const explicitArgs = parseJsonEnv<unknown>(env, "GSD_BROWSER_MCP_ARGS");
163
- const explicitEnv = parseJsonEnv<Record<string, string>>(env, "GSD_BROWSER_MCP_ENV");
164
- const explicitCommand = env.GSD_BROWSER_MCP_COMMAND?.trim();
165
- const explicitCliPath = env.GSD_BROWSER_CLI_PATH?.trim() || env.GSD_BROWSER_BIN_PATH?.trim();
166
- const bundledCliPath = !explicitCommand && !explicitCliPath ? resolveBundledGsdBrowserCliPath(env) : null;
167
- const command =
168
- explicitCommand
169
- || explicitCliPath
170
- || (bundledCliPath ? process.execPath : undefined)
171
- || "gsd-browser";
172
- const args = Array.isArray(explicitArgs) && explicitArgs.length > 0
173
- ? explicitArgs.map(String)
174
- : [
175
- ...(bundledCliPath ? [bundledCliPath] : []),
176
- "mcp",
177
- "--session",
178
- buildBrowserSessionName(resolvedProjectRoot),
179
- "--identity-scope",
180
- "project",
181
- "--identity-project",
182
- resolvedProjectRoot,
183
- ];
184
- const cwd = env.GSD_BROWSER_MCP_CWD?.trim() || resolvedProjectRoot;
119
+ const launch = resolveGsdBrowserMcpLaunchConfig(projectRoot, env);
185
120
 
186
121
  return {
187
- serverName,
122
+ serverName: launch.serverName,
188
123
  server: {
189
- command,
190
- args,
191
- cwd,
192
- ...(explicitEnv ? { env: explicitEnv } : {}),
124
+ command: launch.command,
125
+ args: launch.args,
126
+ cwd: launch.cwd,
127
+ ...(launch.env ? { env: launch.env } : {}),
193
128
  },
194
129
  };
195
130
  }
@@ -9,6 +9,7 @@ import type {
9
9
  HookDispatchResult,
10
10
  PreDispatchResult,
11
11
  HookStatusEntry,
12
+ PostUnitGateBlock,
12
13
  } from "./types.js";
13
14
  import { getOrCreateRegistry, resolveHookArtifactPath } from "./rule-registry.js";
14
15
 
@@ -33,10 +34,22 @@ export function isRetryPending(): boolean {
33
34
  return getOrCreateRegistry().isRetryPending();
34
35
  }
35
36
 
36
- export function consumeRetryTrigger(): { unitType: string; unitId: string; retryArtifact: string } | null {
37
+ export function consumeRetryTrigger(): { unitType: string; unitId: string; retryArtifact?: string } | null {
37
38
  return getOrCreateRegistry().consumeRetryTrigger();
38
39
  }
39
40
 
41
+ export function consumeHookFailure(): { hookName: string; unitType: string; unitId: string; reason: string } | null {
42
+ return getOrCreateRegistry().consumeHookFailure();
43
+ }
44
+
45
+ export function isGateBlockPending(): boolean {
46
+ return getOrCreateRegistry().isGateBlockPending();
47
+ }
48
+
49
+ export function consumeGateBlock(): PostUnitGateBlock | null {
50
+ return getOrCreateRegistry().consumeGateBlock();
51
+ }
52
+
40
53
  export function resetHookState(): void {
41
54
  getOrCreateRegistry().resetState();
42
55
  }
@@ -29,6 +29,14 @@ const VALID_UOK_TURN_ACTIONS = new Set<"commit" | "snapshot" | "status-only">([
29
29
  "snapshot",
30
30
  "status-only",
31
31
  ]);
32
+ const VALID_POST_UNIT_HOOK_CRITICALITIES = new Set(["advisory", "blocking"]);
33
+ const VALID_POST_UNIT_HOOK_ON_BLOCK_ACTIONS = new Set([
34
+ "retry-unit",
35
+ "retry-task",
36
+ "queue-task",
37
+ "queue-slice",
38
+ "pause",
39
+ ]);
32
40
 
33
41
  export function validatePreferences(preferences: GSDPreferences): {
34
42
  preferences: GSDPreferences;
@@ -486,9 +494,37 @@ export function validatePreferences(preferences: GSDPreferences): {
486
494
  if (typeof hook.artifact === "string" && hook.artifact.trim()) {
487
495
  validHook.artifact = hook.artifact.trim();
488
496
  }
497
+ if (hook.criticality !== undefined) {
498
+ const criticality = typeof hook.criticality === "string" ? hook.criticality.trim() : "";
499
+ if (VALID_POST_UNIT_HOOK_CRITICALITIES.has(criticality)) {
500
+ validHook.criticality = criticality as PostUnitHookConfig["criticality"];
501
+ } else {
502
+ errors.push(`post_unit_hooks "${name}" invalid criticality: ${String(hook.criticality)}`);
503
+ }
504
+ }
489
505
  if (typeof hook.retry_on === "string" && hook.retry_on.trim()) {
490
506
  validHook.retry_on = hook.retry_on.trim();
491
507
  }
508
+ if (hook.on_block !== undefined) {
509
+ if (!hook.on_block || typeof hook.on_block !== "object") {
510
+ errors.push(`post_unit_hooks "${name}" on_block must be an object`);
511
+ } else {
512
+ const onBlock = hook.on_block as unknown as Record<string, unknown>;
513
+ const action = typeof onBlock.action === "string" ? onBlock.action.trim() : "";
514
+ if (!VALID_POST_UNIT_HOOK_ON_BLOCK_ACTIONS.has(action)) {
515
+ errors.push(`post_unit_hooks "${name}" invalid on_block action: ${String(onBlock.action)}`);
516
+ } else {
517
+ validHook.on_block = { action: action as NonNullable<PostUnitHookConfig["on_block"]>["action"] };
518
+ if (typeof onBlock.artifact === "string" && onBlock.artifact.trim()) {
519
+ validHook.on_block.artifact = onBlock.artifact.trim();
520
+ }
521
+ }
522
+ }
523
+ }
524
+ if (validHook.criticality === "blocking" && !validHook.artifact) {
525
+ errors.push(`post_unit_hooks "${name}" criticality blocking requires artifact`);
526
+ continue;
527
+ }
492
528
  if (typeof hook.agent === "string" && hook.agent.trim()) {
493
529
  validHook.agent = hook.agent.trim();
494
530
  }
@@ -33,6 +33,10 @@ function hasRequiredExtensionAssets(rootDir: string, exists: ExistsFn = existsSy
33
33
  );
34
34
  }
35
35
 
36
+ function isSourceExtensionDir(moduleDir: string): boolean {
37
+ return moduleDir.replaceAll("\\", "/").endsWith("/src/resources/extensions/gsd");
38
+ }
39
+
36
40
  export function resolveExtensionDirFromCandidates(
37
41
  moduleDir: string,
38
42
  agentGsdDir: string,
@@ -41,6 +45,10 @@ export function resolveExtensionDirFromCandidates(
41
45
  const moduleUsable = hasRequiredExtensionAssets(moduleDir, exists);
42
46
  const agentUsable = hasRequiredExtensionAssets(agentGsdDir, exists);
43
47
 
48
+ // Source checkouts must use their own prompt tree. Otherwise local tests and
49
+ // dev runs can silently render stale prompts from ~/.gsd/agent/extensions/gsd.
50
+ if (moduleUsable && isSourceExtensionDir(moduleDir)) return moduleDir;
51
+
44
52
  // Prefer the user-local extension tree when both are valid. This avoids
45
53
  // leaking npm/global-install paths into prompts on Windows.
46
54
  if (agentUsable) return agentGsdDir;
@@ -63,35 +63,53 @@ After running all checks, compute the **overall verdict**:
63
63
  - `FAIL` — one or more automatable checks failed
64
64
  - `PARTIAL` — one or more automatable checks were skipped or returned inconclusive results (not the same as `NEEDS-HUMAN` — use PARTIAL only when the agent itself could not determine pass/fail for a check it was supposed to automate)
65
65
 
66
- Call `gsd_summary_save` with `milestone_id: "{{milestoneId}}"`, `slice_id: "{{sliceId}}"`, `artifact_type: "ASSESSMENT"`, and the full UAT result markdown as `content`. The tool computes the assessment path, persists to DB/disk, and saves the aggregate UAT gate. The content should follow this logical shape:
66
+ Call `gsd_uat_result_save` once after all checks are complete. The tool computes the assessment path, persists to DB/disk, saves attempt history, and saves the aggregate UAT gate.
67
67
 
68
- ```markdown
69
- ---
70
- sliceId: {{sliceId}}
71
- uatType: {{uatType}}
72
- verdict: PASS | FAIL | PARTIAL
73
- date: <ISO 8601 timestamp>
74
- ---
75
-
76
- # UAT Result — {{sliceId}}
77
-
78
- ## Checks
68
+ Pass these top-level fields:
79
69
 
80
- | Check | Mode | Result | Notes |
81
- |-------|------|--------|-------|
82
- | <check description> | artifact / runtime / human-follow-up | PASS / FAIL / NEEDS-HUMAN | <observed output, evidence, or reason> |
83
-
84
- ## Overall Verdict
85
-
86
- <PASS / FAIL / PARTIAL> — <one sentence summary>
70
+ ```ts
71
+ milestoneId: "{{milestoneId}}",
72
+ sliceId: "{{sliceId}}",
73
+ uatType: "{{uatType}}",
74
+ verdict: "PASS" | "FAIL" | "PARTIAL",
75
+ notes: "<one sentence overall verdict rationale>",
76
+ ```
87
77
 
88
- ## Notes
78
+ Use this exact `presentation` shape in the save call so the audit can verify the run-uat tool surface without retrying missing fields one by one:
79
+
80
+ ```ts
81
+ presentation: {
82
+ surface: "mcp",
83
+ presentedTools: [
84
+ "gsd_uat_exec",
85
+ "gsd_uat_result_save",
86
+ "gsd_resume",
87
+ "gsd_milestone_status",
88
+ "gsd_journal_query",
89
+ ],
90
+ blockedTools: [
91
+ { name: "gsd_exec", reason: "forbidden during run-uat" },
92
+ { name: "gsd_summary_save", reason: "forbidden during run-uat" },
93
+ { name: "gsd_save_gate_result", reason: "forbidden during run-uat" },
94
+ ],
95
+ }
96
+ ```
89
97
 
90
- <any additional context, errors encountered, screenshots/logs gathered, or manual follow-up still required>
98
+ Pass `checks` with this logical shape:
99
+
100
+ ```ts
101
+ checks: [{
102
+ id: "<stable check id>",
103
+ description: "<check description from the UAT file>",
104
+ mode: "artifact" | "runtime" | "browser" | "human-follow-up",
105
+ result: "PASS" | "FAIL" | "NEEDS-HUMAN",
106
+ evidence: [{ kind: "gsd_uat_exec", ref: "<evidence id>" }],
107
+ notes: "<observed output, evidence, reason, or manual follow-up>",
108
+ }]
91
109
  ```
92
110
 
93
111
  ---
94
112
 
95
- **You MUST call `gsd_summary_save` with `artifact_type: "ASSESSMENT"` and the UAT result content before finishing. Do not write the assessment file directly.**
113
+ **You MUST call `gsd_uat_result_save` before finishing. Do not write the assessment file directly, and do not call `gsd_summary_save` as a substitute.**
96
114
 
97
115
  When done, say: "UAT {{sliceId}} complete."
@@ -33,7 +33,7 @@ Prompt: "Review milestone {{milestoneId}} requirements coverage. Working directo
33
33
  Prompt: "Review milestone {{milestoneId}} cross-slice integration. Working directory: {{workingDirectory}}. Read `{{roadmapPath}}` and find the boundary map (produces/consumes contracts). For each boundary, confirm producer SUMMARY produced the artifact and consumer SUMMARY consumed it. Output table: Boundary | Producer Summary | Consumer Summary | Status. End with one-line verdict: PASS if all boundaries honored, NEEDS-ATTENTION if any gaps."
34
34
 
35
35
  **Reviewer C - Assessment & Acceptance Criteria**
36
- Prompt: "Review milestone {{milestoneId}} assessment evidence and acceptance criteria. Working directory: {{workingDirectory}}. Read `.gsd/milestones/{{milestoneId}}/{{milestoneId}}-CONTEXT.md` for criteria. Check slice SUMMARY and ASSESSMENT files under `.gsd/milestones/{{milestoneId}}/slices/`; UAT files are specs, not evidence. Verify each criterion maps to passing evidence. Then review inlined milestone verification classes. For each non-empty planned class, output table: Class | Planned Check | Evidence | Verdict. Use the exact class names `Contract`, `Integration`, `Operational`, and `UAT` whenever those classes are present. If a planned browser/UAT class has no ASSESSMENT with browser/runtime actions and assertions, return NEEDS-ATTENTION. If no verification classes were planned, say that explicitly. Output sections `Acceptance Criteria` with checklist `[ ] Criterion | Evidence`, and `Verification Classes` with the table. End with one-line verdict: PASS if all criteria and classes are covered by evidence, NEEDS-ATTENTION if gaps exist."
36
+ Prompt: "Review milestone {{milestoneId}} assessment evidence and acceptance criteria. Working directory: {{workingDirectory}}. Read `.gsd/milestones/{{milestoneId}}/{{milestoneId}}-CONTEXT.md` for criteria. Check slice SUMMARY and ASSESSMENT files under `.gsd/milestones/{{milestoneId}}/slices/`; UAT files are specs, not evidence. Verify each criterion maps to passing evidence. Then review the inlined `Verification Classes (from planning)` table. For every planned row in that table, output a `Verification Classes` table with columns `Class | Planned Check | Evidence | Verdict`. Preserve every planned non-empty class row; do not summarize, rename, combine, or omit planned classes. The first cell of each row must be exactly `Contract`, `Integration`, `Operational`, or `UAT` when that class is present in planning. If a planned class lacks evidence, still include its canonical row and mark the verdict NEEDS-ATTENTION or FAIL. If a planned browser/UAT class has no ASSESSMENT with browser/runtime actions and assertions, return NEEDS-ATTENTION. If no verification classes were planned, say that explicitly. Output sections `Acceptance Criteria` with checklist `[ ] Criterion | Evidence`, and `Verification Classes` with the table. End with one-line verdict: PASS if all criteria and classes are covered by evidence, NEEDS-ATTENTION if gaps exist."
37
37
 
38
38
  ### Step 2 - Synthesize Findings
39
39
 
@@ -71,8 +71,8 @@ reviewers: 3
71
71
  <if verdict is not pass: specific actions required>
72
72
  ```
73
73
 
74
- Call `gsd_validate_milestone` with the camelCase fields `milestoneId`, `verdict`, `remediationRound`, `successCriteriaChecklist`, `sliceDeliveryAudit`, `crossSliceIntegration`, `requirementCoverage`, `verdictRationale`, and `remediationPlan` when needed. If you include verification-class analysis, pass it in `verificationClasses`.
75
- Extract the `Verification Classes` subsection from Reviewer C and pass it verbatim in `verificationClasses` so the persisted validation output uses the canonical class names `Contract`, `Integration`, `Operational`, and `UAT`.
74
+ Call `gsd_validate_milestone` with the camelCase fields `milestoneId`, `verdict`, `remediationRound`, `successCriteriaChecklist`, `sliceDeliveryAudit`, `crossSliceIntegration`, `requirementCoverage`, `verdictRationale`, and `remediationPlan` when needed. If planning included verification classes, pass a complete canonical table in `verificationClasses`.
75
+ Set `verificationClasses` to the `Verification Classes` subsection from Reviewer C. It must include one canonical row for every non-empty planned class from `Verification Classes (from planning)`: `Contract`, `Integration`, `Operational`, and/or `UAT`. If Reviewer C omitted a planned class, reconstruct the missing row from the planning table, set Evidence to the gap, and use NEEDS-ATTENTION or FAIL. Do not call `gsd_validate_milestone` with a partial `verificationClasses` table.
76
76
 
77
77
  **DB access safety:** Do NOT query `.gsd/gsd.db` directly via `sqlite3` or `node -e require('better-sqlite3')` - the engine owns the WAL connection. Use `gsd_milestone_status` for milestone and slice state. Data is already inlined or available via `gsd_*` tools. Direct DB access risks WAL corruption and bypasses validation.
78
78