@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.
Files changed (219) hide show
  1. package/dist/resources/.managed-resources-content-hash +1 -1
  2. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +18 -2
  3. package/dist/resources/extensions/browser-tools/engine/selection.js +1 -1
  4. package/dist/resources/extensions/browser-tools/extension-manifest.json +1 -1
  5. package/dist/resources/extensions/browser-tools/index.js +29 -2
  6. package/dist/resources/extensions/browser-tools/web-app-detect.js +52 -0
  7. package/dist/resources/extensions/gsd/auto/phases.js +45 -3
  8. package/dist/resources/extensions/gsd/auto/session.js +2 -0
  9. package/dist/resources/extensions/gsd/auto-dispatch.js +21 -2
  10. package/dist/resources/extensions/gsd/auto-model-selection.js +26 -0
  11. package/dist/resources/extensions/gsd/auto-prompts.js +4 -0
  12. package/dist/resources/extensions/gsd/auto-recovery.js +3 -4
  13. package/dist/resources/extensions/gsd/auto-timers.js +24 -10
  14. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +18 -66
  15. package/dist/resources/extensions/gsd/auto-worktree.js +18 -5
  16. package/dist/resources/extensions/gsd/auto.js +26 -4
  17. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +16 -10
  18. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +48 -29
  19. package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
  20. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +18 -29
  21. package/dist/resources/extensions/gsd/closeout-consistency-gate.js +61 -0
  22. package/dist/resources/extensions/gsd/commands/handlers/auto.js +10 -0
  23. package/dist/resources/extensions/gsd/commands-mcp-status.js +1 -1
  24. package/dist/resources/extensions/gsd/config-overlay.js +1 -0
  25. package/dist/resources/extensions/gsd/context-masker.js +129 -5
  26. package/dist/resources/extensions/gsd/guided-flow.js +93 -108
  27. package/dist/resources/extensions/gsd/milestone-closeout.js +3 -1
  28. package/dist/resources/extensions/gsd/pending-auto-start.js +0 -1
  29. package/dist/resources/extensions/gsd/planner-handoff.js +98 -0
  30. package/dist/resources/extensions/gsd/preferences-models.js +1 -0
  31. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  32. package/dist/resources/extensions/gsd/prompts/run-uat.md +5 -19
  33. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  34. package/dist/resources/extensions/gsd/recovery-classification.js +20 -0
  35. package/dist/resources/extensions/gsd/skill-manifest.js +12 -0
  36. package/dist/resources/extensions/gsd/tool-contract.js +6 -1
  37. package/dist/resources/extensions/gsd/tool-presentation-plan.js +47 -7
  38. package/dist/resources/extensions/gsd/tools/complete-slice.js +28 -1
  39. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +113 -8
  40. package/dist/resources/extensions/gsd/unit-tool-contracts.js +193 -0
  41. package/dist/resources/extensions/gsd/workflow-mcp.js +5 -78
  42. package/dist/resources/extensions/gsd/worktree-manager.js +26 -0
  43. package/dist/resources/extensions/gsd/worktree-reentry.js +96 -0
  44. package/dist/resources/extensions/shared/gsd-browser-cli.js +6 -0
  45. package/dist/web/standalone/.next/BUILD_ID +1 -1
  46. package/dist/web/standalone/.next/app-path-routes-manifest.json +5 -5
  47. package/dist/web/standalone/.next/build-manifest.json +2 -2
  48. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  49. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  50. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  58. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/index.html +1 -1
  66. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app-paths-manifest.json +5 -5
  73. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  74. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  75. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  76. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  77. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  78. package/package.json +1 -1
  79. package/packages/cloud-mcp-gateway/package.json +2 -2
  80. package/packages/contracts/package.json +1 -1
  81. package/packages/daemon/package.json +4 -4
  82. package/packages/gsd-agent-core/package.json +5 -5
  83. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  84. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +5 -0
  85. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  86. package/packages/gsd-agent-modes/package.json +7 -7
  87. package/packages/mcp-server/package.json +3 -3
  88. package/packages/native/package.json +1 -1
  89. package/packages/pi-agent-core/dist/agent-loop.js +4 -3
  90. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  91. package/packages/pi-agent-core/dist/harness/agent-harness.d.ts.map +1 -1
  92. package/packages/pi-agent-core/dist/harness/agent-harness.js +3 -1
  93. package/packages/pi-agent-core/dist/harness/agent-harness.js.map +1 -1
  94. package/packages/pi-agent-core/dist/harness/types.d.ts +1 -0
  95. package/packages/pi-agent-core/dist/harness/types.d.ts.map +1 -1
  96. package/packages/pi-agent-core/dist/harness/types.js.map +1 -1
  97. package/packages/pi-agent-core/dist/types.d.ts +3 -1
  98. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  99. package/packages/pi-agent-core/dist/types.js.map +1 -1
  100. package/packages/pi-agent-core/package.json +1 -1
  101. package/packages/pi-ai/dist/models.generated.d.ts +157 -18
  102. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  103. package/packages/pi-ai/dist/models.generated.js +159 -36
  104. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  105. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  106. package/packages/pi-ai/dist/providers/transform-messages.js +8 -1
  107. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  108. package/packages/pi-ai/package.json +1 -1
  109. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts +3 -0
  110. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts.map +1 -1
  111. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.js.map +1 -1
  112. package/packages/pi-coding-agent/dist/core/tools/bash.js +2 -2
  113. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  114. package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
  115. package/packages/pi-coding-agent/dist/core/tools/edit.js +3 -2
  116. package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
  117. package/packages/pi-coding-agent/dist/core/tools/render-utils.d.ts +1 -0
  118. package/packages/pi-coding-agent/dist/core/tools/render-utils.d.ts.map +1 -1
  119. package/packages/pi-coding-agent/dist/core/tools/render-utils.js +6 -0
  120. package/packages/pi-coding-agent/dist/core/tools/render-utils.js.map +1 -1
  121. package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
  122. package/packages/pi-coding-agent/dist/core/tools/write.js +3 -2
  123. package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
  124. package/packages/pi-coding-agent/package.json +7 -7
  125. package/packages/pi-tui/package.json +1 -1
  126. package/packages/rpc-client/package.json +2 -2
  127. package/pkg/package.json +1 -1
  128. package/scripts/install/handoff.js +16 -3
  129. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +21 -2
  130. package/src/resources/extensions/browser-tools/engine/selection.ts +1 -1
  131. package/src/resources/extensions/browser-tools/extension-manifest.json +1 -1
  132. package/src/resources/extensions/browser-tools/index.ts +36 -5
  133. package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +2 -2
  134. package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +37 -0
  135. package/src/resources/extensions/browser-tools/tests/web-app-detect.test.mjs +68 -0
  136. package/src/resources/extensions/browser-tools/web-app-detect.ts +63 -0
  137. package/src/resources/extensions/gsd/auto/phases.ts +48 -6
  138. package/src/resources/extensions/gsd/auto/session.ts +2 -0
  139. package/src/resources/extensions/gsd/auto-dispatch.ts +48 -2
  140. package/src/resources/extensions/gsd/auto-model-selection.ts +26 -0
  141. package/src/resources/extensions/gsd/auto-prompts.ts +4 -0
  142. package/src/resources/extensions/gsd/auto-recovery.ts +3 -3
  143. package/src/resources/extensions/gsd/auto-timers.ts +25 -9
  144. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +43 -74
  145. package/src/resources/extensions/gsd/auto-worktree.ts +23 -5
  146. package/src/resources/extensions/gsd/auto.ts +28 -4
  147. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +16 -10
  148. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +63 -29
  149. package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
  150. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +50 -54
  151. package/src/resources/extensions/gsd/closeout-consistency-gate.ts +137 -0
  152. package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -0
  153. package/src/resources/extensions/gsd/commands-mcp-status.ts +1 -1
  154. package/src/resources/extensions/gsd/config-overlay.ts +1 -0
  155. package/src/resources/extensions/gsd/context-masker.ts +152 -5
  156. package/src/resources/extensions/gsd/guided-flow.ts +128 -135
  157. package/src/resources/extensions/gsd/milestone-closeout.ts +3 -1
  158. package/src/resources/extensions/gsd/pending-auto-start.ts +0 -2
  159. package/src/resources/extensions/gsd/planner-handoff.ts +149 -0
  160. package/src/resources/extensions/gsd/preferences-models.ts +1 -0
  161. package/src/resources/extensions/gsd/preferences-types.ts +8 -0
  162. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  163. package/src/resources/extensions/gsd/prompts/run-uat.md +5 -19
  164. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  165. package/src/resources/extensions/gsd/recovery-classification.ts +20 -0
  166. package/src/resources/extensions/gsd/skill-manifest.ts +12 -0
  167. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +99 -0
  168. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +66 -4
  169. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +10 -2
  170. package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +4 -1
  171. package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +4 -0
  172. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +12 -2
  173. package/src/resources/extensions/gsd/tests/bundled-skill-triggers.test.ts +9 -0
  174. package/src/resources/extensions/gsd/tests/check-auto-start-pending-gate.test.ts +9 -15
  175. package/src/resources/extensions/gsd/tests/check-auto-start-ready-guard.test.ts +26 -16
  176. package/src/resources/extensions/gsd/tests/commands-dispatcher-unmerged-milestone.test.ts +21 -0
  177. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +118 -0
  178. package/src/resources/extensions/gsd/tests/context-masker.test.ts +56 -1
  179. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +1 -0
  180. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +40 -1
  181. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +24 -0
  182. package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +31 -79
  183. package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +5 -3
  184. package/src/resources/extensions/gsd/tests/guided-flow-state-rebuild.test.ts +40 -4
  185. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +8 -0
  186. package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +16 -0
  187. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +7 -1
  188. package/src/resources/extensions/gsd/tests/interrupted-session-auto.test.ts +27 -0
  189. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +1 -0
  190. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +7 -1
  191. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +1 -1
  192. package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +63 -0
  193. package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +10 -1
  194. package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +9 -1
  195. package/src/resources/extensions/gsd/tests/planner-handoff.test.ts +100 -0
  196. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +147 -5
  197. package/src/resources/extensions/gsd/tests/provider-switch-observer.test.ts +55 -0
  198. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +44 -0
  199. package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +4 -0
  200. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +56 -0
  201. package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +4 -3
  202. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +4 -4
  203. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +77 -10
  204. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +409 -0
  205. package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +102 -0
  206. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +15 -0
  207. package/src/resources/extensions/gsd/tool-contract.ts +7 -1
  208. package/src/resources/extensions/gsd/tool-presentation-plan.ts +82 -7
  209. package/src/resources/extensions/gsd/tools/complete-slice.ts +29 -1
  210. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +146 -9
  211. package/src/resources/extensions/gsd/unit-tool-contracts.ts +210 -0
  212. package/src/resources/extensions/gsd/workflow-mcp.ts +5 -78
  213. package/src/resources/extensions/gsd/worktree-manager.ts +32 -0
  214. package/src/resources/extensions/gsd/worktree-reentry.ts +103 -0
  215. package/src/resources/extensions/shared/gsd-browser-cli.ts +6 -0
  216. package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound-corrections.test.ts +0 -246
  217. package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound.test.ts +0 -218
  218. /package/dist/web/standalone/.next/static/{jBtwT9v1u2lUA3UEOy_ZH → zzYMrKpPGfRQRxSFO32Jr}/_buildManifest.js +0 -0
  219. /package/dist/web/standalone/.next/static/{jBtwT9v1u2lUA3UEOy_ZH → zzYMrKpPGfRQRxSFO32Jr}/_ssgManifest.js +0 -0
@@ -1,59 +1,13 @@
1
1
  import { parseUnitId } from "./unit-id.js";
2
- import { RUN_UAT_WORKFLOW_TOOL_NAMES } from "./tool-presentation-plan.js";
3
-
4
- export const RUN_UAT_BROWSER_TOOL_NAMES = [
5
- "browser_navigate",
6
- "browser_click",
7
- "browser_type",
8
- "browser_fill_form",
9
- "browser_click_ref",
10
- "browser_fill_ref",
11
- "browser_wait_for",
12
- "browser_assert",
13
- "browser_verify",
14
- "browser_screenshot",
15
- "browser_snapshot_refs",
16
- "browser_find",
17
- "browser_get_console_logs",
18
- "browser_get_network_logs",
19
- "browser_evaluate",
20
- "browser_reload",
21
- "browser_batch",
22
- "browser_act",
23
- ] as const;
2
+ import {
3
+ AUTO_UNIT_SCOPED_TOOLS,
4
+ getForbiddenGsdToolReason,
5
+ } from "./unit-tool-contracts.js";
24
6
 
25
- export const AUTO_UNIT_SCOPED_TOOLS: Record<string, readonly string[]> = {
26
- "research-milestone": ["gsd_summary_save", "gsd_decision_save"],
27
- "plan-milestone": ["gsd_plan_milestone", "gsd_decision_save", "gsd_requirement_update"],
28
- "discuss-milestone": [
29
- "gsd_summary_save",
30
- "gsd_decision_save",
31
- "gsd_requirement_save",
32
- "gsd_requirement_update",
33
- "gsd_plan_milestone",
34
- "gsd_milestone_generate_id",
35
- ],
36
- "discuss-slice": ["gsd_summary_save", "gsd_decision_save"],
37
- "validate-milestone": ["gsd_validate_milestone", "gsd_reassess_roadmap", "subagent"],
38
- "complete-milestone": ["gsd_complete_milestone", "subagent"],
39
- "research-slice": ["gsd_summary_save", "gsd_decision_save"],
40
- "plan-slice": ["gsd_plan_slice", "gsd_plan_task", "gsd_decision_save"],
41
- "refine-slice": ["gsd_plan_slice", "gsd_plan_task", "gsd_decision_save"],
42
- "replan-slice": ["gsd_replan_slice", "gsd_plan_task", "gsd_decision_save"],
43
- "complete-slice": ["gsd_slice_complete", "gsd_task_reopen", "gsd_replan_slice", "gsd_decision_save", "gsd_requirement_update", "subagent"],
44
- "reassess-roadmap": ["gsd_reassess_roadmap"],
45
- "execute-task": ["gsd_task_complete", "gsd_decision_save"],
46
- "execute-task-simple": ["gsd_task_complete", "gsd_decision_save"],
47
- "reactive-execute": ["gsd_task_complete", "gsd_decision_save"],
48
- "run-uat": [...RUN_UAT_WORKFLOW_TOOL_NAMES, "subagent", ...RUN_UAT_BROWSER_TOOL_NAMES],
49
- "gate-evaluate": ["gsd_save_gate_result"],
50
- "rewrite-docs": ["gsd_summary_save", "gsd_decision_save"],
51
- "workflow-preferences": ["gsd_summary_save"],
52
- "discuss-project": ["gsd_summary_save", "gsd_decision_save", "gsd_requirement_save"],
53
- "discuss-requirements": ["gsd_requirement_save", "gsd_summary_save"],
54
- "research-decision": ["gsd_summary_save"],
55
- "research-project": ["gsd_summary_save", "gsd_decision_save"],
56
- };
7
+ export {
8
+ AUTO_UNIT_SCOPED_TOOLS,
9
+ RUN_UAT_BROWSER_TOOL_NAMES,
10
+ } from "./unit-tool-contracts.js";
57
11
 
58
12
  const WORKFLOW_TOOL_ALIASES: Record<string, string> = {
59
13
  gsd_save_decision: "gsd_decision_save",
@@ -97,6 +51,14 @@ const SCOPED_GSD_LIFECYCLE_TOOLS = new Set(
97
51
  .map(canonicalWorkflowToolName),
98
52
  );
99
53
 
54
+ export const GSD_PHASE_SCOPE_DISPLAY_REASON = "This GSD phase only allows its scoped workflow tools.";
55
+
56
+ type AutoUnitToolScopeResult = {
57
+ block: boolean;
58
+ reason?: string;
59
+ displayReason?: string;
60
+ };
61
+
100
62
  function stripMcpToolPrefix(toolName: string): string {
101
63
  if (!toolName.startsWith("mcp__")) return toolName;
102
64
  const toolSeparator = toolName.indexOf("__", "mcp__".length);
@@ -114,12 +76,20 @@ export function isWorkflowAliasTool(toolName: string): boolean {
114
76
 
115
77
  function hardBlockReason(unitType: string, what: string): string {
116
78
  return [
117
- `HARD BLOCK: unit "${unitType}" is constrained by auto-unit tool scope — ${what}.`,
79
+ `HARD BLOCK: Tool Contract failure for unit "${unitType}" — ${what}.`,
118
80
  "This is a mechanical phase-boundary gate. You MUST NOT proceed, retry the same call,",
119
81
  "or route around this block; the orchestrator owns phase transitions.",
120
82
  ].join(" ");
121
83
  }
122
84
 
85
+ function hardBlock(unitType: string, what: string): AutoUnitToolScopeResult {
86
+ return {
87
+ block: true,
88
+ reason: hardBlockReason(unitType, what),
89
+ displayReason: GSD_PHASE_SCOPE_DISPLAY_REASON,
90
+ };
91
+ }
92
+
123
93
  function allowedGsdToolsForUnit(unitType: string): string[] {
124
94
  return [...new Set(
125
95
  (AUTO_UNIT_SCOPED_TOOLS[unitType] ?? [])
@@ -144,7 +114,7 @@ function shouldBlockTaskCompletionScope(
144
114
  unitId: string | undefined,
145
115
  toolName: string,
146
116
  input: unknown,
147
- ): { block: boolean; reason?: string } {
117
+ ): AutoUnitToolScopeResult {
148
118
  if (!EXECUTE_TASK_UNIT_TYPES.has(unitType)) return { block: false };
149
119
  if (canonicalWorkflowToolName(toolName) !== "gsd_task_complete") return { block: false };
150
120
  if (!unitId) return { block: false };
@@ -165,13 +135,10 @@ function shouldBlockTaskCompletionScope(
165
135
  return { block: false };
166
136
  }
167
137
 
168
- return {
169
- block: true,
170
- reason: hardBlockReason(
171
- unitType,
172
- `gsd_task_complete may only complete the active task ${expected.milestone}/${expected.slice}/${expected.task}; requested ${actualMilestone}/${actualSlice}/${actualTask}`,
173
- ),
174
- };
138
+ return hardBlock(
139
+ unitType,
140
+ `gsd_task_complete may only complete the active task ${expected.milestone}/${expected.slice}/${expected.task}; requested ${actualMilestone}/${actualSlice}/${actualTask}`,
141
+ );
175
142
  }
176
143
 
177
144
  export function shouldBlockAutoUnitToolCall(
@@ -179,15 +146,12 @@ export function shouldBlockAutoUnitToolCall(
179
146
  toolName: string,
180
147
  input?: unknown,
181
148
  unitId?: string,
182
- ): { block: boolean; reason?: string } {
149
+ ): AutoUnitToolScopeResult {
183
150
  const scopedTools = AUTO_UNIT_SCOPED_TOOLS[unitType];
184
151
  if (!scopedTools) return { block: false };
185
152
 
186
153
  if (isNativeWorkflowTool(toolName)) {
187
- return {
188
- block: true,
189
- reason: hardBlockReason(unitType, "native Workflow is not permitted inside a dispatched GSD auto-mode unit"),
190
- };
154
+ return hardBlock(unitType, "native Workflow is not permitted inside a dispatched GSD auto-mode unit");
191
155
  }
192
156
 
193
157
  const taskScope = shouldBlockTaskCompletionScope(unitType, unitId, toolName, input);
@@ -199,11 +163,16 @@ export function shouldBlockAutoUnitToolCall(
199
163
  const allowedTools = allowedGsdToolsForUnit(unitType);
200
164
  if (allowedTools.includes(canonicalTool)) return { block: false };
201
165
 
202
- return {
203
- block: true,
204
- reason: hardBlockReason(
166
+ const forbiddenReason = getForbiddenGsdToolReason(unitType, canonicalTool);
167
+ if (forbiddenReason) {
168
+ return hardBlock(
205
169
  unitType,
206
- `GSD lifecycle tool "${canonicalTool}" is not permitted; allowed GSD tools: ${allowedTools.length > 0 ? allowedTools.join(", ") : "(none)"}`,
207
- ),
208
- };
170
+ `GSD lifecycle tool "${canonicalTool}" is not permitted; ${forbiddenReason} Fix unit-tool-contracts.ts or the ${unitType} prompt.`,
171
+ );
172
+ }
173
+
174
+ return hardBlock(
175
+ unitType,
176
+ `GSD lifecycle tool "${canonicalTool}" is not permitted; allowed GSD tools: ${allowedTools.length > 0 ? allowedTools.join(", ") : "(none)"}`,
177
+ );
209
178
  }
@@ -83,6 +83,11 @@ import {
83
83
  nativeWorktreeList,
84
84
  nativeLsFiles,
85
85
  } from "./native-git-bridge.js";
86
+ import {
87
+ CLOSEOUT_CONSISTENCY_BLOCKED_REASON,
88
+ checkCloseoutConsistencyGate,
89
+ formatCloseoutConsistencyBlock,
90
+ } from "./closeout-consistency-gate.js";
86
91
  import { gsdHome } from "./gsd-home.js";
87
92
  import { type MilestoneScope, type GsdWorkspace, createWorkspace } from "./workspace.js";
88
93
  import {
@@ -1771,16 +1776,29 @@ export function mergeMilestoneToMain(
1771
1776
  // symlink layout) — ATTACHing a WAL-mode file to itself corrupts the
1772
1777
  // database (#2823).
1773
1778
  if (isDbAvailable()) {
1779
+ const contract = resolveGsdPathContract(worktreeCwd, originalBasePath_);
1780
+ const worktreeDbPath = join(contract.worktreeGsd ?? join(worktreeCwd, ".gsd"), "gsd.db");
1781
+ const mainDbPath = contract.projectDb;
1774
1782
  try {
1775
- const contract = resolveGsdPathContract(worktreeCwd, originalBasePath_);
1776
- const worktreeDbPath = join(contract.worktreeGsd ?? join(worktreeCwd, ".gsd"), "gsd.db");
1777
- const mainDbPath = contract.projectDb;
1783
+ const activeDbPath = getDbPath();
1784
+ if (activeDbPath && _shouldReconcileWorktreeDb(activeDbPath, mainDbPath)) {
1785
+ closeDatabase();
1786
+ if (!openDatabase(mainDbPath)) {
1787
+ throw new Error(`cannot open project DB at ${mainDbPath}`);
1788
+ }
1789
+ }
1778
1790
  if (_shouldReconcileWorktreeDb(worktreeDbPath, mainDbPath)) {
1779
1791
  reconcileWorktreeDb(mainDbPath, worktreeDbPath);
1780
1792
  }
1781
1793
  } catch (err) {
1782
- /* non-fatal */
1783
- logError("worktree", `DB reconciliation failed: ${err instanceof Error ? err.message : String(err)}`);
1794
+ const message = `DB reconciliation failed before milestone ${milestoneId} merge: ${err instanceof Error ? err.message : String(err)}`;
1795
+ logError("worktree", message);
1796
+ throw new GSDError(GSD_GIT_ERROR, `${message}. Recovery reason: ${CLOSEOUT_CONSISTENCY_BLOCKED_REASON}.`);
1797
+ }
1798
+
1799
+ const closeoutGate = checkCloseoutConsistencyGate(milestoneId);
1800
+ if (!closeoutGate.ok) {
1801
+ throw new GSDError(GSD_GIT_ERROR, formatCloseoutConsistencyBlock(closeoutGate));
1784
1802
  }
1785
1803
  }
1786
1804
 
@@ -107,7 +107,7 @@ import {
107
107
  } from "./auto-tool-tracking.js";
108
108
  import { closeoutUnit } from "./auto-unit-closeout.js";
109
109
  import { recoverTimedOutUnit } from "./auto-timeout-recovery.js";
110
- import { selectAndApplyModel, resolveModelId, clearToolBaseline } from "./auto-model-selection.js";
110
+ import { selectAndApplyModel, resolveModelId, clearToolBaseline, getToolBaselineSnapshot } from "./auto-model-selection.js";
111
111
  import { resetRoutingHistory, recordOutcome } from "./routing-history.js";
112
112
  import {
113
113
  checkPostUnitHooks,
@@ -542,8 +542,26 @@ function handlePausedSessionResumeRecovery(
542
542
  ): { skippedReplay: boolean } {
543
543
  if (!state.pausedSessionFile) return { skippedReplay: false };
544
544
 
545
- const pausedRecoveryUnitType = state.currentUnit?.type ?? state.pausedUnitType ?? "unknown";
546
- const pausedRecoveryUnitId = state.currentUnit?.id ?? state.pausedUnitId ?? "unknown";
545
+ const pausedRecoveryUnitType = state.currentUnit?.type ?? state.pausedUnitType ?? null;
546
+ const pausedRecoveryUnitId = state.currentUnit?.id ?? state.pausedUnitId ?? null;
547
+
548
+ // When the paused-session metadata never captured the unit identity (the
549
+ // pause happened between units, or the worker died before currentUnit was
550
+ // set), we have nothing to verify against and nothing correct to target. A
551
+ // replay synthesized with an "unknown" unit re-injects an unbounded,
552
+ // mis-identified tool-call blob into the fresh resume context — exactly the
553
+ // thrash that turns one stuck unit into several. Disk state has already been
554
+ // rebuilt (rebuildState + doctor) before this runs, so skip the replay and
555
+ // let the normal dispatcher recompute the next unit from disk.
556
+ if (!pausedRecoveryUnitType || !pausedRecoveryUnitId) {
557
+ state.pausedSessionFile = null;
558
+ state.pausedUnitType = null;
559
+ state.pausedUnitId = null;
560
+ state.pendingCrashRecovery = null;
561
+ notify("Paused session had no recorded unit identity. Skipping tool-call replay and resuming from disk state.");
562
+ return { skippedReplay: true };
563
+ }
564
+
547
565
  const completedPausedUnit = verifyExpectedArtifact(
548
566
  pausedRecoveryUnitType,
549
567
  pausedRecoveryUnitId,
@@ -2154,7 +2172,10 @@ export function createWiredDispatchAdapter(
2154
2172
  sessionProvider && typeof ctx.modelRegistry?.getProviderAuthMode === "function"
2155
2173
  ? ctx.modelRegistry.getProviderAuthMode(sessionProvider)
2156
2174
  : undefined;
2157
- const activeTools = typeof pi.getActiveTools === "function" ? pi.getActiveTools() : [];
2175
+ // Use baseline snapshot same reason as phases.ts:runDispatch: the live
2176
+ // active set may be narrowed by the prior unit before selectAndApplyModel
2177
+ // restores it, causing false transport-preflight failures (#477 follow-up).
2178
+ const activeTools = getToolBaselineSnapshot(pi);
2158
2179
  // Mirrors runDispatch: deep-planning keeps approval gates in plain chat
2159
2180
  // because structured questions can be cancelled outside the chat turn on
2160
2181
  // some transports.
@@ -2201,6 +2222,9 @@ export function createWiredDispatchAdapter(
2201
2222
  sessionContextWindow,
2202
2223
  sessionProvider,
2203
2224
  modelRegistry,
2225
+ activeTools,
2226
+ sessionAuthMode: authMode,
2227
+ sessionBaseUrl: ctx.model?.baseUrl,
2204
2228
  });
2205
2229
 
2206
2230
  if (action.action === "stop") {
@@ -78,6 +78,9 @@ function readDetails(result: any): any {
78
78
 
79
79
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- result shape varies by tool
80
80
  function formatToolErrorText(result: any, details: any): string {
81
+ if (typeof details?.displayReason === "string" && details.displayReason) {
82
+ return details.displayReason;
83
+ }
81
84
  const message = details?.error
82
85
  ?? result?.content?.find((entry: { type?: string; text?: string }) => entry.type === "text")?.text
83
86
  ?? "unknown";
@@ -424,6 +427,9 @@ export function registerDbTools(pi: ExtensionAPI): void {
424
427
  kind: StringEnum(["gsd_uat_exec", "gsd_exec", "screenshot", "log", "url", "browser"], { description: "Evidence kind" }),
425
428
  ref: Type.String({ description: "Evidence ID, approved .gsd path, or URL" }),
426
429
  note: Type.Optional(Type.String({ description: "Short evidence note" })),
430
+ unitType: Type.Optional(Type.String({ description: "Unit that produced the evidence" })),
431
+ tool: Type.Optional(Type.String({ description: "Tool that produced the evidence" })),
432
+ executionId: Type.Optional(Type.String({ description: "Stable execution or artifact id" })),
427
433
  });
428
434
 
429
435
  const uatCheck = Type.Object({
@@ -437,17 +443,17 @@ export function registerDbTools(pi: ExtensionAPI): void {
437
443
  });
438
444
 
439
445
  const toolPresentationBlock = Type.Object({
440
- surface: StringEnum(["provider-tools", "claude-code-sdk", "mcp", "hybrid"], { description: "Tool presentation surface" }),
446
+ surface: Type.Optional(StringEnum(["provider-tools", "claude-code-sdk", "mcp", "hybrid"], { description: "Tool presentation surface" })),
441
447
  model: Type.Optional(Type.Object({
442
448
  provider: Type.Optional(Type.String()),
443
449
  api: Type.Optional(Type.String()),
444
450
  id: Type.Optional(Type.String()),
445
451
  })),
446
- presentedTools: Type.Array(Type.String(), { description: "Tool names actually presented to the model" }),
447
- blockedTools: Type.Array(Type.Object({
452
+ presentedTools: Type.Optional(Type.Array(Type.String(), { description: "Tool names actually presented to the model" })),
453
+ blockedTools: Type.Optional(Type.Array(Type.Object({
448
454
  name: Type.String(),
449
455
  reason: Type.String(),
450
- }), { description: "Tool names blocked from the model with reasons" }),
456
+ }), { description: "Tool names blocked from the model with reasons" })),
451
457
  aliases: Type.Optional(Type.Array(Type.Object({
452
458
  requested: Type.String(),
453
459
  canonical: Type.String(),
@@ -471,12 +477,12 @@ export function registerDbTools(pi: ExtensionAPI): void {
471
477
  "Do not use raw gsd_summary_save as a substitute for UAT results.",
472
478
  ],
473
479
  parameters: Type.Object({
474
- milestoneId: Type.String({ description: "Milestone ID (e.g. M001)" }),
475
- sliceId: Type.String({ description: "Slice ID (e.g. S01)" }),
476
- uatType: StringEnum(["artifact-driven", "browser-executable", "runtime-executable", "live-runtime", "mixed", "human-experience"], { description: "Declared UAT mode" }),
477
- verdict: StringEnum(["PASS", "FAIL", "PARTIAL"], { description: "Overall UAT verdict" }),
478
- checks: Type.Array(uatCheck, { description: "Structured check results" }),
479
- presentation: toolPresentationBlock,
480
+ milestoneId: Type.Optional(Type.String({ description: "Milestone ID (e.g. M001)" })),
481
+ sliceId: Type.Optional(Type.String({ description: "Slice ID (e.g. S01)" })),
482
+ uatType: Type.Optional(Type.String({ description: "Declared UAT mode" })),
483
+ verdict: Type.Optional(Type.String({ description: "Overall UAT verdict: PASS, FAIL, or PARTIAL" })),
484
+ checks: Type.Optional(Type.Array(uatCheck, { description: "Structured check results" })),
485
+ presentation: Type.Optional(toolPresentationBlock),
480
486
  notes: Type.Optional(Type.String({ description: "Overall verdict rationale" })),
481
487
  attempt: Type.Optional(Type.String({ description: "Attempt number or auto" })),
482
488
  previousAttemptId: Type.Optional(Type.String({ description: "Prior attempt ID, when retrying" })),
@@ -38,7 +38,7 @@ import { getGuidedUnitContext } from "../guided-unit-context.js";
38
38
  import { registerPlanMilestoneSchemaRecovery } from "./plan-milestone-schema-recovery.js";
39
39
  import { AUTO_UNIT_SCOPED_TOOLS, RUN_UAT_BROWSER_TOOL_NAMES, isWorkflowAliasTool } from "../auto-unit-tool-scope.js";
40
40
  import { filterToolsForProvider } from "../model-router.js";
41
- import { RUN_UAT_WORKFLOW_TOOL_NAMES } from "../tool-presentation-plan.js";
41
+ import { RUN_UAT_READ_ONLY_TOOL_NAMES, RUN_UAT_WORKFLOW_TOOL_NAMES } from "../tool-presentation-plan.js";
42
42
 
43
43
  let approvalQuestionAbortInFlight = false;
44
44
 
@@ -252,9 +252,26 @@ export function buildRunUatGsdToolSet(
252
252
  ): string[] {
253
253
  const scoped = resolveScopedToolNames(
254
254
  [...activeToolNames, ...registeredToolNames],
255
- [...RUN_UAT_WORKFLOW_TOOL_NAMES, "subagent", ...RUN_UAT_BROWSER_TOOL_NAMES],
255
+ [
256
+ ...RUN_UAT_WORKFLOW_TOOL_NAMES,
257
+ ...RUN_UAT_READ_ONLY_TOOL_NAMES,
258
+ "subagent",
259
+ ...RUN_UAT_BROWSER_TOOL_NAMES,
260
+ ],
256
261
  );
257
- return [...new Set(scoped)];
262
+ const resolved = [...new Set(scoped)];
263
+
264
+ const unresolved = RUN_UAT_WORKFLOW_TOOL_NAMES.filter(
265
+ (tool) => !resolved.some((name) => name === tool || (name.startsWith("mcp__") && name.endsWith(`__${tool}`))),
266
+ );
267
+ if (unresolved.length > 0) {
268
+ safetyLogWarning(
269
+ "bootstrap",
270
+ `buildRunUatGsdToolSet: required run-uat workflow tool(s) not found in active/registered surface: ${unresolved.join(", ")}. Session may lack gsd-workflow MCP connection.`,
271
+ );
272
+ }
273
+
274
+ return resolved;
258
275
  }
259
276
 
260
277
  export function buildMinimalGsdWorkflowToolSet(
@@ -480,22 +497,30 @@ function isContextDraftSummarySave(toolName: string, input: unknown): boolean {
480
497
  return (input as { artifact_type?: unknown }).artifact_type === "CONTEXT-DRAFT";
481
498
  }
482
499
 
500
+ function withDepthGateDisplayReason<T extends { block: boolean; reason?: string }>(
501
+ result: T,
502
+ displayReason = "Depth confirmation is waiting for your answer.",
503
+ ): T & { displayReason?: string } {
504
+ if (!result.block) return result;
505
+ return { ...result, displayReason };
506
+ }
507
+
483
508
  function shouldBlockDeferredApprovalTool(
484
509
  toolName: string,
485
510
  input: unknown,
486
511
  basePath: string,
487
- ): { block: boolean; reason?: string } {
512
+ ): { block: boolean; reason?: string; displayReason?: string } {
488
513
  if (deferredApprovalGate?.basePath !== basePath) return { block: false };
489
514
  if (toolName === "ask_user_questions") return { block: false };
490
515
  if (isContextDraftSummarySave(toolName, input)) return { block: false };
491
- return {
516
+ return withDepthGateDisplayReason({
492
517
  block: true,
493
518
  reason: [
494
519
  `HARD BLOCK: Approval question "${deferredApprovalGate.gateId}" has been shown to the user.`,
495
520
  `Only CONTEXT-DRAFT persistence may finish in this same assistant turn.`,
496
521
  `Wait for the user's answer before calling additional tools.`,
497
522
  ].join(" "),
498
- };
523
+ });
499
524
  }
500
525
 
501
526
  export function resolveNotificationStoreBasePath(basePath: string): string {
@@ -564,6 +589,17 @@ export function registerHooks(
564
589
  if (isAutoActive() || preserveCloseoutSurface) {
565
590
  ctx.ui.setWidget("gsd-health", undefined);
566
591
  }
592
+ // Cold start after /quit relaunches with cwd at the project root. When
593
+ // auto-mode is neither active nor paused (its own resume path re-enters the
594
+ // worktree with a lease check — auto.ts:3032), proactively chdir back into
595
+ // the active milestone's worktree so subsequent work isn't stranded at the
596
+ // root. Best-effort and a no-op when already inside a worktree.
597
+ if (!isAutoActive() && !isAutoPaused() && !preserveCloseoutSurface) {
598
+ try {
599
+ const { reenterActiveWorktreeIfNeeded } = await import("../worktree-reentry.js");
600
+ await reenterActiveWorktreeIfNeeded(basePath);
601
+ } catch { /* non-fatal */ }
602
+ }
567
603
  });
568
604
 
569
605
  pi.on("session_switch", async (_event, ctx) => {
@@ -917,7 +953,7 @@ export function registerHooks(
917
953
  "Depth confirmation is waiting for your answer — pausing auto-mode.",
918
954
  );
919
955
  }
920
- return bashGuard;
956
+ return withDepthGateDisplayReason(bashGuard);
921
957
  }
922
958
  } else {
923
959
  const gateGuard = shouldBlockPendingGate(
@@ -935,7 +971,7 @@ export function registerHooks(
935
971
  "Depth confirmation is waiting for your answer — pausing auto-mode.",
936
972
  );
937
973
  }
938
- return gateGuard;
974
+ return withDepthGateDisplayReason(gateGuard);
939
975
  }
940
976
  }
941
977
  }
@@ -1040,7 +1076,9 @@ export function registerHooks(
1040
1076
  isQueuePhaseActive(discussionBasePath),
1041
1077
  discussionBasePath,
1042
1078
  );
1043
- if (result.block) return result;
1079
+ if (result.block) {
1080
+ return withDepthGateDisplayReason(result, "Depth check required before writing milestone context.");
1081
+ }
1044
1082
  });
1045
1083
 
1046
1084
  // ── Safety harness: evidence collection + destructive command blocking ──
@@ -1278,17 +1316,25 @@ export function registerHooks(
1278
1316
  if (isAutoActive()) {
1279
1317
  try {
1280
1318
  const { loadEffectiveGSDPreferences } = await import("../preferences.js");
1319
+ const {
1320
+ createObservationMask,
1321
+ createResponsesInputObservationMask,
1322
+ truncateContextResultMessages,
1323
+ truncateResponsesInputResultItems,
1324
+ } = await import("../context-masker.js");
1281
1325
  const prefs = loadEffectiveGSDPreferences();
1282
1326
  const cmConfig = prefs?.preferences.context_management;
1283
1327
 
1284
1328
  // Observation masking: replace old tool results with placeholders
1285
1329
  if (cmConfig?.observation_masking !== false) {
1286
1330
  const keepTurns = cmConfig?.observation_mask_turns ?? 8;
1287
- const { createObservationMask } = await import("../context-masker.js");
1288
- const mask = createObservationMask(keepTurns);
1289
1331
  const messages = payload.messages;
1290
1332
  if (Array.isArray(messages)) {
1291
- payload.messages = mask(messages);
1333
+ payload.messages = createObservationMask(keepTurns)(messages);
1334
+ }
1335
+ const input = payload.input;
1336
+ if (Array.isArray(input)) {
1337
+ payload.input = createResponsesInputObservationMask(keepTurns)(input);
1292
1338
  }
1293
1339
  }
1294
1340
 
@@ -1298,23 +1344,11 @@ export function registerHooks(
1298
1344
  const maxChars = cmConfig?.tool_result_max_chars ?? 800;
1299
1345
  const msgs = payload.messages;
1300
1346
  if (Array.isArray(msgs)) {
1301
- payload.messages = msgs.map((msg: Record<string, unknown>) => {
1302
- // Match toolResult messages (role: "toolResult", content is array of content blocks)
1303
- if (msg?.role === "toolResult" && Array.isArray(msg.content)) {
1304
- const blocks = msg.content as Array<Record<string, unknown>>;
1305
- const totalLen = blocks.reduce((sum: number, b) => sum + (typeof b.text === "string" ? b.text.length : 0), 0);
1306
- if (totalLen > maxChars) {
1307
- const truncated = blocks.map(b => {
1308
- if (typeof b.text === "string" && b.text.length > maxChars) {
1309
- return { ...b, text: b.text.slice(0, maxChars) + "\n…[truncated]" };
1310
- }
1311
- return b;
1312
- });
1313
- return { ...msg, content: truncated };
1314
- }
1315
- }
1316
- return msg;
1317
- });
1347
+ payload.messages = truncateContextResultMessages(msgs as any, maxChars);
1348
+ }
1349
+ const input = payload.input;
1350
+ if (Array.isArray(input)) {
1351
+ payload.input = truncateResponsesInputResultItems(input as any, maxChars);
1318
1352
  }
1319
1353
  } catch { /* non-fatal */ }
1320
1354
  }
@@ -61,7 +61,7 @@ export const BUNDLED_SKILL_TRIGGERS: Array<{ trigger: string; skill: string }> =
61
61
  { trigger: "Core Web Vitals — fix LCP, CLS, INP; layout shifts; page experience optimization", skill: "core-web-vitals" },
62
62
  { trigger: "GitHub Actions CI/CD — write, run, and debug workflow files; live syntax and run monitoring", skill: "github-workflows" },
63
63
  { trigger: "Comprehensive web quality audit — performance, accessibility, SEO, and best-practices (Lighthouse-style)", skill: "web-quality-audit" },
64
- { trigger: "gsd-browser UAT default browser MCP/CLI for real UI verification, screenshots, assertions, console/network diagnostics", skill: "gsd-browser" },
64
+ { trigger: "gsd-browser opt-in and External MCP UAT screenshots, assertions, console/network diagnostics", skill: "gsd-browser" },
65
65
  { trigger: "Browser automation — open sites, fill forms, click, screenshot, scrape, or test web apps programmatically", skill: "agent-browser" },
66
66
  { trigger: "Review UI code for Web Interface Guidelines compliance — UX, design, and accessibility patterns", skill: "web-design-guidelines" },
67
67
  { trigger: "UI/UX patterns reference — animations, CSS, typography, prefetching, icons (file:line findings)", skill: "userinterface-wiki" },