gsd-pi 2.70.1-dev.ec24142 → 2.71.0-dev.06b86c6

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 (161) hide show
  1. package/README.md +24 -17
  2. package/dist/cli.js +12 -3
  3. package/dist/mcp-server.js +6 -6
  4. package/dist/provider-migrations.d.ts +10 -0
  5. package/dist/provider-migrations.js +12 -0
  6. package/dist/resource-loader.js +136 -13
  7. package/dist/resources/GSD-WORKFLOW.md +1 -1
  8. package/dist/resources/extensions/gsd/auto-start.js +1 -1
  9. package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
  10. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +2 -0
  11. package/dist/resources/extensions/gsd/bootstrap/system-context.js +6 -0
  12. package/dist/resources/extensions/gsd/commands/context.js +15 -6
  13. package/dist/resources/extensions/gsd/commands/dispatcher.js +12 -2
  14. package/dist/resources/extensions/gsd/custom-workflow-engine.js +16 -12
  15. package/dist/resources/extensions/gsd/dispatch-guard.js +18 -1
  16. package/dist/resources/extensions/gsd/error-classifier.js +1 -1
  17. package/dist/resources/extensions/gsd/file-lock.js +60 -0
  18. package/dist/resources/extensions/gsd/notification-store.js +21 -1
  19. package/dist/resources/extensions/gsd/notification-widget.js +1 -1
  20. package/dist/resources/extensions/gsd/pre-execution-checks.js +35 -2
  21. package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  22. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -0
  23. package/dist/resources/extensions/gsd/prompts/execute-task.md +20 -19
  24. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  25. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +2 -0
  26. package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  27. package/dist/resources/extensions/gsd/prompts/queue.md +3 -2
  28. package/dist/resources/extensions/gsd/prompts/system.md +1 -0
  29. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -1
  30. package/dist/resources/extensions/gsd/state.js +234 -332
  31. package/dist/resources/extensions/gsd/workflow-events.js +25 -13
  32. package/dist/resources/skills/create-skill/SKILL.md +2 -0
  33. package/dist/web/standalone/.next/BUILD_ID +1 -1
  34. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  35. package/dist/web/standalone/.next/build-manifest.json +2 -2
  36. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  37. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  38. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  46. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/index.html +1 -1
  54. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  61. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  62. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  63. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  64. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  65. package/package.json +1 -1
  66. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  67. package/packages/mcp-server/dist/workflow-tools.js +21 -11
  68. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  69. package/packages/mcp-server/src/workflow-tools.test.ts +110 -0
  70. package/packages/mcp-server/src/workflow-tools.ts +31 -11
  71. package/packages/pi-ai/dist/providers/amazon-bedrock.js +11 -2
  72. package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  73. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts +4 -1
  74. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  75. package/packages/pi-ai/dist/providers/anthropic-shared.js +8 -3
  76. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  77. package/packages/pi-ai/dist/providers/anthropic-shared.test.js +44 -1
  78. package/packages/pi-ai/dist/providers/anthropic-shared.test.js.map +1 -1
  79. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  80. package/packages/pi-ai/dist/providers/openai-completions.js +11 -0
  81. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  82. package/packages/pi-ai/src/providers/amazon-bedrock.ts +13 -1
  83. package/packages/pi-ai/src/providers/anthropic-shared.test.ts +55 -1
  84. package/packages/pi-ai/src/providers/anthropic-shared.ts +14 -3
  85. package/packages/pi-ai/src/providers/openai-completions.ts +14 -0
  86. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +202 -1
  87. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  88. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts +19 -2
  89. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  90. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js +50 -1
  91. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  92. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  93. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +90 -2
  94. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  95. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
  96. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  97. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  98. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +6 -0
  99. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  100. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +57 -1
  101. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  102. package/packages/pi-coding-agent/package.json +1 -1
  103. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +249 -1
  104. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.ts +58 -2
  105. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +96 -2
  106. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -0
  107. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +65 -1
  108. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.d.ts +2 -0
  109. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.d.ts.map +1 -0
  110. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +66 -0
  111. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -0
  112. package/packages/pi-tui/dist/components/markdown.d.ts +3 -0
  113. package/packages/pi-tui/dist/components/markdown.d.ts.map +1 -1
  114. package/packages/pi-tui/dist/components/markdown.js +17 -1
  115. package/packages/pi-tui/dist/components/markdown.js.map +1 -1
  116. package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +75 -0
  117. package/packages/pi-tui/src/components/markdown.ts +22 -1
  118. package/pkg/package.json +1 -1
  119. package/src/resources/GSD-WORKFLOW.md +1 -1
  120. package/src/resources/extensions/gsd/auto-start.ts +1 -1
  121. package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
  122. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +2 -0
  123. package/src/resources/extensions/gsd/bootstrap/system-context.ts +7 -0
  124. package/src/resources/extensions/gsd/commands/context.ts +16 -5
  125. package/src/resources/extensions/gsd/commands/dispatcher.ts +14 -2
  126. package/src/resources/extensions/gsd/custom-workflow-engine.ts +19 -14
  127. package/src/resources/extensions/gsd/dispatch-guard.ts +18 -1
  128. package/src/resources/extensions/gsd/error-classifier.ts +1 -1
  129. package/src/resources/extensions/gsd/file-lock.ts +59 -0
  130. package/src/resources/extensions/gsd/notification-store.ts +19 -1
  131. package/src/resources/extensions/gsd/notification-widget.ts +1 -1
  132. package/src/resources/extensions/gsd/pre-execution-checks.ts +39 -2
  133. package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  134. package/src/resources/extensions/gsd/prompts/discuss.md +2 -0
  135. package/src/resources/extensions/gsd/prompts/execute-task.md +20 -19
  136. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  137. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +2 -0
  138. package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  139. package/src/resources/extensions/gsd/prompts/queue.md +3 -2
  140. package/src/resources/extensions/gsd/prompts/system.md +1 -0
  141. package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -1
  142. package/src/resources/extensions/gsd/state.ts +274 -344
  143. package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +28 -0
  144. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +39 -0
  145. package/src/resources/extensions/gsd/tests/complete-slice-prompt-task-summary-layout.test.ts +18 -0
  146. package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +436 -0
  147. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +27 -0
  148. package/src/resources/extensions/gsd/tests/execute-task-prompt-existing-artifact-guard.test.ts +33 -0
  149. package/src/resources/extensions/gsd/tests/file-lock.test.ts +103 -0
  150. package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +73 -0
  151. package/src/resources/extensions/gsd/tests/notification-store.test.ts +17 -0
  152. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +25 -0
  153. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +49 -0
  154. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +19 -0
  155. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +7 -0
  156. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +7 -0
  157. package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +18 -0
  158. package/src/resources/extensions/gsd/workflow-events.ts +34 -25
  159. package/src/resources/skills/create-skill/SKILL.md +2 -0
  160. /package/dist/web/standalone/.next/static/{20e8bFnNjxQJflHNodEve → dYVdRaunb2ZSEA8fjkT-V}/_buildManifest.js +0 -0
  161. /package/dist/web/standalone/.next/static/{20e8bFnNjxQJflHNodEve → dYVdRaunb2ZSEA8fjkT-V}/_ssgManifest.js +0 -0
@@ -92,7 +92,7 @@ export function clearInFlightTools(): void {
92
92
  * handler. When these errors occur, retrying the same unit will produce the same
93
93
  * failure, so the retry loop must be broken.
94
94
  */
95
- const TOOL_INVOCATION_ERROR_RE = /Validation failed for tool|Expected ',' or '\}' in JSON|Unexpected end of JSON|Unexpected token.*in JSON/i;
95
+ const TOOL_INVOCATION_ERROR_RE = /Validation failed for tool|Expected ',' or '\}'(?: after property value)?(?: in JSON)?|Unexpected end of JSON|Unexpected token.*in JSON/i;
96
96
 
97
97
  /**
98
98
  * Returns true if the error message indicates a tool invocation failure due to
@@ -121,6 +121,8 @@ export function registerHooks(pi: ExtensionAPI): void {
121
121
  return { cancel: true };
122
122
  }
123
123
  const basePath = process.cwd();
124
+ const { ensureDbOpen } = await import("./dynamic-tools.js");
125
+ await ensureDbOpen();
124
126
  const state = await deriveState(basePath);
125
127
  if (!state.activeMilestone || !state.activeSlice || !state.activeTask) return;
126
128
  if (state.phase !== "executing") return;
@@ -293,6 +293,11 @@ function buildWorktreeContextBlock(): string {
293
293
  const RESUME_INTENT_PATTERNS = /^(continue|resume|ok|go|go ahead|proceed|keep going|carry on|next|yes|yeah|yep|sure|do it|let's go|pick up where you left off)$/;
294
294
 
295
295
  async function buildGuidedExecuteContextInjection(prompt: string, basePath: string): Promise<string | null> {
296
+ const ensureStateDbOpen = async () => {
297
+ const { ensureDbOpen } = await import("./dynamic-tools.js");
298
+ await ensureDbOpen();
299
+ };
300
+
296
301
  const executeMatch = prompt.match(/Execute the next task:\s+(T\d+)\s+\("([^"]+)"\)\s+in slice\s+(S\d+)\s+of milestone\s+(M\d+(?:-[a-z0-9]{6})?)/i);
297
302
  if (executeMatch) {
298
303
  const [, taskId, taskTitle, sliceId, milestoneId] = executeMatch;
@@ -302,6 +307,7 @@ async function buildGuidedExecuteContextInjection(prompt: string, basePath: stri
302
307
  const resumeMatch = prompt.match(/Resume interrupted work\.[\s\S]*?slice\s+(S\d+)\s+of milestone\s+(M\d+(?:-[a-z0-9]{6})?)/i);
303
308
  if (resumeMatch) {
304
309
  const [, sliceId, milestoneId] = resumeMatch;
310
+ await ensureStateDbOpen();
305
311
  const state = await deriveState(basePath);
306
312
  if (state.activeMilestone?.id === milestoneId && state.activeSlice?.id === sliceId && state.activeTask) {
307
313
  return buildTaskExecutionContextInjection(basePath, milestoneId, sliceId, state.activeTask.id, state.activeTask.title);
@@ -317,6 +323,7 @@ async function buildGuidedExecuteContextInjection(prompt: string, basePath: stri
317
323
  // replanning, gate evaluation, or other non-execution phases.
318
324
  const trimmed = prompt.trim().toLowerCase().replace(/[.!?,]+$/g, "");
319
325
  if (RESUME_INTENT_PATTERNS.test(trimmed)) {
326
+ await ensureStateDbOpen();
320
327
  const state = await deriveState(basePath);
321
328
  if (state.phase === "executing" && state.activeTask && state.activeMilestone && state.activeSlice) {
322
329
  return buildTaskExecutionContextInjection(
@@ -1,7 +1,7 @@
1
1
  import type { ExtensionAPI, ExtensionCommandContext } from "@gsd/pi-coding-agent";
2
2
 
3
3
  import { checkRemoteAutoSession, isAutoActive, isAutoPaused, stopAutoRemote } from "../auto.js";
4
- import { assertSafeDirectory } from "../validate-directory.js";
4
+ import { validateDirectory } from "../validate-directory.js";
5
5
  import { resolveProjectRoot } from "../worktree.js";
6
6
  import { showNextAction } from "../../shared/tui.js";
7
7
  import { handleStatus } from "./handlers/core.js";
@@ -12,6 +12,17 @@ export interface GsdDispatchContext {
12
12
  trimmed: string;
13
13
  }
14
14
 
15
+ /**
16
+ * Typed error for when GSD is run outside a valid project directory.
17
+ * Command handlers catch this to show a friendly message instead of a raw exception.
18
+ */
19
+ export class GSDNoProjectError extends Error {
20
+ constructor(reason: string) {
21
+ super(reason);
22
+ this.name = "GSDNoProjectError";
23
+ }
24
+ }
25
+
15
26
  export function projectRoot(): string {
16
27
  let cwd: string;
17
28
  try {
@@ -21,10 +32,10 @@ export function projectRoot(): string {
21
32
  cwd = process.env.HOME ?? "/";
22
33
  }
23
34
  const root = resolveProjectRoot(cwd);
24
- if (root !== cwd) {
25
- assertSafeDirectory(cwd);
26
- } else {
27
- assertSafeDirectory(root);
35
+ const pathToCheck = root !== cwd ? cwd : root;
36
+ const result = validateDirectory(pathToCheck);
37
+ if (result.severity === "blocked") {
38
+ throw new GSDNoProjectError(result.reason ?? "GSD must be run inside a project directory.");
28
39
  }
29
40
  return root;
30
41
  }
@@ -1,5 +1,6 @@
1
1
  import type { ExtensionAPI, ExtensionCommandContext } from "@gsd/pi-coding-agent";
2
2
 
3
+ import { GSDNoProjectError } from "./context.js";
3
4
  import { handleAutoCommand } from "./handlers/auto.js";
4
5
  import { handleCoreCommand } from "./handlers/core.js";
5
6
  import { handleOpsCommand } from "./handlers/ops.js";
@@ -21,10 +22,21 @@ export async function handleGSDCommand(
21
22
  () => handleOpsCommand(trimmed, ctx, pi),
22
23
  ];
23
24
 
24
- for (const handler of handlers) {
25
- if (await handler()) {
25
+ try {
26
+ for (const handler of handlers) {
27
+ if (await handler()) {
28
+ return;
29
+ }
30
+ }
31
+ } catch (err) {
32
+ if (err instanceof GSDNoProjectError) {
33
+ ctx.ui.notify(
34
+ `${err.message} \`cd\` into a project directory first.`,
35
+ "warning",
36
+ );
26
37
  return;
27
38
  }
39
+ throw err;
28
40
  }
29
41
 
30
42
  ctx.ui.notify(`Unknown: /gsd ${trimmed}. Run /gsd help for available commands.`, "warning");
@@ -34,6 +34,7 @@ import {
34
34
  import { injectContext } from "./context-injector.js";
35
35
  import type { WorkflowDefinition, StepDefinition } from "./definition-loader.js";
36
36
  import { parseUnitId } from "./unit-id.js";
37
+ import { withFileLock } from "./file-lock.js";
37
38
 
38
39
  /** Read and parse the frozen DEFINITION.yaml from a run directory. */
39
40
  export function readFrozenDefinition(runDir: string): WorkflowDefinition {
@@ -179,24 +180,28 @@ export class CustomWorkflowEngine implements WorkflowEngine {
179
180
  state: EngineState,
180
181
  completedStep: CompletedStep,
181
182
  ): Promise<ReconcileResult> {
182
- // Re-read the graph from disk so we do not overwrite concurrent
183
- // workflow edits with a stale in-memory snapshot from deriveState().
184
- const graph = readGraph(this.runDir);
183
+ const graphPath = join(this.runDir, "GRAPH.yaml");
185
184
 
186
- // Extract stepId from "<workflowName>/<stepId>"
187
- const { milestone, slice, task } = parseUnitId(completedStep.unitId);
188
- const stepId = task ?? slice ?? milestone;
185
+ return await withFileLock(graphPath, () => {
186
+ // Re-read the graph from disk so we do not overwrite concurrent
187
+ // workflow edits with a stale in-memory snapshot from deriveState().
188
+ const graph = readGraph(this.runDir);
189
189
 
190
- const updatedGraph = markStepComplete(graph, stepId);
191
- writeGraph(this.runDir, updatedGraph);
190
+ // Extract stepId from "<workflowName>/<stepId>"
191
+ const { milestone, slice, task } = parseUnitId(completedStep.unitId);
192
+ const stepId = task ?? slice ?? milestone;
192
193
 
193
- const allDone = updatedGraph.steps.every(
194
- (s) => s.status === "complete" || s.status === "expanded",
195
- );
194
+ const updatedGraph = markStepComplete(graph, stepId);
195
+ writeGraph(this.runDir, updatedGraph);
196
196
 
197
- return {
198
- outcome: allDone ? "milestone-complete" : "continue",
199
- };
197
+ const allDone = updatedGraph.steps.every(
198
+ (s) => s.status === "complete" || s.status === "expanded",
199
+ );
200
+
201
+ return {
202
+ outcome: allDone ? "milestone-complete" : "continue",
203
+ };
204
+ });
200
205
  }
201
206
 
202
207
  /**
@@ -107,10 +107,27 @@ export function getPriorSliceCompletionBlocker(
107
107
  // it may be a cross-milestone reference handled elsewhere.
108
108
  }
109
109
  } else {
110
+ // Positional fallback is only a heuristic for legacy slices with no
111
+ // declared dependencies. Skip any earlier slice that depends on the
112
+ // target, directly or transitively, or we can deadlock a valid zero-dep
113
+ // slice behind its own downstream dependents (#3720).
114
+ const reverseDependents = new Set<string>();
115
+ let changed = true;
116
+ while (changed) {
117
+ changed = false;
118
+ for (const slice of slices) {
119
+ if (reverseDependents.has(slice.id)) continue;
120
+ if (slice.depends.some((depId) => depId === targetSid || reverseDependents.has(depId))) {
121
+ reverseDependents.add(slice.id);
122
+ changed = true;
123
+ }
124
+ }
125
+ }
126
+
110
127
  const targetIndex = slices.findIndex((slice) => slice.id === targetSid);
111
128
  const incomplete = slices
112
129
  .slice(0, targetIndex)
113
- .find((slice) => !slice.done);
130
+ .find((slice) => !slice.done && !reverseDependents.has(slice.id));
114
131
  if (incomplete) {
115
132
  return `Cannot dispatch ${unitType} ${unitId}: earlier slice ${targetMid}/${incomplete.id} is not complete.`;
116
133
  }
@@ -47,7 +47,7 @@ const RATE_LIMIT_RE = /rate.?limit|too many requests|429/i;
47
47
  const NETWORK_RE = /network|ECONNRESET|ETIMEDOUT|ECONNREFUSED|socket hang up|fetch failed|connection.*reset|dns/i;
48
48
  const SERVER_RE = /internal server error|500|502|503|overloaded|server_error|api_error|service.?unavailable/i;
49
49
  // ECONNRESET/ECONNREFUSED are in NETWORK_RE (same-model retry first).
50
- const CONNECTION_RE = /terminated|connection.?refused|other side closed|EPIPE|network.?(?:is\s+)?unavailable|stream_exhausted(?:_without_result)?/i;
50
+ const CONNECTION_RE = /terminated|connection.?(?:refused|error)|other side closed|EPIPE|network.?(?:is\s+)?unavailable|stream_exhausted(?:_without_result)?/i;
51
51
  // Catch-all for V8 JSON.parse errors: all modern variants end with "in JSON at position \d+".
52
52
  // This eliminates the need to enumerate every error message variant individually.
53
53
  const STREAM_RE = /in JSON at position \d+|Unexpected end of JSON|SyntaxError.*JSON/i;
@@ -0,0 +1,59 @@
1
+ import { existsSync } from "node:fs";
2
+
3
+ function _require(name: string) {
4
+ try {
5
+ return require(name);
6
+ } catch {
7
+ try {
8
+ const gsdPiRequire = require("module").createRequire(
9
+ require("path").join(process.cwd(), "node_modules", "gsd-pi", "index.js")
10
+ );
11
+ return gsdPiRequire(name);
12
+ } catch {
13
+ return null;
14
+ }
15
+ }
16
+ }
17
+
18
+ export function withFileLockSync<T>(filePath: string, fn: () => T): T {
19
+ const lockfile = _require("proper-lockfile");
20
+ if (!lockfile) return fn();
21
+
22
+ if (!existsSync(filePath)) return fn();
23
+
24
+ try {
25
+ const release = lockfile.lockSync(filePath, { retries: 5, stale: 10000 });
26
+ try {
27
+ return fn();
28
+ } finally {
29
+ release();
30
+ }
31
+ } catch (err: any) {
32
+ if (err.code === "ELOCKED") {
33
+ // Could not get lock after retries, let's fallback to un-locked instead of crashing the whole state machine
34
+ return fn();
35
+ }
36
+ throw err;
37
+ }
38
+ }
39
+
40
+ export async function withFileLock<T>(filePath: string, fn: () => Promise<T> | T): Promise<T> {
41
+ const lockfile = _require("proper-lockfile");
42
+ if (!lockfile) return await fn();
43
+
44
+ if (!existsSync(filePath)) return await fn();
45
+
46
+ try {
47
+ const release = await lockfile.lock(filePath, { retries: 5, stale: 10000 });
48
+ try {
49
+ return await fn();
50
+ } finally {
51
+ await release();
52
+ }
53
+ } catch (err: any) {
54
+ if (err.code === "ELOCKED") {
55
+ return await fn();
56
+ }
57
+ throw err;
58
+ }
59
+ }
@@ -26,12 +26,15 @@ export interface NotificationEntry {
26
26
  const MAX_ENTRIES = 500;
27
27
  const FILENAME = "notifications.jsonl";
28
28
  const LOCKFILE = "notifications.lock";
29
+ const DEDUP_WINDOW_MS = 30_000;
30
+ const DEDUP_PRUNE_THRESHOLD = 200;
29
31
 
30
32
  // ─── Module State ───────────────────────────────────────────────────────
31
33
 
32
34
  let _basePath: string | null = null;
33
35
  let _lineCount = 0; // Hint for rotation — not authoritative for public API
34
36
  let _suppressCount = 0;
37
+ let _recentMessageTimestamps = new Map<string, number>();
35
38
 
36
39
  // ─── Public API ─────────────────────────────────────────────────────────
37
40
 
@@ -40,6 +43,9 @@ let _suppressCount = 0;
40
43
  * project root. Seeds in-memory counters from the existing file on disk.
41
44
  */
42
45
  export function initNotificationStore(basePath: string): void {
46
+ if (_basePath !== basePath) {
47
+ _recentMessageTimestamps.clear();
48
+ }
43
49
  _basePath = basePath;
44
50
  // Seed line count hint for rotation — public counters read from disk
45
51
  _lineCount = _readEntriesFromDisk(basePath).length;
@@ -56,12 +62,23 @@ export function appendNotification(
56
62
  ): void {
57
63
  if (!_basePath) return;
58
64
  if (_suppressCount > 0) return;
65
+ const persistedMessage = message.length > 500 ? message.slice(0, 500) + "…" : message;
66
+ const dedupKey = `${_basePath}:${severity}:${source}:${persistedMessage}`;
67
+ const now = Date.now();
68
+ const lastSeen = _recentMessageTimestamps.get(dedupKey);
69
+ if (lastSeen !== undefined && now - lastSeen < DEDUP_WINDOW_MS) return;
70
+ _recentMessageTimestamps.set(dedupKey, now);
71
+ if (_recentMessageTimestamps.size > DEDUP_PRUNE_THRESHOLD) {
72
+ for (const [key, ts] of _recentMessageTimestamps) {
73
+ if (now - ts > DEDUP_WINDOW_MS) _recentMessageTimestamps.delete(key);
74
+ }
75
+ }
59
76
 
60
77
  const entry: NotificationEntry = {
61
78
  id: randomUUID(),
62
79
  ts: new Date().toISOString(),
63
80
  severity,
64
- message: message.length > 500 ? message.slice(0, 500) + "…" : message,
81
+ message: persistedMessage,
65
82
  source,
66
83
  read: false,
67
84
  };
@@ -181,6 +198,7 @@ export function _resetNotificationStore(): void {
181
198
  _basePath = null;
182
199
  _lineCount = 0;
183
200
  _suppressCount = 0;
201
+ _recentMessageTimestamps = new Map();
184
202
  }
185
203
 
186
204
  // ─── Internal ───────────────────────────────────────────────────────────
@@ -25,7 +25,7 @@ export function buildNotificationWidgetLines(): string[] {
25
25
  ? latest.message.slice(0, msgMax - 1) + "…"
26
26
  : latest.message;
27
27
 
28
- return [` ${icon} [${badge}] ${truncated} (${formatShortcut("Ctrl+Alt+N")} to view)`];
28
+ return [` ${icon} [${badge}] ${truncated} (${formatShortcut("Ctrl+Alt+N")} or /gsd notifications)`];
29
29
  }
30
30
 
31
31
  // ─── Widget init ────────────────────────────────────────────────────────
@@ -280,6 +280,38 @@ function extractPathFromAnnotation(raw: string): string {
280
280
  return trimmed.replace(/`/g, "");
281
281
  }
282
282
 
283
+ /**
284
+ * Planning units sometimes use task.inputs for prose like "Current enum shape"
285
+ * instead of concrete file paths. Those entries should not fail path checks.
286
+ * Keep validation for anything that still looks like a real file reference:
287
+ * explicit backticks, globs, separators, dot-paths, or single-token basenames
288
+ * like Dockerfile.
289
+ */
290
+ function shouldValidateInputAsPath(raw: string): boolean {
291
+ const trimmed = raw.trim();
292
+ if (!trimmed) return false;
293
+
294
+ if (/^`+[^`]+`+/.test(trimmed)) {
295
+ return true;
296
+ }
297
+
298
+ const candidate = extractPathFromAnnotation(trimmed);
299
+ if (!candidate) return false;
300
+
301
+ if (!/\s/.test(candidate)) {
302
+ return true;
303
+ }
304
+
305
+ return (
306
+ candidate.startsWith("/") ||
307
+ candidate.startsWith("./") ||
308
+ candidate.startsWith("../") ||
309
+ candidate.startsWith("~/") ||
310
+ /[\\/]/.test(candidate) ||
311
+ /[*?[\]{}]/.test(candidate)
312
+ );
313
+ }
314
+
283
315
  /**
284
316
  * Build a set of files that will be created by tasks up to (but not including) taskIndex.
285
317
  * All paths are normalized for consistent comparison.
@@ -318,6 +350,7 @@ export function checkFilePathConsistency(
318
350
  for (const file of filesToCheck) {
319
351
  // Skip empty strings
320
352
  if (!file.trim()) continue;
353
+ if (!shouldValidateInputAsPath(file)) continue;
321
354
 
322
355
  // Normalize path for consistent comparison
323
356
  const normalizedFile = normalizeFilePath(file);
@@ -354,7 +387,7 @@ export function checkFilePathConsistency(
354
387
  */
355
388
  export function checkTaskOrdering(
356
389
  tasks: TaskRow[],
357
- _basePath: string
390
+ basePath: string
358
391
  ): PreExecutionCheckJSON[] {
359
392
  const results: PreExecutionCheckJSON[] = [];
360
393
 
@@ -378,9 +411,13 @@ export function checkTaskOrdering(
378
411
  const filesToCheck = [...task.inputs];
379
412
 
380
413
  for (const file of filesToCheck) {
414
+ if (!shouldValidateInputAsPath(file)) continue;
415
+
381
416
  const normalizedFile = normalizeFilePath(file);
382
417
  const creator = fileCreators.get(normalizedFile);
383
- if (creator && creator.index > i) {
418
+ const absolutePath = resolve(basePath, normalizedFile);
419
+ const existsOnDisk = existsSync(absolutePath);
420
+ if (creator && creator.index > i && !existsOnDisk) {
384
421
  // Task reads file that is created later — impossible ordering
385
422
  results.push({
386
423
  category: "file",
@@ -21,7 +21,7 @@ All relevant context has been preloaded below — the slice plan, all task summa
21
21
  Then:
22
22
  1. Use the **Slice Summary** and **UAT** output templates from the inlined context above
23
23
  2. {{skillActivation}}
24
- 3. Run all slice-level verification checks defined in the slice plan. All must pass before marking the slice done. If any fail, fix them first.
24
+ 3. Run all slice-level verification checks defined in the slice plan. All must pass before marking the slice done. If any fail, fix them first. Task artifacts use a **flat file layout** directly inside `tasks/` (for example `T01-SUMMARY.md`, `T02-SUMMARY.md`) rather than per-task subdirectories. If you need to count or re-read task summaries during verification, use `find .gsd/milestones/{{milestoneId}}/slices/{{sliceId}}/tasks -name "*-SUMMARY.md"` or `ls .gsd/milestones/{{milestoneId}}/slices/{{sliceId}}/tasks/*-SUMMARY.md`. Never use `tasks/*/SUMMARY.md` — that glob expects subdirectories that do not exist.
25
25
  4. If the slice plan includes observability/diagnostic surfaces, confirm they work. Skip this for simple slices that don't have observability sections.
26
26
  5. If the slice involved runtime behavior, fill the **Operational Readiness** section (Q8) in the slice summary: health signal, failure signal, recovery procedure, and monitoring gaps. Omit entirely for simple slices with no runtime concerns.
27
27
  6. If this slice produced evidence that a requirement changed status (Active → Validated, Active → Deferred, etc.), call `gsd_requirement_update` with the requirement ID, updated `status`, and `validation` evidence. Do NOT write `.gsd/REQUIREMENTS.md` directly — the engine renders it from the database.
@@ -35,7 +35,7 @@ Then:
35
35
 
36
36
  **Autonomous execution:** Do not call `ask_user_questions` or `secure_env_collect`. You are running in auto-mode — there is no human available to answer questions. Make reasonable assumptions and document them in the slice summary. If a decision genuinely requires human input, note it in the summary and proceed with the best available option.
37
37
 
38
- **File system safety:** Task summaries are preloaded in the inlined context above. If you need to re-read any of them, use `find .gsd/milestones/{{milestoneId}}/slices/{{sliceId}}/tasks -name "*-SUMMARY.md"` to list file paths first never pass `{{slicePath}}` or any other directory path directly to the `read` tool. The `read` tool only accepts file paths, not directories.
38
+ **File system safety:** Task summaries are preloaded in the inlined context above. Task artifacts use a **flat file layout** — files such as `T01-SUMMARY.md` and `T02-SUMMARY.md` live directly inside the `tasks/` directory, not inside per-task subdirectories like `tasks/T01/SUMMARY.md`. If you need to re-read any of them, use `find .gsd/milestones/{{milestoneId}}/slices/{{sliceId}}/tasks -name "*-SUMMARY.md"` to list file paths first. Never use `tasks/*/SUMMARY.md`, and never pass `{{slicePath}}` or any other directory path directly to the `read` tool. The `read` tool only accepts file paths, not directories.
39
39
 
40
40
  **You MUST call `gsd_complete_slice` with the slice summary and UAT content before finishing. The tool persists to both DB and disk and renders `{{sliceSummaryPath}}` and `{{sliceUatPath}}` automatically.**
41
41
 
@@ -73,6 +73,8 @@ After each round of answers, decide whether you already have enough depth to wri
73
73
 
74
74
  You are a thinking partner, not an interviewer.
75
75
 
76
+ **Turn-taking contract (non-bypassable).** Never fabricate, simulate, or role-play user responses. Never generate fake transcript markers like `[User]`, `[Human]`, or `User:` to invent input. Ask one question round (1-3 questions) per turn, then stop and wait for the user's actual response before continuing. If you use `ask_user_questions`, call it at most once per turn and treat its returned response as the only valid structured user input for that round.
77
+
76
78
  **Start open, follow energy.** Let the user's enthusiasm guide where you dig deeper. If they light up about a particular aspect, explore it. If they're vague about something, that's where you probe.
77
79
 
78
80
  **Challenge vagueness, make abstract concrete.** When the user says something abstract ("it should be smart" / "it needs to handle edge cases" / "good UX"), push for specifics. What does "smart" mean in practice? Which edge cases? What does good UX look like for this specific interaction?
@@ -32,29 +32,30 @@ Then:
32
32
  0. Narrate step transitions, key implementation decisions, and verification outcomes as you work. Keep it terse — one line between tool-call clusters, not between every call — but write complete sentences in user-facing prose, not shorthand notes or scratchpad fragments.
33
33
  1. {{skillActivation}} Follow any activated skills before writing code. If no skills match this task, skip this step.
34
34
  2. Execute the steps in the inlined task plan, adapting minor local mismatches when the surrounding code differs from the planner's snapshot
35
- 3. Build the real thing. If the task plan says "create login endpoint", build an endpoint that actually authenticates against a real store, not one that returns a hardcoded success response. If the task plan says "create dashboard page", build a page that renders real data from the API, not a component with hardcoded props. Stubs and mocks are for tests, not for the shipped feature.
36
- 4. Write or update tests as part of execution tests are verification, not an afterthought. If the slice plan defines test files in its Verification section and this is the first task, create them (they should initially fail).
37
- 5. When implementing non-trivial runtime behavior (async flows, API boundaries, background processes, error paths), add or preserve agent-usable observability. Skip this for simple changes where it doesn't apply.
35
+ 3. Before any `Write` that creates an artifact or output file, check whether that path already exists. If it does, read it first and decide whether the work is already done, should be extended, or truly needs replacement. "Create" in the plan does **not** mean the file is missing a prior session may already have started it.
36
+ 4. Build the real thing. If the task plan says "create login endpoint", build an endpoint that actually authenticates against a real store, not one that returns a hardcoded success response. If the task plan says "create dashboard page", build a page that renders real data from the API, not a component with hardcoded props. Stubs and mocks are for tests, not for the shipped feature.
37
+ 5. Write or update tests as part of execution tests are verification, not an afterthought. If the slice plan defines test files in its Verification section and this is the first task, create them (they should initially fail).
38
+ 6. When implementing non-trivial runtime behavior (async flows, API boundaries, background processes, error paths), add or preserve agent-usable observability. Skip this for simple changes where it doesn't apply.
38
39
 
39
40
  **Background process rule:** Never use bare `command &` to run background processes. The shell's `&` operator leaves stdout/stderr attached to the parent, which causes the Bash tool to hang indefinitely waiting for those streams to close. Always redirect output before backgrounding:
40
41
  - Correct: `command > /dev/null 2>&1 &` or `nohup command > /dev/null 2>&1 &`
41
42
  - Example: `python -m http.server 8080 > /dev/null 2>&1 &` (NOT `python -m http.server 8080 &`)
42
43
  - Preferred: use the `bg_shell` tool if available — it manages process lifecycle correctly without stream-inheritance issues
43
- 6. If the task plan includes a **Failure Modes** section (Q5), implement the error/timeout/malformed handling specified. Verify each dependency's failure path is handled. Skip if the section is absent.
44
- 7. If the task plan includes a **Load Profile** section (Q6), implement protections for the identified 10x breakpoint (connection pooling, rate limiting, pagination, etc.). Skip if absent.
45
- 8. If the task plan includes a **Negative Tests** section (Q7), write the specified negative test cases alongside the happy-path tests — malformed inputs, error paths, and boundary conditions. Skip if absent.
46
- 9. Verify must-haves are met by running concrete checks (tests, commands, observable behaviors)
47
- 10. Run the slice-level verification checks defined in the slice plan's Verification section. Track which pass. On the final task of the slice, all must pass before marking done. On intermediate tasks, partial passes are expected — note which ones pass in the summary.
48
- 11. After the verification gate runs (you'll see gate results in stderr/notify output), populate the `## Verification Evidence` table in your task summary with the check results. Use the `formatEvidenceTable` format: one row per check with command, exit code, verdict (✅ pass / ❌ fail), and duration. If no verification commands were discovered, note that in the section.
49
- 12. If the task touches UI, browser flows, DOM behavior, or user-visible web state:
44
+ 7. If the task plan includes a **Failure Modes** section (Q5), implement the error/timeout/malformed handling specified. Verify each dependency's failure path is handled. Skip if the section is absent.
45
+ 8. If the task plan includes a **Load Profile** section (Q6), implement protections for the identified 10x breakpoint (connection pooling, rate limiting, pagination, etc.). Skip if absent.
46
+ 9. If the task plan includes a **Negative Tests** section (Q7), write the specified negative test cases alongside the happy-path tests — malformed inputs, error paths, and boundary conditions. Skip if absent.
47
+ 10. Verify must-haves are met by running concrete checks (tests, commands, observable behaviors)
48
+ 11. Run the slice-level verification checks defined in the slice plan's Verification section. Track which pass. On the final task of the slice, all must pass before marking done. On intermediate tasks, partial passes are expected — note which ones pass in the summary.
49
+ 12. After the verification gate runs (you'll see gate results in stderr/notify output), populate the `## Verification Evidence` table in your task summary with the check results. Use the `formatEvidenceTable` format: one row per check with command, exit code, verdict (✅ pass / ❌ fail), and duration. If no verification commands were discovered, note that in the section.
50
+ 13. If the task touches UI, browser flows, DOM behavior, or user-visible web state:
50
51
  - exercise the real flow in the browser
51
52
  - prefer `browser_batch` when the next few actions are obvious and sequential
52
53
  - prefer `browser_assert` for explicit pass/fail verification of the intended outcome
53
54
  - use `browser_diff` when an action's effect is ambiguous
54
55
  - use console/network/dialog diagnostics when validating async, stateful, or failure-prone UI
55
56
  - record verification in terms of explicit checks passed/failed, not only prose interpretation
56
- 13. If the task plan includes an Observability Impact section, verify those signals directly. Skip this step if the task plan omits the section.
57
- 14. **If execution is running long or verification fails:**
57
+ 14. If the task plan includes an Observability Impact section, verify those signals directly. Skip this step if the task plan omits the section.
58
+ 15. **If execution is running long or verification fails:**
58
59
 
59
60
  **Context budget:** You have approximately **{{verificationBudget}}** reserved for verification context. If you've used most of your context and haven't finished all steps, stop implementing and prioritize writing the task summary with clear notes on what's done and what remains. A partial summary that enables clean resumption is more valuable than one more half-finished step with no documentation. Never sacrifice summary quality for one more implementation step.
60
61
 
@@ -65,13 +66,13 @@ Then:
65
66
  - Distinguish "I know" from "I assume." Observable facts (the error says X) are strong evidence. Assumptions (this library should work this way) need verification.
66
67
  - Know when to stop. If you've tried 3+ fixes without progress, your mental model is probably wrong. Stop. List what you know for certain. List what you've ruled out. Form fresh hypotheses from there.
67
68
  - Don't fix symptoms. Understand *why* something fails before changing code. A test that passes after a change you don't understand is luck, not a fix.
68
- 15. **Blocker discovery:** If execution reveals that the remaining slice plan is fundamentally invalid — not just a bug or minor deviation, but a plan-invalidating finding like a wrong API, missing capability, or architectural mismatch — set `blocker_discovered: true` in the task summary frontmatter and describe the blocker clearly in the summary narrative. Do NOT set `blocker_discovered: true` for ordinary debugging, minor deviations, or issues that can be fixed within the current task or the remaining plan. This flag triggers an automatic replan of the slice.
69
- 16. If you made an architectural, pattern, library, or observability decision during this task that downstream work should know about, append it to `.gsd/DECISIONS.md` (read the template at `~/.gsd/agent/extensions/gsd/templates/decisions.md` if the file doesn't exist yet). Not every task produces decisions — only append when a meaningful choice was made.
70
- 17. If you discover a non-obvious rule, recurring gotcha, or useful pattern during execution, append it to `.gsd/KNOWLEDGE.md`. Only add entries that would save future agents from repeating your investigation. Don't add obvious things.
71
- 18. Read the template at `~/.gsd/agent/extensions/gsd/templates/task-summary.md`
72
- 19. Use that template to prepare the completion content you will pass to `gsd_complete_task` using the camelCase fields `milestoneId`, `sliceId`, `taskId`, `oneLiner`, `narrative`, `verification`, and `verificationEvidence`. Do **not** manually write `{{taskSummaryPath}}` — the DB-backed tool is the canonical write path and renders the summary file for you.
73
- 20. Call `gsd_complete_task` with milestoneId, sliceId, taskId, and the completion fields derived from the template. This is your final required step — do NOT manually edit PLAN.md checkboxes. The tool marks the task complete, updates the DB, renders `{{taskSummaryPath}}`, and updates PLAN.md automatically.
74
- 21. Do not run git commands — the system reads your task summary after completion and creates a meaningful commit from it (type inferred from title, message from your one-liner, key files from frontmatter). Write a clear, specific one-liner in the summary — it becomes the commit message.
69
+ 16. **Blocker discovery:** If execution reveals that the remaining slice plan is fundamentally invalid — not just a bug or minor deviation, but a plan-invalidating finding like a wrong API, missing capability, or architectural mismatch — set `blocker_discovered: true` in the task summary frontmatter and describe the blocker clearly in the summary narrative. Do NOT set `blocker_discovered: true` for ordinary debugging, minor deviations, or issues that can be fixed within the current task or the remaining plan. This flag triggers an automatic replan of the slice.
70
+ 17. If you made an architectural, pattern, library, or observability decision during this task that downstream work should know about, append it to `.gsd/DECISIONS.md` (read the template at `~/.gsd/agent/extensions/gsd/templates/decisions.md` if the file doesn't exist yet). Not every task produces decisions — only append when a meaningful choice was made.
71
+ 18. If you discover a non-obvious rule, recurring gotcha, or useful pattern during execution, append it to `.gsd/KNOWLEDGE.md`. Only add entries that would save future agents from repeating your investigation. Don't add obvious things.
72
+ 19. Read the template at `~/.gsd/agent/extensions/gsd/templates/task-summary.md`
73
+ 20. Use that template to prepare the completion content you will pass to `gsd_complete_task` using the camelCase fields `milestoneId`, `sliceId`, `taskId`, `oneLiner`, `narrative`, `verification`, and `verificationEvidence`. Do **not** manually write `{{taskSummaryPath}}` — the DB-backed tool is the canonical write path and renders the summary file for you.
74
+ 21. Call `gsd_complete_task` with milestoneId, sliceId, taskId, and the completion fields derived from the template. This is your final required step — do NOT manually edit PLAN.md checkboxes. The tool marks the task complete, updates the DB, renders `{{taskSummaryPath}}`, and updates PLAN.md automatically.
75
+ 22. Do not run git commands — the system reads your task summary after completion and creates a meaningful commit from it (type inferred from title, message from your one-liner, key files from frontmatter). Write a clear, specific one-liner in the summary — it becomes the commit message.
75
76
 
76
77
  All work stays in your working directory: `{{workingDirectory}}`.
77
78
 
@@ -32,6 +32,8 @@ Ask **1–3 questions per round**. Keep each question focused on one of:
32
32
  - **The biggest technical unknowns / risks** — what could fail, what hasn't been proven
33
33
  - **What external systems/services this touches** — APIs, databases, third-party services
34
34
 
35
+ **Never fabricate or simulate user input.** Never generate fake transcript markers like `[User]`, `[Human]`, or `User:`. Ask one question round, then wait for the user's actual response before continuing.
36
+
35
37
  **If `{{structuredQuestionsAvailable}}` is `true`:** use `ask_user_questions` for each round. 1–3 questions per call, each as a separate question object. Keep option labels short (3–5 words). Always include a freeform "Other / let me explain" option. When the user picks that option or writes a long freeform answer, switch to plain text follow-up for that thread before resuming structured questions. **IMPORTANT: Call `ask_user_questions` exactly once per turn. Never make multiple calls with the same or overlapping questions — wait for the user's response before asking the next round.**
36
38
 
37
39
  **If `{{structuredQuestionsAvailable}}` is `false`:** ask questions in plain text. Keep each round to 1–3 focused questions. Wait for answers before asking the next round.
@@ -22,6 +22,8 @@ Do **not** go deep — just enough that your questions reflect what's actually t
22
22
 
23
23
  ### Question rounds
24
24
 
25
+ **Never fabricate or simulate user input.** Never generate fake transcript markers like `[User]`, `[Human]`, or `User:`. Ask one question round, then wait for the user's actual response before continuing.
26
+
25
27
  **If `{{structuredQuestionsAvailable}}` is `true`:** Ask **1–3 questions per round** using `ask_user_questions`. **Call `ask_user_questions` exactly once per turn — never make multiple calls with the same or overlapping questions. Wait for the user's response before asking the next round.**
26
28
  **If `{{structuredQuestionsAvailable}}` is `false`:** Ask **1–3 questions per round** in plain text. Number them and wait for the user's response before asking the next round.
27
29
  Keep each question focused on one of:
@@ -1 +1 @@
1
- Resume interrupted work. Find the continue file (`{{sliceId}}-CONTINUE.md` or `continue.md`) in slice {{sliceId}} of milestone {{milestoneId}}, read it, and use it as the recovery contract for where to pick up. Do **not** delete the continue file immediately. Keep it until the task is successfully completed or you have written a newer summary/continue artifact that clearly supersedes it. If the resumed attempt fails again, update or replace the continue file so no recovery context is lost. {{skillActivation}}
1
+ Resume interrupted work. Find the continue file (`{{sliceId}}-CONTINUE.md` or `continue.md`) in slice {{sliceId}} of milestone {{milestoneId}}, read it, and use it as the recovery contract for where to pick up. Before you create any expected artifact or output file, check whether it already exists and read it first — a prior session may already have started or completed that work. Do **not** delete the continue file immediately. Keep it until the task is successfully completed or you have written a newer summary/continue artifact that clearly supersedes it. If the resumed attempt fails again, update or replace the continue file so no recovery context is lost. {{skillActivation}}
@@ -18,6 +18,7 @@ Say exactly: "What do you want to add?" — nothing else. Wait for the user's an
18
18
  ## Discussion Phase
19
19
 
20
20
  After they describe it, your job is to understand the new work deeply enough to create context files that a future planning session can use.
21
+ Never fabricate or simulate user input during this discussion. Never generate fake transcript markers like `[User]`, `[Human]`, or `User:`. Ask one question round, then wait for the user's actual response before continuing.
21
22
 
22
23
  **If the user provides a file path or pastes a large document** (spec, design doc, product plan, chat export), read it fully before asking questions. Use it as the starting point — don't ask them to re-explain what's already in the document. Your questions should fill gaps and resolve ambiguities the document doesn't cover.
23
24
 
@@ -36,11 +37,11 @@ Don't go deep — just enough that your next question reflects what's actually t
36
37
  - How the new work relates to existing milestones — overlap, dependencies, prerequisites
37
38
  - If `.gsd/REQUIREMENTS.md` exists: which unmet Active or Deferred requirements this queued work advances
38
39
 
39
- **Then use ask_user_questions** to dig into gray areas — scope boundaries, proof expectations, integration choices, tech preferences when they materially matter, and what's in vs out. 1-3 questions per round.
40
+ **Then use ask_user_questions** to dig into gray areas — scope boundaries, proof expectations, integration choices, tech preferences when they materially matter, and what's in vs out. Ask 1-3 questions per round, then wait for the user's response before asking the next round.
40
41
 
41
42
  If a `GSD Skill Preferences` block is present in system context, use it to decide which skills to load and follow during discuss/planning work, but do not let it override the required discuss flow or artifact requirements.
42
43
 
43
- **Self-regulate:** Do **not** ask a meta "ready to queue?" question after every round. Keep going until you have enough depth to write the context well, then use a single wrap-up prompt if needed. If the user clearly keeps adding detail instead of objecting, treat that as permission to continue.
44
+ **Self-regulate:** Do **not** ask a meta "ready to queue?" question after every round. Keep going until you have enough depth to write the context well, then use a single wrap-up prompt if needed. Do not infer permission to continue from silence or from partial prior answers each new round requires an actual user response.
44
45
 
45
46
  ## Existing Milestone Awareness
46
47
 
@@ -35,6 +35,7 @@ GSD ships with bundled skills. Load the relevant skill file with the `read` tool
35
35
  - Read before edit.
36
36
  - Reproduce before fix when possible.
37
37
  - Work is not done until the relevant verification has passed.
38
+ - **Never fabricate, simulate, or role-play user responses.** Never generate markers like `[User]`, `[Human]`, `User:`, or similar to represent user input inside your own output. Ask one question round (1-3 questions), then stop and wait for the user's actual response before continuing. If `ask_user_questions` is available, treat its returned response as the only valid structured user input for that round.
38
39
  - Never print, echo, log, or restate secrets or credentials. Report only key names and applied/skipped status.
39
40
  - Never ask the user to edit `.env` files or set secrets manually. Use `secure_env_collect`.
40
41
  - In enduring files, write current state only unless the file is explicitly historical.
@@ -31,7 +31,7 @@ Prompt: "Review milestone {{milestoneId}} requirements coverage. Working directo
31
31
  Prompt: "Review milestone {{milestoneId}} cross-slice integration. Working directory: {{workingDirectory}}. Read `{{roadmapPath}}` and find the boundary map (produces/consumes contracts). For each boundary, check that the producing slice's SUMMARY confirms it produced the artifact, and the consuming slice's SUMMARY confirms it consumed it. Output a markdown table: Boundary | Producer Summary | Consumer Summary | Status. End with a one-line verdict: PASS if all boundaries honored, NEEDS-ATTENTION if any gaps."
32
32
 
33
33
  **Reviewer C — Assessment & Acceptance Criteria**
34
- Prompt: "Review milestone {{milestoneId}} assessment evidence and acceptance criteria. Working directory: {{workingDirectory}}. Read `.gsd/{{milestoneId}}/CONTEXT.md` for acceptance criteria. Check for ASSESSMENT files in each slice directory. Verify each acceptance criterion maps to either a passing assessment result or clear SUMMARY evidence. Output a checklist: [ ] Criterion | Evidence. End with a one-line verdict: PASS if all criteria met, NEEDS-ATTENTION if gaps exist."
34
+ Prompt: "Review milestone {{milestoneId}} assessment evidence and acceptance criteria. Working directory: {{workingDirectory}}. Read `.gsd/{{milestoneId}}/CONTEXT.md` for acceptance criteria. Check for ASSESSMENT files in each slice directory. Verify each acceptance criterion maps to either a passing assessment result or clear SUMMARY evidence. Then review the inlined milestone verification classes from planning. For each non-empty planned class, output a markdown table: Class | Planned Check | Evidence | Verdict. Use the exact class names `Contract`, `Integration`, `Operational`, and `UAT` whenever those classes are present. If no verification classes were planned, say that explicitly. Output two sections: `Acceptance Criteria` with a checklist `[ ] Criterion | Evidence`, and `Verification Classes` with the table. End with a one-line verdict: PASS if all criteria and verification classes are covered, NEEDS-ATTENTION if gaps exist."
35
35
 
36
36
  ### Step 2 — Synthesize Findings
37
37
 
@@ -70,6 +70,7 @@ reviewers: 3
70
70
  ```
71
71
 
72
72
  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`.
73
+ 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`.
73
74
 
74
75
  **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` to read milestone and slice state. All data you need is already inlined in the context above or accessible via the `gsd_*` tools. Direct DB access corrupts the WAL and bypasses tool-level validation.
75
76