gsd-pi 2.78.1-dev.e9d88a536 → 2.78.1-dev.eccf86e27

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 (212) hide show
  1. package/README.md +5 -7
  2. package/dist/help-text.js +1 -1
  3. package/dist/resource-loader.js +6 -1
  4. package/dist/resources/.managed-resources-content-hash +1 -1
  5. package/dist/resources/extensions/gsd/auto/detect-stuck.js +41 -5
  6. package/dist/resources/extensions/gsd/auto/loop.js +235 -36
  7. package/dist/resources/extensions/gsd/auto/phases.js +14 -7
  8. package/dist/resources/extensions/gsd/auto/session.js +36 -0
  9. package/dist/resources/extensions/gsd/auto-dispatch.js +49 -4
  10. package/dist/resources/extensions/gsd/auto-post-unit.js +26 -12
  11. package/dist/resources/extensions/gsd/auto-worktree.js +185 -201
  12. package/dist/resources/extensions/gsd/auto.js +139 -49
  13. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +1 -1
  14. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +26 -20
  15. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +67 -55
  16. package/dist/resources/extensions/gsd/crash-recovery.js +160 -47
  17. package/dist/resources/extensions/gsd/db/auto-workers.js +227 -0
  18. package/dist/resources/extensions/gsd/db/command-queue.js +105 -0
  19. package/dist/resources/extensions/gsd/db/milestone-leases.js +210 -0
  20. package/dist/resources/extensions/gsd/db/runtime-kv.js +91 -0
  21. package/dist/resources/extensions/gsd/db/unit-dispatches.js +322 -0
  22. package/dist/resources/extensions/gsd/db-writer.js +96 -16
  23. package/dist/resources/extensions/gsd/delegation-policy.js +155 -0
  24. package/dist/resources/extensions/gsd/docs/COORDINATION.md +42 -0
  25. package/dist/resources/extensions/gsd/doctor-proactive.js +4 -0
  26. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +22 -6
  27. package/dist/resources/extensions/gsd/doctor.js +12 -2
  28. package/dist/resources/extensions/gsd/gsd-db.js +355 -3
  29. package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
  30. package/dist/resources/extensions/gsd/guided-flow.js +116 -26
  31. package/dist/resources/extensions/gsd/interrupted-session.js +18 -15
  32. package/dist/resources/extensions/gsd/metrics.js +287 -1
  33. package/dist/resources/extensions/gsd/paths.js +79 -8
  34. package/dist/resources/extensions/gsd/prompts/complete-slice.md +4 -4
  35. package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -3
  36. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +8 -1
  37. package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +22 -7
  38. package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +6 -2
  39. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -1
  40. package/dist/resources/extensions/gsd/state.js +21 -6
  41. package/dist/resources/extensions/gsd/templates/project.md +10 -0
  42. package/dist/resources/extensions/gsd/workflow-mcp.js +2 -2
  43. package/dist/resources/extensions/gsd/workspace.js +59 -0
  44. package/dist/resources/extensions/gsd/worktree-resolver.js +79 -2
  45. package/dist/resources/extensions/gsd/write-intercept.js +3 -3
  46. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  47. package/dist/web/standalone/.next/BUILD_ID +1 -1
  48. package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
  49. package/dist/web/standalone/.next/build-manifest.json +2 -2
  50. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  51. package/dist/web/standalone/.next/required-server-files.json +1 -1
  52. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  61. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/index.html +1 -1
  69. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
  76. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  77. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  78. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  79. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  80. package/dist/web/standalone/server.js +1 -1
  81. package/package.json +1 -1
  82. package/packages/mcp-server/README.md +2 -11
  83. package/packages/mcp-server/dist/remote-questions.d.ts +27 -0
  84. package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -1
  85. package/packages/mcp-server/dist/remote-questions.js +28 -0
  86. package/packages/mcp-server/dist/remote-questions.js.map +1 -1
  87. package/packages/mcp-server/dist/server.d.ts +28 -0
  88. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  89. package/packages/mcp-server/dist/server.js +94 -4
  90. package/packages/mcp-server/dist/server.js.map +1 -1
  91. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  92. package/packages/mcp-server/src/mcp-server.test.ts +226 -0
  93. package/packages/mcp-server/src/remote-questions.test.ts +103 -0
  94. package/packages/mcp-server/src/remote-questions.ts +35 -0
  95. package/packages/mcp-server/src/server.ts +129 -6
  96. package/packages/mcp-server/src/workflow-tools.ts +1 -1
  97. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  98. package/src/resources/extensions/gsd/auto/detect-stuck.ts +37 -5
  99. package/src/resources/extensions/gsd/auto/loop.ts +263 -41
  100. package/src/resources/extensions/gsd/auto/phases.ts +15 -7
  101. package/src/resources/extensions/gsd/auto/session.ts +40 -0
  102. package/src/resources/extensions/gsd/auto-dispatch.ts +63 -4
  103. package/src/resources/extensions/gsd/auto-post-unit.ts +27 -12
  104. package/src/resources/extensions/gsd/auto-worktree.ts +218 -225
  105. package/src/resources/extensions/gsd/auto.ts +166 -43
  106. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +1 -1
  107. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +26 -21
  108. package/src/resources/extensions/gsd/bootstrap/tests/write-gate-basepath.test.ts +103 -0
  109. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +80 -55
  110. package/src/resources/extensions/gsd/crash-recovery.ts +177 -43
  111. package/src/resources/extensions/gsd/db/auto-workers.ts +273 -0
  112. package/src/resources/extensions/gsd/db/command-queue.ts +149 -0
  113. package/src/resources/extensions/gsd/db/milestone-leases.ts +274 -0
  114. package/src/resources/extensions/gsd/db/runtime-kv.ts +127 -0
  115. package/src/resources/extensions/gsd/db/unit-dispatches.ts +446 -0
  116. package/src/resources/extensions/gsd/db-writer.ts +113 -17
  117. package/src/resources/extensions/gsd/delegation-policy.ts +197 -0
  118. package/src/resources/extensions/gsd/docs/COORDINATION.md +42 -0
  119. package/src/resources/extensions/gsd/doctor-proactive.ts +4 -0
  120. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +24 -6
  121. package/src/resources/extensions/gsd/doctor.ts +10 -2
  122. package/src/resources/extensions/gsd/gsd-db.ts +354 -3
  123. package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
  124. package/src/resources/extensions/gsd/guided-flow.ts +152 -26
  125. package/src/resources/extensions/gsd/interrupted-session.ts +19 -12
  126. package/src/resources/extensions/gsd/metrics.ts +321 -1
  127. package/src/resources/extensions/gsd/paths.ts +67 -8
  128. package/src/resources/extensions/gsd/prompts/complete-slice.md +4 -4
  129. package/src/resources/extensions/gsd/prompts/execute-task.md +3 -3
  130. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +8 -1
  131. package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +22 -7
  132. package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +6 -2
  133. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -1
  134. package/src/resources/extensions/gsd/state.ts +44 -6
  135. package/src/resources/extensions/gsd/templates/project.md +10 -0
  136. package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +14 -14
  137. package/src/resources/extensions/gsd/tests/auto-loop-no-copy-artifacts.test.ts +72 -0
  138. package/src/resources/extensions/gsd/tests/auto-loop-symlink-worktree.test.ts +190 -0
  139. package/src/resources/extensions/gsd/tests/auto-session-scope.test.ts +331 -0
  140. package/src/resources/extensions/gsd/tests/auto-workers.test.ts +105 -0
  141. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +176 -0
  142. package/src/resources/extensions/gsd/tests/command-queue.test.ts +141 -0
  143. package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +203 -0
  144. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +169 -59
  145. package/src/resources/extensions/gsd/tests/db-writer-path-containment.test.ts +152 -0
  146. package/src/resources/extensions/gsd/tests/db-writer-root-artifact.test.ts +221 -0
  147. package/src/resources/extensions/gsd/tests/db-writer-scope.test.ts +230 -0
  148. package/src/resources/extensions/gsd/tests/delegation-policy.test.ts +151 -0
  149. package/src/resources/extensions/gsd/tests/detect-stuck-respects-retry.test.ts +173 -0
  150. package/src/resources/extensions/gsd/tests/dispatch-backgroundable-annotation.test.ts +55 -0
  151. package/src/resources/extensions/gsd/tests/draft-promotion.test.ts +3 -23
  152. package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +193 -0
  153. package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound-corrections.test.ts +246 -0
  154. package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound.test.ts +218 -0
  155. package/src/resources/extensions/gsd/tests/gsd-db-failed-open-restore.test.ts +117 -0
  156. package/src/resources/extensions/gsd/tests/gsd-db-workspace-scope.test.ts +226 -0
  157. package/src/resources/extensions/gsd/tests/gsd-root-canonical.test.ts +66 -0
  158. package/src/resources/extensions/gsd/tests/gsd-root-home-guard.test.ts +68 -5
  159. package/src/resources/extensions/gsd/tests/guided-flow-prompt-consolidation.test.ts +4 -4
  160. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +22 -12
  161. package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +24 -10
  162. package/src/resources/extensions/gsd/tests/integration/doctor-runtime.test.ts +35 -23
  163. package/src/resources/extensions/gsd/tests/integration/workspace-collapse-integration.test.ts +369 -0
  164. package/src/resources/extensions/gsd/tests/interrupted-session-auto.test.ts +72 -25
  165. package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +72 -25
  166. package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +9 -6
  167. package/src/resources/extensions/gsd/tests/metrics-atomic-merge.test.ts +222 -0
  168. package/src/resources/extensions/gsd/tests/metrics-lock-hardening.test.ts +400 -0
  169. package/src/resources/extensions/gsd/tests/metrics-lock-not-acquired.test.ts +141 -0
  170. package/src/resources/extensions/gsd/tests/metrics-lock-retry-sleep.test.ts +287 -0
  171. package/src/resources/extensions/gsd/tests/metrics-prune-cache-invalidation.test.ts +149 -0
  172. package/src/resources/extensions/gsd/tests/metrics-scope.test.ts +378 -0
  173. package/src/resources/extensions/gsd/tests/milestone-leases.test.ts +152 -0
  174. package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +329 -0
  175. package/src/resources/extensions/gsd/tests/parallel-milestone-isolation.test.ts +106 -0
  176. package/src/resources/extensions/gsd/tests/path-cache-decoupled.test.ts +209 -0
  177. package/src/resources/extensions/gsd/tests/path-normalization-unified.test.ts +175 -0
  178. package/src/resources/extensions/gsd/tests/paths-cache.test.ts +170 -0
  179. package/src/resources/extensions/gsd/tests/paused-session-via-db.test.ts +119 -0
  180. package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +120 -0
  181. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +58 -0
  182. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +3 -17
  183. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +150 -7
  184. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +138 -16
  185. package/src/resources/extensions/gsd/tests/resume-missing-worktree-warning.test.ts +209 -0
  186. package/src/resources/extensions/gsd/tests/runtime-kv.test.ts +120 -0
  187. package/src/resources/extensions/gsd/tests/skipped-validation-completion.test.ts +133 -28
  188. package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +17 -0
  189. package/src/resources/extensions/gsd/tests/stuck-state-via-db.test.ts +134 -0
  190. package/src/resources/extensions/gsd/tests/sync-layer-scope.test.ts +434 -0
  191. package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +162 -0
  192. package/src/resources/extensions/gsd/tests/teardown-cleanup-parity.test.ts +98 -0
  193. package/src/resources/extensions/gsd/tests/teardown-failure-clears-registry.test.ts +186 -0
  194. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +1 -1
  195. package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +247 -0
  196. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +41 -1
  197. package/src/resources/extensions/gsd/tests/validator-scope-parity.test.ts +239 -0
  198. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -2
  199. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +9 -15
  200. package/src/resources/extensions/gsd/tests/workspace.test.ts +196 -0
  201. package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +35 -35
  202. package/src/resources/extensions/gsd/tests/write-gate.test.ts +94 -71
  203. package/src/resources/extensions/gsd/tests/write-intercept.test.ts +1 -1
  204. package/src/resources/extensions/gsd/workflow-mcp.ts +2 -2
  205. package/src/resources/extensions/gsd/workspace.ts +95 -0
  206. package/src/resources/extensions/gsd/worktree-resolver.ts +78 -2
  207. package/src/resources/extensions/gsd/write-intercept.ts +3 -3
  208. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +0 -213
  209. package/src/resources/extensions/gsd/tests/auto-stale-lock-self-kill.test.ts +0 -87
  210. package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +0 -159
  211. /package/dist/web/standalone/.next/static/{oZGTPvJBQX_IDKKnuV8Bt → Y5UeGFkXTYM9WIQOWHkot}/_buildManifest.js +0 -0
  212. /package/dist/web/standalone/.next/static/{oZGTPvJBQX_IDKKnuV8Bt → Y5UeGFkXTYM9WIQOWHkot}/_ssgManifest.js +0 -0
@@ -14,7 +14,7 @@ import type { GSDPreferences } from "./preferences.js";
14
14
  import type { UatType } from "./files.js";
15
15
  import type { MinimalModelRegistry } from "./context-budget.js";
16
16
  import { loadFile, extractUatType, loadActiveOverrides } from "./files.js";
17
- import { isDbAvailable, getMilestoneSlices, getPendingGates, markAllGatesOmitted, getMilestone } from "./gsd-db.js";
17
+ import { isDbAvailable, getMilestoneSlices, getPendingGates, markAllGatesOmitted, getMilestone, insertAssessment, transaction } from "./gsd-db.js";
18
18
  import { isClosedStatus } from "./status-guards.js";
19
19
  import { extractVerdict, isAcceptableUatVerdict } from "./verdict-parser.js";
20
20
 
@@ -77,6 +77,9 @@ import {
77
77
  resolveDeepProjectSetupState,
78
78
  type DeepProjectSetupStage,
79
79
  } from "./deep-project-setup-policy.js";
80
+ import { annotateBackgroundable } from "./delegation-policy.js";
81
+ import { invalidateAllCaches } from "./cache.js";
82
+ import { insertMilestoneValidationGates } from "./milestone-validation-gates.js";
80
83
 
81
84
  // ─── Types ────────────────────────────────────────────────────────────────
82
85
 
@@ -89,6 +92,12 @@ export type DispatchAction =
89
92
  pauseAfterDispatch?: boolean;
90
93
  /** Name of the matched dispatch rule from the unified registry (journal provenance). */
91
94
  matchedRule?: string;
95
+ /**
96
+ * True when the matched unit type has a `good` verdict in delegation-policy.ts.
97
+ * Annotated in `resolveDispatch`. Consumers may use this to fork the prompt
98
+ * to a background sub-agent; default behavior is unchanged (synchronous).
99
+ */
100
+ backgroundable?: boolean;
92
101
  }
93
102
  | { action: "stop"; reason: string; level: "info" | "warning" | "error"; matchedRule?: string }
94
103
  | { action: "skip"; matchedRule?: string };
@@ -1196,9 +1205,12 @@ export const DISPATCH_RULES: DispatchRule[] = [
1196
1205
  const skipSource = trivialVariant
1197
1206
  ? "trivial-scope pipeline variant (#4781)"
1198
1207
  : "`skip_milestone_validation` preference";
1208
+ const skipValidationReason = trivialVariant ? "trivial-scope" : "preference";
1199
1209
  const content = [
1200
1210
  "---",
1201
1211
  "verdict: pass",
1212
+ "skip_validation: true",
1213
+ `skip_validation_reason: ${skipValidationReason}`,
1202
1214
  "remediation_round: 0",
1203
1215
  "---",
1204
1216
  "",
@@ -1207,6 +1219,45 @@ export const DISPATCH_RULES: DispatchRule[] = [
1207
1219
  `Milestone validation was skipped via ${skipSource}.`,
1208
1220
  ].join("\n");
1209
1221
  writeFileSync(validationPath, content, "utf-8");
1222
+ try {
1223
+ // DB-backed state derivation keys off assessments, not only the file
1224
+ // projection. Persist the skipped validation there too so the next
1225
+ // loop iteration advances to completing-milestone instead of
1226
+ // re-entering validating-milestone.
1227
+ if (isDbAvailable()) {
1228
+ transaction(() => {
1229
+ insertAssessment({
1230
+ path: validationPath,
1231
+ milestoneId: mid,
1232
+ sliceId: null,
1233
+ taskId: null,
1234
+ status: "pass",
1235
+ scope: "milestone-validation",
1236
+ fullContent: content,
1237
+ });
1238
+ const gateSliceId = getMilestoneSlices(mid)[0]?.id;
1239
+ if (gateSliceId) {
1240
+ insertMilestoneValidationGates(
1241
+ mid,
1242
+ gateSliceId,
1243
+ "pass",
1244
+ new Date().toISOString(),
1245
+ );
1246
+ }
1247
+ });
1248
+ }
1249
+ } catch (err) {
1250
+ try {
1251
+ unlinkSync(validationPath);
1252
+ } catch (unlinkErr) {
1253
+ logWarning(
1254
+ "dispatch",
1255
+ `failed to remove skipped validation file after DB write failure for ${mid}: ${unlinkErr instanceof Error ? unlinkErr.message : String(unlinkErr)}`,
1256
+ );
1257
+ }
1258
+ throw err;
1259
+ }
1260
+ invalidateAllCaches();
1210
1261
  }
1211
1262
  return { action: "skip" };
1212
1263
  }
@@ -1291,7 +1342,9 @@ export const DISPATCH_RULES: DispatchRule[] = [
1291
1342
  if (validationContent) {
1292
1343
  // Allow completion when validation was intentionally skipped by
1293
1344
  // preference/budget profile (#3399, #3344).
1345
+ const skippedByMarker = /^skip_validation:\s*true$/im.test(validationContent);
1294
1346
  const skippedByPreference = /skip(?:ped)?[\s\-]+(?:by|per|due to)\s+(?:preference|budget|profile)/i.test(validationContent);
1347
+ const skippedByTrivialVariant = /trivial-scope pipeline variant/i.test(validationContent);
1295
1348
 
1296
1349
  // Accept either the structured template format (table with MET/N/A/SATISFIED)
1297
1350
  // or prose evidence patterns the validation agent may emit.
@@ -1300,7 +1353,12 @@ export const DISPATCH_RULES: DispatchRule[] = [
1300
1353
  (validationContent.includes("MET") || validationContent.includes("N/A") || validationContent.includes("SATISFIED"));
1301
1354
  const proseMatch =
1302
1355
  /[Oo]perational[\s\S]{0,500}?(?:✅|pass|verified|confirmed|met|complete|true|yes|addressed|covered|satisfied|partially|n\/a|not[\s-]+applicable)/i.test(validationContent);
1303
- const hasOperationalCheck = skippedByPreference || structuredMatch || proseMatch;
1356
+ const hasOperationalCheck =
1357
+ skippedByMarker ||
1358
+ skippedByPreference ||
1359
+ skippedByTrivialVariant ||
1360
+ structuredMatch ||
1361
+ proseMatch;
1304
1362
  if (!hasOperationalCheck) {
1305
1363
  return {
1306
1364
  action: "stop" as const,
@@ -1366,7 +1424,7 @@ export async function resolveDispatch(
1366
1424
  // Delegate to registry when available
1367
1425
  try {
1368
1426
  const registry = getRegistry();
1369
- return await registry.evaluateDispatch(ctx);
1427
+ return annotateBackgroundable(await registry.evaluateDispatch(ctx));
1370
1428
  } catch (err) {
1371
1429
  // Registry not initialized — fall back to inline loop
1372
1430
  logWarning("dispatch", `registry dispatch failed, falling back to inline rules: ${err instanceof Error ? err.message : String(err)}`);
@@ -1376,7 +1434,7 @@ export async function resolveDispatch(
1376
1434
  const result = await rule.match(ctx);
1377
1435
  if (result) {
1378
1436
  if (result.action !== "skip") result.matchedRule = rule.name;
1379
- return result;
1437
+ return annotateBackgroundable(result);
1380
1438
  }
1381
1439
  }
1382
1440
 
@@ -1392,6 +1450,7 @@ export async function resolveDispatch(
1392
1450
  };
1393
1451
  }
1394
1452
 
1453
+
1395
1454
  /** Exposed for testing — returns the rule names in evaluation order. */
1396
1455
  export function getDispatchRuleNames(): string[] {
1397
1456
  return DISPATCH_RULES.map((r) => r.name);
@@ -42,6 +42,7 @@ import {
42
42
  } from "./auto-recovery.js";
43
43
  import { regenerateIfMissing } from "./workflow-projections.js";
44
44
  import { syncStateToProjectRoot } from "./auto-worktree.js";
45
+ import { normalizeWorktreePathForCompare } from "./worktree-root.js";
45
46
  import { isDbAvailable, getTask, getSlice, getMilestone, updateTaskStatus, _getAdapter } from "./gsd-db.js";
46
47
  import { renderPlanCheckboxes } from "./markdown-renderer.js";
47
48
  import { consumeSignal } from "./session-status-io.js";
@@ -80,6 +81,12 @@ import {
80
81
  } from "./project-research-policy.js";
81
82
  import { validateArtifact } from "./schemas/validate.js";
82
83
 
84
+ // ─── Path Comparison Helper ───────────────────────────────────────────────
85
+ /** Compare two paths for physical identity, tolerating trailing slashes and symlinks. */
86
+ function isSamePathLocal(a: string, b: string): boolean {
87
+ return normalizeWorktreePathForCompare(a) === normalizeWorktreePathForCompare(b);
88
+ }
89
+
83
90
  /** Maximum verification retry attempts before escalating to blocker placeholder (#2653). */
84
91
  const MAX_VERIFICATION_RETRIES = 3;
85
92
  /** Keep failure toasts short while still showing concrete examples. */
@@ -617,7 +624,7 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
617
624
  });
618
625
 
619
626
  // Sync worktree state back to project root (skipped for lightweight sidecars)
620
- if (!opts?.skipWorktreeSync && s.originalBasePath && s.originalBasePath !== s.basePath) {
627
+ if (!opts?.skipWorktreeSync && s.originalBasePath && !isSamePathLocal(s.originalBasePath, s.basePath)) {
621
628
  await runSafely("postUnit", "worktree-sync", () => {
622
629
  syncStateToProjectRoot(s.basePath, s.originalBasePath!, s.currentMilestoneId);
623
630
  });
@@ -761,14 +768,16 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
761
768
  if (s.currentUnit.type === "triage-captures") {
762
769
  try {
763
770
  const { executeTriageResolutions } = await import("./triage-resolution.js");
764
- const state = await deriveState(s.basePath);
771
+ const state = await deriveState(s.canonicalProjectRoot);
765
772
  const mid = state.activeMilestone?.id ?? "";
766
773
  const sid = state.activeSlice?.id ?? "";
767
774
 
768
775
  // executeTriageResolutions handles defer milestone creation even
769
776
  // without an active milestone/slice (the "all milestones complete"
770
777
  // scenario from #1562). inject/replan/quick-task still require mid+sid.
771
- const triageResult = executeTriageResolutions(s.basePath, mid, sid);
778
+ // Phase C: write to canonical project root. copyPlanningArtifacts
779
+ // has been deleted, so triage writes land where readers consult.
780
+ const triageResult = executeTriageResolutions(s.canonicalProjectRoot, mid, sid);
772
781
 
773
782
  if (triageResult.injected > 0) {
774
783
  ctx.ui.notify(
@@ -933,10 +942,14 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
933
942
  try {
934
943
  const { milestone: mid, slice: sid } = parseUnitId(s.currentUnit.id);
935
944
  if (mid && sid) {
936
- const regenerated = await regenerateIfMissing(s.basePath, mid, sid, "PLAN");
945
+ // Phase C: write to the canonical project root (#5236 scope)
946
+ // so non-symlinked worktrees no longer maintain a separate
947
+ // local .gsd/ projection. copyPlanningArtifacts has been
948
+ // deleted; reads + writes converge at projectRoot.
949
+ const regenerated = await regenerateIfMissing(s.canonicalProjectRoot, mid, sid, "PLAN");
937
950
  if (regenerated) {
938
951
  // Re-check after regeneration
939
- triggerArtifactVerified = verifyExpectedArtifact(s.currentUnit.type, s.currentUnit.id, s.basePath);
952
+ triggerArtifactVerified = verifyExpectedArtifact(s.currentUnit.type, s.currentUnit.id, s.canonicalProjectRoot);
940
953
  if (triggerArtifactVerified) {
941
954
  invalidateAllCaches();
942
955
  }
@@ -1171,7 +1184,7 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
1171
1184
  if (mid && sid && tid) {
1172
1185
  try {
1173
1186
  updateTaskStatus(mid, sid, tid, "pending");
1174
- await renderPlanCheckboxes(s.basePath, mid, sid);
1187
+ await renderPlanCheckboxes(s.canonicalProjectRoot, mid, sid);
1175
1188
  } catch (dbErr) {
1176
1189
  // DB unavailable — fail explicitly rather than silently reverting to markdown mutation.
1177
1190
  // Use 'gsd recover' to rebuild DB state from disk if needed.
@@ -1181,7 +1194,8 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
1181
1194
 
1182
1195
  // 2. Delete SUMMARY.md for the task
1183
1196
  if (mid && sid && tid) {
1184
- const tasksDir = resolveTasksDir(s.basePath, mid, sid);
1197
+ // Phase C: read+delete via canonical project root.
1198
+ const tasksDir = resolveTasksDir(s.canonicalProjectRoot, mid, sid);
1185
1199
  if (tasksDir) {
1186
1200
  const summaryFile = join(tasksDir, buildTaskFileName(tid, "SUMMARY"));
1187
1201
  if (existsSync(summaryFile)) {
@@ -1192,7 +1206,7 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
1192
1206
 
1193
1207
  // 3. Delete the retry_on artifact (e.g. NEEDS-REWORK.md)
1194
1208
  if (trigger.retryArtifact) {
1195
- const retryArtifactPath = resolveHookArtifactPath(s.basePath, trigger.unitId, trigger.retryArtifact);
1209
+ const retryArtifactPath = resolveHookArtifactPath(s.canonicalProjectRoot, trigger.unitId, trigger.retryArtifact);
1196
1210
  if (existsSync(retryArtifactPath)) {
1197
1211
  unlinkSync(retryArtifactPath);
1198
1212
  }
@@ -1470,16 +1484,17 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
1470
1484
  if (hasPendingCaptures(s.basePath)) {
1471
1485
  const pending = loadPendingCaptures(s.basePath);
1472
1486
  if (pending.length > 0) {
1473
- const state = await deriveState(s.basePath);
1487
+ const readRoot = s.canonicalProjectRoot;
1488
+ const state = await deriveState(readRoot);
1474
1489
  const mid = state.activeMilestone?.id;
1475
1490
  const sid = state.activeSlice?.id;
1476
1491
 
1477
1492
  if (mid && sid) {
1478
1493
  let currentPlan = "";
1479
1494
  let roadmapContext = "";
1480
- const planFile = resolveSliceFile(s.basePath, mid, sid, "PLAN");
1495
+ const planFile = resolveSliceFile(readRoot, mid, sid, "PLAN");
1481
1496
  if (planFile) currentPlan = (await loadFile(planFile)) ?? "";
1482
- const roadmapFile = resolveMilestoneFile(s.basePath, mid, "ROADMAP");
1497
+ const roadmapFile = resolveMilestoneFile(readRoot, mid, "ROADMAP");
1483
1498
  if (roadmapFile) roadmapContext = (await loadFile(roadmapFile)) ?? "";
1484
1499
 
1485
1500
  const capturesList = pending.map(c =>
@@ -1547,7 +1562,7 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
1547
1562
  // exits the loop, leaving the user with no hint to /clear and /gsd again.
1548
1563
  if (s.stepMode) {
1549
1564
  try {
1550
- const nextState = await deriveState(s.basePath);
1565
+ const nextState = await deriveState(s.canonicalProjectRoot);
1551
1566
  ctx.ui.notify(buildStepCompleteMessage(nextState), "info");
1552
1567
  } catch (e) {
1553
1568
  debugLog("postUnit", { phase: "step-wizard-notify", error: String(e) });