gsd-pi 2.82.0-dev.725028083 → 2.82.0-dev.9d5798940

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 (210) hide show
  1. package/README.md +3 -2
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/GSD-WORKFLOW.md +3 -1
  4. package/dist/resources/extensions/claude-code-cli/partial-builder.js +2 -1
  5. package/dist/resources/extensions/cmux/index.js +5 -0
  6. package/dist/resources/extensions/gsd/auto/orchestrator.js +113 -6
  7. package/dist/resources/extensions/gsd/auto-dispatch.js +13 -6
  8. package/dist/resources/extensions/gsd/auto-post-unit.js +232 -126
  9. package/dist/resources/extensions/gsd/auto-prompts.js +2 -2
  10. package/dist/resources/extensions/gsd/auto-recovery.js +31 -1
  11. package/dist/resources/extensions/gsd/auto-start.js +7 -3
  12. package/dist/resources/extensions/gsd/auto-verification.js +28 -22
  13. package/dist/resources/extensions/gsd/auto-worktree.js +96 -0
  14. package/dist/resources/extensions/gsd/auto.js +128 -52
  15. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +4 -1
  16. package/dist/resources/extensions/gsd/bootstrap/subagent-input.js +16 -7
  17. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +16 -2
  18. package/dist/resources/extensions/gsd/clean-root-preflight.js +170 -8
  19. package/dist/resources/extensions/gsd/commands/catalog.js +4 -1
  20. package/dist/resources/extensions/gsd/commands/handlers/core.js +37 -0
  21. package/dist/resources/extensions/gsd/commands-bootstrap.js +5 -0
  22. package/dist/resources/extensions/gsd/db/unit-dispatches.js +2 -2
  23. package/dist/resources/extensions/gsd/export-html.js +27 -425
  24. package/dist/resources/extensions/gsd/md-importer.js +1 -1
  25. package/dist/resources/extensions/gsd/migrate/command.js +5 -0
  26. package/dist/resources/extensions/gsd/migrate/preview.js +9 -0
  27. package/dist/resources/extensions/gsd/migrate/transformer.js +51 -4
  28. package/dist/resources/extensions/gsd/migrate/writer.js +11 -1
  29. package/dist/resources/extensions/gsd/milestone-actions.js +11 -4
  30. package/dist/resources/extensions/gsd/native-git-bridge.js +8 -3
  31. package/dist/resources/extensions/gsd/state-reconciliation/drift/merge-state.js +6 -1
  32. package/dist/resources/extensions/gsd/tools/plan-slice.js +2 -1
  33. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +119 -0
  34. package/dist/resources/extensions/gsd/unit-context-manifest.js +32 -10
  35. package/dist/resources/extensions/gsd/verification-verdict.js +26 -0
  36. package/dist/resources/extensions/gsd/worktree-lifecycle.js +49 -9
  37. package/dist/resources/extensions/shared/html-shell.js +388 -0
  38. package/dist/resources/extensions/subagent/index.js +448 -78
  39. package/dist/resources/extensions/subagent/launch.js +77 -0
  40. package/dist/resources/extensions/subagent/run-store.js +148 -0
  41. package/dist/resources/extensions/visual-brief/artifact-policy.js +29 -0
  42. package/dist/resources/extensions/visual-brief/extension-manifest.json +8 -0
  43. package/dist/resources/extensions/visual-brief/index.js +5 -0
  44. package/dist/resources/extensions/visual-brief/page-contract.js +124 -0
  45. package/dist/resources/extensions/visual-brief/prompts.js +140 -0
  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 +15 -15
  49. package/dist/web/standalone/.next/build-manifest.json +3 -3
  50. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  51. package/dist/web/standalone/.next/react-loadable-manifest.json +3 -3
  52. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  54. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  62. package/dist/web/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  63. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  64. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -7
  66. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -7
  67. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -5
  69. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -5
  72. package/dist/web/standalone/.next/server/app/index.html +1 -1
  73. package/dist/web/standalone/.next/server/app/index.rsc +4 -7
  74. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -7
  76. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -5
  78. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -5
  79. package/dist/web/standalone/.next/server/app/page.js +2 -2
  80. package/dist/web/standalone/.next/server/app/page.js.nft.json +1 -1
  81. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  82. package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
  83. package/dist/web/standalone/.next/server/chunks/4266.js +2 -0
  84. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  86. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  88. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  89. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  90. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  91. package/dist/web/standalone/.next/static/chunks/2973.33f26573894b6153.js +2 -0
  92. package/dist/web/standalone/.next/static/chunks/{8359.e059d86b255fce1c.js → 8359.7eb3bb8f8ecf4c01.js} +2 -2
  93. package/dist/web/standalone/.next/static/chunks/app/layout-8c10ec293ae0f1d5.js +1 -0
  94. package/dist/web/standalone/.next/static/chunks/{webpack-de742b64187e13fe.js → webpack-9a4db269f9ed63ad.js} +1 -1
  95. package/dist/web/standalone/.next/static/css/746ee28c929d1880.css +1 -0
  96. package/package.json +3 -3
  97. package/packages/contracts/dist/rpc.test.js +7 -0
  98. package/packages/contracts/dist/rpc.test.js.map +1 -1
  99. package/packages/contracts/dist/workflow.d.ts +21 -0
  100. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  101. package/packages/contracts/dist/workflow.js +24 -0
  102. package/packages/contracts/dist/workflow.js.map +1 -1
  103. package/packages/contracts/src/rpc.test.ts +8 -0
  104. package/packages/contracts/src/workflow.ts +24 -0
  105. package/packages/mcp-server/README.md +13 -4
  106. package/packages/mcp-server/dist/workflow-tools.d.ts +0 -3
  107. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  108. package/packages/mcp-server/dist/workflow-tools.js +80 -0
  109. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  110. package/packages/mcp-server/src/workflow-tools.test.ts +22 -0
  111. package/packages/mcp-server/src/workflow-tools.ts +168 -0
  112. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  113. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  114. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  115. package/packages/pi-tui/dist/tui.js +5 -0
  116. package/packages/pi-tui/dist/tui.js.map +1 -1
  117. package/packages/pi-tui/src/tui.ts +6 -0
  118. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  119. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  120. package/src/resources/GSD-WORKFLOW.md +3 -1
  121. package/src/resources/extensions/claude-code-cli/partial-builder.ts +2 -1
  122. package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +19 -2
  123. package/src/resources/extensions/cmux/index.ts +6 -0
  124. package/src/resources/extensions/gsd/auto/contracts.ts +46 -11
  125. package/src/resources/extensions/gsd/auto/orchestrator.ts +118 -6
  126. package/src/resources/extensions/gsd/auto-dispatch.ts +14 -6
  127. package/src/resources/extensions/gsd/auto-post-unit.ts +265 -138
  128. package/src/resources/extensions/gsd/auto-prompts.ts +2 -2
  129. package/src/resources/extensions/gsd/auto-recovery.ts +29 -0
  130. package/src/resources/extensions/gsd/auto-start.ts +7 -3
  131. package/src/resources/extensions/gsd/auto-verification.ts +36 -34
  132. package/src/resources/extensions/gsd/auto-worktree.ts +104 -0
  133. package/src/resources/extensions/gsd/auto.ts +136 -51
  134. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +6 -1
  135. package/src/resources/extensions/gsd/bootstrap/subagent-input.ts +16 -6
  136. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +19 -3
  137. package/src/resources/extensions/gsd/clean-root-preflight.ts +174 -8
  138. package/src/resources/extensions/gsd/commands/catalog.ts +4 -1
  139. package/src/resources/extensions/gsd/commands/handlers/core.ts +40 -0
  140. package/src/resources/extensions/gsd/commands-bootstrap.ts +10 -0
  141. package/src/resources/extensions/gsd/db/unit-dispatches.ts +3 -3
  142. package/src/resources/extensions/gsd/export-html.ts +27 -427
  143. package/src/resources/extensions/gsd/md-importer.ts +1 -1
  144. package/src/resources/extensions/gsd/migrate/command.ts +5 -0
  145. package/src/resources/extensions/gsd/migrate/preview.ts +10 -0
  146. package/src/resources/extensions/gsd/migrate/transformer.ts +58 -4
  147. package/src/resources/extensions/gsd/migrate/writer.ts +14 -1
  148. package/src/resources/extensions/gsd/milestone-actions.ts +10 -4
  149. package/src/resources/extensions/gsd/native-git-bridge.ts +8 -3
  150. package/src/resources/extensions/gsd/state-reconciliation/drift/merge-state.ts +8 -1
  151. package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +116 -0
  152. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +408 -4
  153. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +6 -5
  154. package/src/resources/extensions/gsd/tests/auto-post-unit-step-message.test.ts +12 -1
  155. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +15 -1
  156. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +4 -4
  157. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +69 -1
  158. package/src/resources/extensions/gsd/tests/brief-command.test.ts +89 -0
  159. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +107 -2
  160. package/src/resources/extensions/gsd/tests/closeout-git-deferral.test.ts +16 -0
  161. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +57 -2
  162. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +39 -0
  163. package/src/resources/extensions/gsd/tests/evidence-cross-ref.test.ts +38 -0
  164. package/src/resources/extensions/gsd/tests/export-html-enhancements.test.ts +8 -0
  165. package/src/resources/extensions/gsd/tests/integration/migrate-command.test.ts +48 -3
  166. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +5 -1
  167. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +6 -1
  168. package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +55 -1
  169. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +25 -0
  170. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +79 -1
  171. package/src/resources/extensions/gsd/tests/post-unit-git-failure.test.ts +1 -1
  172. package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +46 -2
  173. package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +10 -0
  174. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +6 -0
  175. package/src/resources/extensions/gsd/tests/stuck-state-via-db.test.ts +39 -0
  176. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +86 -7
  177. package/src/resources/extensions/gsd/tests/verification-verdict.test.ts +78 -0
  178. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +64 -12
  179. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +25 -0
  180. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +47 -0
  181. package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -0
  182. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +135 -0
  183. package/src/resources/extensions/gsd/unit-context-manifest.ts +47 -11
  184. package/src/resources/extensions/gsd/verification-verdict.ts +47 -0
  185. package/src/resources/extensions/gsd/worktree-lifecycle.ts +54 -9
  186. package/src/resources/extensions/shared/html-shell.ts +412 -0
  187. package/src/resources/extensions/subagent/index.ts +567 -103
  188. package/src/resources/extensions/subagent/launch.ts +131 -0
  189. package/src/resources/extensions/subagent/run-store.ts +218 -0
  190. package/src/resources/extensions/subagent/tests/launch.test.ts +115 -0
  191. package/src/resources/extensions/subagent/tests/run-store.test.ts +111 -0
  192. package/src/resources/extensions/visual-brief/artifact-policy.ts +41 -0
  193. package/src/resources/extensions/visual-brief/extension-manifest.json +8 -0
  194. package/src/resources/extensions/visual-brief/index.ts +8 -0
  195. package/src/resources/extensions/visual-brief/page-contract.ts +136 -0
  196. package/src/resources/extensions/visual-brief/prompts.ts +183 -0
  197. package/src/resources/extensions/visual-brief/tests/visual-brief.test.ts +212 -0
  198. package/dist/web/standalone/.next/server/chunks/5822.js +0 -2
  199. package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +0 -1
  200. package/dist/web/standalone/.next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js +0 -1
  201. package/dist/web/standalone/.next/static/css/54ec2745c1da488b.css +0 -1
  202. package/dist/web/standalone/.next/static/css/de70bee13400563f.css +0 -1
  203. package/dist/web/standalone/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
  204. package/dist/web/standalone/.next/static/media/747892c23ea88013-s.woff2 +0 -0
  205. package/dist/web/standalone/.next/static/media/8d697b304b401681-s.woff2 +0 -0
  206. package/dist/web/standalone/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
  207. package/dist/web/standalone/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
  208. package/dist/web/standalone/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
  209. /package/dist/web/standalone/.next/static/{KDRTXR-22LPCsa80X9dey → BdZQhe8yKl6bdKLiXVEzh}/_buildManifest.js +0 -0
  210. /package/dist/web/standalone/.next/static/{KDRTXR-22LPCsa80X9dey → BdZQhe8yKl6bdKLiXVEzh}/_ssgManifest.js +0 -0
@@ -24,6 +24,7 @@ import { deleteMilestone, getMilestone, isDbAvailable, updateMilestoneStatus } f
24
24
  import { removeWorktree } from "./worktree-manager.js";
25
25
  import { logWarning } from "./workflow-logger.js";
26
26
  import { isAutoActive } from "./auto.js";
27
+ import { isClosedStatus } from "./status-guards.js";
27
28
 
28
29
  /**
29
30
  * Writer-side assert for mutations that race with auto-mode's squash merge (#4704).
@@ -44,7 +45,7 @@ function assertNotAutoActive(action: string): void {
44
45
  /**
45
46
  * Park a milestone — creates a PARKED.md marker file with reason and timestamp.
46
47
  * Parked milestones are skipped during active-milestone discovery but stay on disk.
47
- * Returns true if successfully parked, false if milestone not found or already parked.
48
+ * Returns true if successfully parked, false if milestone not found, already parked, or complete.
48
49
  */
49
50
  export function parkMilestone(basePath: string, milestoneId: string, reason: string): boolean {
50
51
  assertNotAutoActive("park milestone");
@@ -52,8 +53,13 @@ export function parkMilestone(basePath: string, milestoneId: string, reason: str
52
53
  if (!mDir || !existsSync(mDir)) return false;
53
54
 
54
55
  // Guard: do not park a completed milestone — it would corrupt depends_on satisfaction
55
- const summaryFile = resolveMilestoneFile(basePath, milestoneId, "SUMMARY");
56
- if (summaryFile) return false;
56
+ const dbAvailable = isDbAvailable();
57
+ const milestone = dbAvailable ? getMilestone(milestoneId) : null;
58
+ if (milestone && isClosedStatus(milestone.status)) return false;
59
+ if (!dbAvailable) {
60
+ const summaryFile = resolveMilestoneFile(basePath, milestoneId, "SUMMARY");
61
+ if (summaryFile) return false;
62
+ }
57
63
 
58
64
  const parkedPath = join(mDir, buildMilestoneFileName(milestoneId, "PARKED"));
59
65
  if (existsSync(parkedPath)) return false; // already parked
@@ -72,7 +78,7 @@ export function parkMilestone(basePath: string, milestoneId: string, reason: str
72
78
 
73
79
  writeFileSync(parkedPath, content, "utf-8");
74
80
  // Sync DB status so deriveStateFromDb also skips this milestone (#2694)
75
- if (isDbAvailable()) {
81
+ if (dbAvailable) {
76
82
  try {
77
83
  updateMilestoneStatus(milestoneId, "parked");
78
84
  } catch (err) {
@@ -428,16 +428,21 @@ export function nativeDiffNameStatus(
428
428
 
429
429
  /**
430
430
  * Get numstat diff between two refs.
431
+ * useMergeBase: if true, uses three-dot semantics.
431
432
  * Native: libgit2 patch line stats.
432
433
  * Fallback: `git diff --numstat`.
433
434
  */
434
- export function nativeDiffNumstat(basePath: string, fromRef: string, toRef: string): GitNumstat[] {
435
+ export function nativeDiffNumstat(basePath: string, fromRef: string, toRef: string, useMergeBase?: boolean): GitNumstat[] {
435
436
  const native = loadNative();
436
- if (native) {
437
+ if (native && !useMergeBase) {
437
438
  return native.gitDiffNumstat(basePath, fromRef, toRef);
438
439
  }
439
440
 
440
- const result = gitExec(basePath, ["diff", "--numstat", fromRef, toRef], true);
441
+ const refspec = useMergeBase ? `${fromRef}...${toRef}` : undefined;
442
+ const args = refspec
443
+ ? ["diff", "--numstat", refspec]
444
+ : ["diff", "--numstat", fromRef, toRef];
445
+ const result = gitExec(basePath, args, true);
441
446
  if (!result) return [];
442
447
 
443
448
  return result.split("\n").filter(Boolean).map(line => {
@@ -23,6 +23,7 @@ import {
23
23
  } from "../../native-git-bridge.js";
24
24
  import type { GSDState } from "../../types.js";
25
25
  import { logError, logWarning } from "../../workflow-logger.js";
26
+ import { isGsdWorktreePath } from "../../worktree-root.js";
26
27
  import type { DriftContext, DriftHandler, DriftRecord } from "../types.js";
27
28
 
28
29
  export type MergeReconcileResult = "clean" | "reconciled" | "blocked";
@@ -46,7 +47,13 @@ function resolveGitDir(basePath: string): string {
46
47
  return isAbsolute(gitDir) ? gitDir : resolve(basePath, gitDir);
47
48
  }
48
49
  } catch (err) {
49
- logWarning("recovery", `gitdir resolution failed: ${getErrorMessage(err)}`);
50
+ const message = getErrorMessage(err);
51
+ logWarning("recovery", `gitdir resolution failed: ${message}`);
52
+ if (isGsdWorktreePath(basePath)) {
53
+ throw new Error(
54
+ `Worktree integrity failure: ${basePath} is not a valid git worktree (git rev-parse failed: ${message.split("\n")[0]}). Repair or recreate the worktree before retrying.`,
55
+ );
56
+ }
50
57
  }
51
58
 
52
59
  return join(basePath, ".git");
@@ -42,6 +42,20 @@ function makeTmpBase(): string {
42
42
  return base;
43
43
  }
44
44
 
45
+ function makeBrokenIsolatedWorktree(): string {
46
+ const root = mkdtempSync(join(tmpdir(), `gsd-test-5848-${randomUUID().slice(0, 8)}-`));
47
+ tmpDirs.push(root);
48
+ const base = join(root, ".gsd", "projects", "project-id", "worktrees", "M003");
49
+ mkdirSync(join(base, ".gsd", "milestones", "M003", "slices", "S03"), { recursive: true });
50
+ return base;
51
+ }
52
+
53
+ function makeBrokenIsolatedWorktreeRevParse(): string {
54
+ const base = makeBrokenIsolatedWorktree();
55
+ mkdirSync(join(base, ".git"));
56
+ return base;
57
+ }
58
+
45
59
  function resetAutoState(): void {
46
60
  _setAutoActiveForTest(false);
47
61
  }
@@ -231,6 +245,108 @@ describe("Test 5 — postUnitPreVerification short-circuits on deterministic err
231
245
  });
232
246
  });
233
247
 
248
+ describe("Test 5b — broken isolated worktree short-circuits artifact retry (#5848)", () => {
249
+ test("pauses with worktree integrity failure instead of setting pendingVerificationRetry", async () => {
250
+ const { postUnitPreVerification } = await import("../auto-post-unit.ts");
251
+
252
+ const base = makeBrokenIsolatedWorktree();
253
+ const s = new AutoSession();
254
+ s.active = true;
255
+ s.basePath = base;
256
+ s.currentUnit = { type: "research-slice", id: "M003/S03", startedAt: Date.now() };
257
+ s.verificationRetryCount.set("research-slice:M003/S03", 2);
258
+
259
+ const notifications: string[] = [];
260
+ let pauseCalled = false;
261
+ const pctx = {
262
+ s,
263
+ ctx: {
264
+ ui: {
265
+ notify: (message: string) => {
266
+ notifications.push(message);
267
+ },
268
+ },
269
+ },
270
+ pi: {},
271
+ buildSnapshotOpts: () => ({}) as any,
272
+ lockBase: () => base,
273
+ stopAuto: async () => {},
274
+ pauseAuto: async () => {
275
+ pauseCalled = true;
276
+ },
277
+ updateProgressWidget: () => {},
278
+ } as any;
279
+
280
+ const result = await postUnitPreVerification(pctx, {
281
+ skipSettleDelay: true,
282
+ skipWorktreeSync: true,
283
+ });
284
+
285
+ assert.strictEqual(result, "dispatched", "worktree integrity failure must pause instead of retrying");
286
+ assert.strictEqual(pauseCalled, true, "pauseAuto must be called for a broken isolated worktree");
287
+ assert.strictEqual(s.pendingVerificationRetry, null, "pendingVerificationRetry must NOT be set");
288
+ assert.strictEqual(s.verificationRetryCount.has("research-slice:M003/S03"), false, "stale retry count must be cleared");
289
+ assert.ok(
290
+ notifications.some((message) => message.includes("Worktree integrity failure") && message.includes(".git missing")),
291
+ `expected worktree integrity notification, got: ${notifications.join("\n")}`,
292
+ );
293
+ assert.ok(
294
+ notifications.every((message) => !message.includes("Artifact verification failed")),
295
+ `must not surface artifact retry messaging, got: ${notifications.join("\n")}`,
296
+ );
297
+ });
298
+
299
+ test("pauses when git rev-parse cannot validate an isolated worktree", async () => {
300
+ const { postUnitPreVerification } = await import("../auto-post-unit.ts");
301
+
302
+ const base = makeBrokenIsolatedWorktreeRevParse();
303
+ const s = new AutoSession();
304
+ s.active = true;
305
+ s.basePath = base;
306
+ s.currentUnit = { type: "research-slice", id: "M003/S03", startedAt: Date.now() };
307
+ s.verificationRetryCount.set("research-slice:M003/S03", 2);
308
+
309
+ const notifications: string[] = [];
310
+ let pauseCalled = false;
311
+ const pctx = {
312
+ s,
313
+ ctx: {
314
+ ui: {
315
+ notify: (message: string) => {
316
+ notifications.push(message);
317
+ },
318
+ },
319
+ },
320
+ pi: {},
321
+ buildSnapshotOpts: () => ({}) as any,
322
+ lockBase: () => base,
323
+ stopAuto: async () => {},
324
+ pauseAuto: async () => {
325
+ pauseCalled = true;
326
+ },
327
+ updateProgressWidget: () => {},
328
+ } as any;
329
+
330
+ const result = await postUnitPreVerification(pctx, {
331
+ skipSettleDelay: true,
332
+ skipWorktreeSync: true,
333
+ });
334
+
335
+ assert.strictEqual(result, "dispatched", "worktree integrity failure must pause instead of retrying");
336
+ assert.strictEqual(pauseCalled, true, "pauseAuto must be called for a broken isolated worktree");
337
+ assert.strictEqual(s.pendingVerificationRetry, null, "pendingVerificationRetry must NOT be set");
338
+ assert.strictEqual(s.verificationRetryCount.has("research-slice:M003/S03"), false, "stale retry count must be cleared");
339
+ assert.ok(
340
+ notifications.some((message) => message.includes("Worktree integrity failure") && message.includes("git rev-parse")),
341
+ `expected git rev-parse worktree integrity notification, got: ${notifications.join("\n")}`,
342
+ );
343
+ assert.ok(
344
+ notifications.every((message) => !message.includes("Artifact verification failed")),
345
+ `must not surface artifact retry messaging, got: ${notifications.join("\n")}`,
346
+ );
347
+ });
348
+ });
349
+
234
350
  // ─── Test 6: Model-quality failures use standard retry path ──────────────────
235
351
 
236
352
  describe("Test 6 — non-deterministic failures use standard retry; tier escalates once (#4973)", () => {