gsd-pi 2.81.0-dev.72a81bdf3 → 2.82.0-dev.2841a1e44

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 (243) hide show
  1. package/README.md +49 -30
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/GSD-WORKFLOW.md +3 -1
  4. package/dist/resources/extensions/browser-tools/tools/screenshot.js +1 -0
  5. package/dist/resources/extensions/browser-tools/tools/zoom.js +1 -0
  6. package/dist/resources/extensions/cmux/index.js +5 -0
  7. package/dist/resources/extensions/gsd/auto/orchestrator.js +113 -6
  8. package/dist/resources/extensions/gsd/auto/phases.js +9 -0
  9. package/dist/resources/extensions/gsd/auto-post-unit.js +169 -124
  10. package/dist/resources/extensions/gsd/auto-prompts.js +13 -5
  11. package/dist/resources/extensions/gsd/auto-verification.js +28 -22
  12. package/dist/resources/extensions/gsd/auto.js +128 -52
  13. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +5 -0
  14. package/dist/resources/extensions/gsd/bootstrap/subagent-input.js +16 -7
  15. package/dist/resources/extensions/gsd/bootstrap/system-context.js +55 -12
  16. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +3 -1
  17. package/dist/resources/extensions/gsd/clean-root-preflight.js +170 -8
  18. package/dist/resources/extensions/gsd/commands/catalog.js +4 -1
  19. package/dist/resources/extensions/gsd/commands/handlers/core.js +22 -1
  20. package/dist/resources/extensions/gsd/commands-bootstrap.js +5 -0
  21. package/dist/resources/extensions/gsd/commands-handlers.js +15 -2
  22. package/dist/resources/extensions/gsd/context-store.js +112 -0
  23. package/dist/resources/extensions/gsd/db-writer.js +150 -84
  24. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  25. package/dist/resources/extensions/gsd/doctor-git-checks.js +41 -6
  26. package/dist/resources/extensions/gsd/knowledge-backfill.js +144 -0
  27. package/dist/resources/extensions/gsd/knowledge-capture.js +136 -0
  28. package/dist/resources/extensions/gsd/knowledge-parser.js +154 -0
  29. package/dist/resources/extensions/gsd/knowledge-projection.js +210 -0
  30. package/dist/resources/extensions/gsd/markdown-renderer.js +6 -1
  31. package/dist/resources/extensions/gsd/md-importer.js +1 -1
  32. package/dist/resources/extensions/gsd/memory-backfill.js +73 -17
  33. package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +222 -0
  34. package/dist/resources/extensions/gsd/migrate/command.js +5 -0
  35. package/dist/resources/extensions/gsd/migrate/preview.js +9 -0
  36. package/dist/resources/extensions/gsd/migrate/transformer.js +51 -4
  37. package/dist/resources/extensions/gsd/migrate/writer.js +11 -1
  38. package/dist/resources/extensions/gsd/prompts/system.md +2 -2
  39. package/dist/resources/extensions/gsd/provider-switch-observer.js +146 -0
  40. package/dist/resources/extensions/gsd/templates/knowledge.md +2 -2
  41. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +119 -0
  42. package/dist/resources/extensions/gsd/unit-context-manifest.js +25 -2
  43. package/dist/resources/extensions/gsd/verification-verdict.js +26 -0
  44. package/dist/resources/extensions/gsd/worktree-lifecycle.js +21 -2
  45. package/dist/resources/extensions/subagent/index.js +448 -78
  46. package/dist/resources/extensions/subagent/launch.js +77 -0
  47. package/dist/resources/extensions/subagent/run-store.js +148 -0
  48. package/dist/resources/extensions/visual-brief/artifact-policy.js +29 -0
  49. package/dist/resources/extensions/visual-brief/extension-manifest.json +8 -0
  50. package/dist/resources/extensions/visual-brief/index.js +5 -0
  51. package/dist/resources/extensions/visual-brief/page-contract.js +122 -0
  52. package/dist/resources/extensions/visual-brief/prompts.js +111 -0
  53. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  54. package/dist/web/standalone/.next/BUILD_ID +1 -1
  55. package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
  56. package/dist/web/standalone/.next/build-manifest.json +3 -3
  57. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  58. package/dist/web/standalone/.next/react-loadable-manifest.json +3 -3
  59. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  60. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  61. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  69. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.rsc +2 -2
  71. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  74. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  77. package/dist/web/standalone/.next/server/app/index.html +1 -1
  78. package/dist/web/standalone/.next/server/app/index.rsc +2 -2
  79. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
  81. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
  83. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  84. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
  86. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +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/{webpack-de742b64187e13fe.js → webpack-6a95bc41e0f7ec89.js} +1 -1
  94. package/dist/web/standalone/.next/static/css/0262768ec1b89d34.css +1 -0
  95. package/package.json +5 -4
  96. package/packages/contracts/dist/rpc.test.js +7 -0
  97. package/packages/contracts/dist/rpc.test.js.map +1 -1
  98. package/packages/contracts/dist/workflow.d.ts +21 -0
  99. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  100. package/packages/contracts/dist/workflow.js +24 -0
  101. package/packages/contracts/dist/workflow.js.map +1 -1
  102. package/packages/contracts/src/rpc.test.ts +8 -0
  103. package/packages/contracts/src/workflow.ts +24 -0
  104. package/packages/daemon/package.json +2 -2
  105. package/packages/mcp-server/README.md +14 -3
  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/package.json +2 -2
  111. package/packages/mcp-server/src/workflow-tools-parity.test.ts +244 -0
  112. package/packages/mcp-server/src/workflow-tools.test.ts +22 -0
  113. package/packages/mcp-server/src/workflow-tools.ts +168 -0
  114. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  115. package/packages/native/package.json +1 -1
  116. package/packages/pi-agent-core/package.json +1 -1
  117. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  118. package/packages/pi-ai/dist/index.d.ts +2 -2
  119. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  120. package/packages/pi-ai/dist/index.js +1 -1
  121. package/packages/pi-ai/dist/index.js.map +1 -1
  122. package/packages/pi-ai/dist/providers/transform-messages.d.ts +11 -0
  123. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  124. package/packages/pi-ai/dist/providers/transform-messages.js +20 -0
  125. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  126. package/packages/pi-ai/package.json +1 -1
  127. package/packages/pi-ai/src/index.ts +7 -2
  128. package/packages/pi-ai/src/providers/transform-messages.ts +24 -0
  129. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  130. package/packages/pi-coding-agent/dist/core/system-prompt.js +4 -4
  131. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  132. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.d.ts +2 -0
  133. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.d.ts.map +1 -0
  134. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.js +17 -0
  135. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.js.map +1 -0
  136. package/packages/pi-coding-agent/package.json +1 -1
  137. package/packages/pi-coding-agent/src/core/system-prompt.ts +4 -4
  138. package/packages/pi-coding-agent/src/tests/system-prompt-file-safety.test.ts +22 -0
  139. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  140. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  141. package/packages/pi-tui/dist/tui.js +5 -0
  142. package/packages/pi-tui/dist/tui.js.map +1 -1
  143. package/packages/pi-tui/package.json +1 -1
  144. package/packages/pi-tui/src/tui.ts +6 -0
  145. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  146. package/packages/rpc-client/package.json +1 -1
  147. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  148. package/pkg/package.json +1 -1
  149. package/src/resources/GSD-WORKFLOW.md +3 -1
  150. package/src/resources/extensions/browser-tools/tools/screenshot.ts +1 -0
  151. package/src/resources/extensions/browser-tools/tools/zoom.ts +1 -0
  152. package/src/resources/extensions/cmux/index.ts +6 -0
  153. package/src/resources/extensions/gsd/auto/contracts.ts +46 -11
  154. package/src/resources/extensions/gsd/auto/orchestrator.ts +118 -6
  155. package/src/resources/extensions/gsd/auto/phases.ts +14 -0
  156. package/src/resources/extensions/gsd/auto-post-unit.ts +194 -137
  157. package/src/resources/extensions/gsd/auto-prompts.ts +13 -5
  158. package/src/resources/extensions/gsd/auto-verification.ts +36 -34
  159. package/src/resources/extensions/gsd/auto.ts +136 -51
  160. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +6 -0
  161. package/src/resources/extensions/gsd/bootstrap/subagent-input.ts +16 -6
  162. package/src/resources/extensions/gsd/bootstrap/system-context.ts +58 -15
  163. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +3 -2
  164. package/src/resources/extensions/gsd/clean-root-preflight.ts +174 -8
  165. package/src/resources/extensions/gsd/commands/catalog.ts +4 -1
  166. package/src/resources/extensions/gsd/commands/handlers/core.ts +25 -1
  167. package/src/resources/extensions/gsd/commands-bootstrap.ts +10 -0
  168. package/src/resources/extensions/gsd/commands-handlers.ts +19 -2
  169. package/src/resources/extensions/gsd/context-store.ts +120 -1
  170. package/src/resources/extensions/gsd/db-writer.ts +167 -84
  171. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  172. package/src/resources/extensions/gsd/doctor-git-checks.ts +44 -6
  173. package/src/resources/extensions/gsd/doctor-types.ts +2 -0
  174. package/src/resources/extensions/gsd/knowledge-backfill.ts +164 -0
  175. package/src/resources/extensions/gsd/knowledge-capture.ts +160 -0
  176. package/src/resources/extensions/gsd/knowledge-parser.ts +174 -0
  177. package/src/resources/extensions/gsd/knowledge-projection.ts +241 -0
  178. package/src/resources/extensions/gsd/markdown-renderer.ts +6 -1
  179. package/src/resources/extensions/gsd/md-importer.ts +1 -1
  180. package/src/resources/extensions/gsd/memory-backfill.ts +89 -17
  181. package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +277 -0
  182. package/src/resources/extensions/gsd/migrate/command.ts +5 -0
  183. package/src/resources/extensions/gsd/migrate/preview.ts +10 -0
  184. package/src/resources/extensions/gsd/migrate/transformer.ts +58 -4
  185. package/src/resources/extensions/gsd/migrate/writer.ts +14 -1
  186. package/src/resources/extensions/gsd/prompts/system.md +2 -2
  187. package/src/resources/extensions/gsd/provider-switch-observer.ts +185 -0
  188. package/src/resources/extensions/gsd/templates/knowledge.md +2 -2
  189. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +75 -0
  190. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +408 -4
  191. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +6 -5
  192. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +4 -4
  193. package/src/resources/extensions/gsd/tests/brief-command.test.ts +89 -0
  194. package/src/resources/extensions/gsd/tests/browser-tools-compatibility-declarations.test.ts +62 -0
  195. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +107 -2
  196. package/src/resources/extensions/gsd/tests/closeout-git-deferral.test.ts +16 -0
  197. package/src/resources/extensions/gsd/tests/context-store-decisions-from-memories.test.ts +312 -0
  198. package/src/resources/extensions/gsd/tests/db-writer.test.ts +13 -8
  199. package/src/resources/extensions/gsd/tests/decisions-projection-from-memories.test.ts +453 -0
  200. package/src/resources/extensions/gsd/tests/decisions-stop-table-writes.test.ts +348 -0
  201. package/src/resources/extensions/gsd/tests/evidence-cross-ref.test.ts +38 -0
  202. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +8 -4
  203. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +11 -7
  204. package/src/resources/extensions/gsd/tests/integration/doctor-git.test.ts +44 -0
  205. package/src/resources/extensions/gsd/tests/integration/integration-lifecycle.test.ts +13 -5
  206. package/src/resources/extensions/gsd/tests/integration/migrate-command.test.ts +48 -3
  207. package/src/resources/extensions/gsd/tests/knowledge-backfill-projection.test.ts +323 -0
  208. package/src/resources/extensions/gsd/tests/knowledge-capture.test.ts +242 -0
  209. package/src/resources/extensions/gsd/tests/knowledge.test.ts +47 -2
  210. package/src/resources/extensions/gsd/tests/load-knowledge-block-rules-only.test.ts +209 -0
  211. package/src/resources/extensions/gsd/tests/memory-consolidation-scanner.test.ts +316 -0
  212. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +5 -1
  213. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +6 -1
  214. package/src/resources/extensions/gsd/tests/plan-milestone-sketch-render.test.ts +157 -0
  215. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +79 -1
  216. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +8 -0
  217. package/src/resources/extensions/gsd/tests/provider-switch-observer.test.ts +252 -0
  218. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +16 -4
  219. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +6 -0
  220. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +21 -0
  221. package/src/resources/extensions/gsd/tests/verification-verdict.test.ts +78 -0
  222. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +25 -0
  223. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +16 -0
  224. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +135 -0
  225. package/src/resources/extensions/gsd/unit-context-manifest.ts +35 -2
  226. package/src/resources/extensions/gsd/verification-verdict.ts +47 -0
  227. package/src/resources/extensions/gsd/workflow-logger.ts +4 -0
  228. package/src/resources/extensions/gsd/worktree-lifecycle.ts +20 -2
  229. package/src/resources/extensions/subagent/index.ts +567 -103
  230. package/src/resources/extensions/subagent/launch.ts +131 -0
  231. package/src/resources/extensions/subagent/run-store.ts +218 -0
  232. package/src/resources/extensions/subagent/tests/launch.test.ts +115 -0
  233. package/src/resources/extensions/subagent/tests/run-store.test.ts +111 -0
  234. package/src/resources/extensions/visual-brief/artifact-policy.ts +41 -0
  235. package/src/resources/extensions/visual-brief/extension-manifest.json +8 -0
  236. package/src/resources/extensions/visual-brief/index.ts +8 -0
  237. package/src/resources/extensions/visual-brief/page-contract.ts +134 -0
  238. package/src/resources/extensions/visual-brief/prompts.ts +147 -0
  239. package/src/resources/extensions/visual-brief/tests/visual-brief.test.ts +172 -0
  240. package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +0 -1
  241. package/dist/web/standalone/.next/static/css/54ec2745c1da488b.css +0 -1
  242. /package/dist/web/standalone/.next/static/{rIkMv4YSNlfSeqmGqWVns → Qgr2B_MRhPxC0z8fwv4vT}/_buildManifest.js +0 -0
  243. /package/dist/web/standalone/.next/static/{rIkMv4YSNlfSeqmGqWVns → Qgr2B_MRhPxC0z8fwv4vT}/_ssgManifest.js +0 -0
@@ -7,8 +7,8 @@
7
7
  *
8
8
  * Split into two functions called sequentially by auto-loop with
9
9
  * the verification gate between them:
10
- * 1. postUnitPreVerification() — commit, doctor, state rebuild, worktree sync, artifact verification
11
- * 2. postUnitPostVerification() — DB dual-write, hooks, triage, quick-tasks
10
+ * 1. postUnitPreVerification() — closeout git for non-task units, doctor, state rebuild, worktree sync, artifact verification
11
+ * 2. postUnitPostVerification() — post-verified task git, DB dual-write, hooks, triage, quick-tasks
12
12
  *
13
13
  * Extracted from the pre-loop agent_end handler in auto.ts.
14
14
  */
@@ -62,8 +62,7 @@ import { runSafely } from "./auto-utils.js";
62
62
  import type { AutoSession, SidecarItem } from "./auto/session.js";
63
63
  import { getEvidence, clearEvidenceFromDisk } from "./safety/evidence-collector.js";
64
64
  import { validateFileChanges } from "./safety/file-change-validator.js";
65
- // crossReferenceEvidence available for future use when verification_evidence is stored in DB
66
- // import { crossReferenceEvidence, type ClaimedEvidence } from "./safety/evidence-cross-ref.js";
65
+ import { crossReferenceEvidence, type ClaimedEvidence } from "./safety/evidence-cross-ref.js";
67
66
  import { validateContent } from "./safety/content-validator.js";
68
67
  import { resolveSafetyHarnessConfig } from "./safety/safety-harness.js";
69
68
  import { resolveExpectedArtifactPath as resolveArtifactForContent } from "./auto-artifact-paths.js";
@@ -222,6 +221,10 @@ export function _shouldDispatchQuickTaskForTest(
222
221
  state.currentUnit.type !== "quick-task";
223
222
  }
224
223
 
224
+ export function shouldDeferCloseoutGitAction(unitType: string): boolean {
225
+ return unitType === "execute-task";
226
+ }
227
+
225
228
  /** Unit types that only touch `.gsd/` internal state files (no code changes).
226
229
  * Auto-commit is skipped for these — their state files are picked up by the
227
230
  * next actual task commit via `smartStage()`. */
@@ -466,6 +469,139 @@ export async function autoCommitUnit(
466
469
  }
467
470
  }
468
471
 
472
+ async function runCloseoutGitAction(
473
+ pctx: PostUnitContext,
474
+ unit: NonNullable<AutoSession["currentUnit"]>,
475
+ ): Promise<"continue" | "dispatched"> {
476
+ const { s, ctx, pi, pauseAuto } = pctx;
477
+ const prefs = loadEffectiveGSDPreferences()?.preferences;
478
+ const uokFlags = resolveUokFlags(prefs);
479
+ const turnAction: TurnGitActionMode = uokFlags.gitops ? uokFlags.gitopsTurnAction : "commit";
480
+ const traceId = s.currentTraceId ?? `turn:${unit.startedAt}`;
481
+ const turnId = s.currentTurnId ?? `${unit.type}/${unit.id}/${unit.startedAt}`;
482
+
483
+ s.lastGitActionFailure = null;
484
+ s.lastGitActionStatus = null;
485
+
486
+ try {
487
+ let taskContext: TaskCommitContext | undefined;
488
+
489
+ if (turnAction === "commit" && unit.type === "execute-task") {
490
+ taskContext = await buildTaskCommitContextForUnit(s.basePath, unit.id);
491
+ }
492
+
493
+ // Invalidate the nativeHasChanges cache before auto-commit (#1853).
494
+ // The cache has a 10-second TTL and is keyed by basePath. A stale
495
+ // `false` result causes autoCommit to skip staging entirely.
496
+ _resetHasChangesCache();
497
+
498
+ const skipLifecycleCommit =
499
+ turnAction === "commit" && LIFECYCLE_ONLY_UNITS.has(unit.type);
500
+
501
+ if (skipLifecycleCommit) {
502
+ debugLog("postUnit", {
503
+ phase: "git-action-skipped",
504
+ reason: "lifecycle-only-unit",
505
+ unitType: unit.type,
506
+ unitId: unit.id,
507
+ });
508
+ } else {
509
+ const gitResult = runTurnGitAction({
510
+ basePath: s.basePath,
511
+ action: turnAction,
512
+ unitType: unit.type,
513
+ unitId: unit.id,
514
+ taskContext,
515
+ });
516
+
517
+ if (uokFlags.gitops) {
518
+ writeTurnGitTransaction({
519
+ basePath: s.basePath,
520
+ traceId,
521
+ turnId,
522
+ unitType: unit.type,
523
+ unitId: unit.id,
524
+ stage: "publish",
525
+ action: turnAction,
526
+ push: uokFlags.gitopsTurnPush,
527
+ status: gitResult.status,
528
+ error: gitResult.error,
529
+ metadata: {
530
+ dirty: gitResult.dirty,
531
+ commitMessage: gitResult.commitMessage,
532
+ snapshotLabel: gitResult.snapshotLabel,
533
+ },
534
+ });
535
+ }
536
+
537
+ if (gitResult.status === "failed") {
538
+ s.lastGitActionFailure = gitResult.error ?? `git ${turnAction} failed`;
539
+ s.lastGitActionStatus = "failed";
540
+ if (uokFlags.gitops && uokFlags.gates) {
541
+ const parsed = parseUnitId(unit.id);
542
+ const gateRunner = new UokGateRunner();
543
+ gateRunner.register({
544
+ id: "closeout-git-action",
545
+ type: "closeout",
546
+ execute: async () => ({
547
+ outcome: "fail",
548
+ failureClass: "git",
549
+ rationale: `turn git action "${turnAction}" failed`,
550
+ findings: gitResult.error ?? "unknown git failure",
551
+ }),
552
+ });
553
+ await gateRunner.run("closeout-git-action", {
554
+ basePath: s.basePath,
555
+ traceId,
556
+ turnId,
557
+ milestoneId: parsed.milestone ?? undefined,
558
+ sliceId: parsed.slice ?? undefined,
559
+ taskId: parsed.task ?? undefined,
560
+ unitType: unit.type,
561
+ unitId: unit.id,
562
+ });
563
+ }
564
+
565
+ const failureMsg = `Git ${turnAction} failed: ${(gitResult.error ?? "unknown error").split("\n")[0]}`;
566
+ ctx.ui.notify(failureMsg, "error");
567
+ debugLog("postUnit", {
568
+ phase: "git-action-failed-blocking",
569
+ action: turnAction,
570
+ error: gitResult.error ?? "unknown error",
571
+ });
572
+ await pauseAuto(ctx, pi);
573
+ return "dispatched";
574
+ }
575
+
576
+ s.lastGitActionStatus = "ok";
577
+
578
+ if (turnAction === "commit" && gitResult.commitMessage) {
579
+ ctx.ui.notify(`Committed: ${gitResult.commitMessage.split("\n")[0]}`, "info");
580
+ } else if (turnAction === "snapshot" && gitResult.snapshotLabel) {
581
+ ctx.ui.notify(`Snapshot recorded: ${gitResult.snapshotLabel}`, "info");
582
+ }
583
+ }
584
+ } catch (e) {
585
+ const message = e instanceof Error ? e.message : String(e);
586
+ s.lastGitActionFailure = message;
587
+ s.lastGitActionStatus = "failed";
588
+ debugLog("postUnit", { phase: "git-action", error: message, action: turnAction });
589
+ ctx.ui.notify(`Git ${turnAction} failed: ${message.split("\n")[0]}`, uokFlags.gitops ? "error" : "warning");
590
+ if (uokFlags.gitops) {
591
+ await pauseAuto(ctx, pi);
592
+ return "dispatched";
593
+ }
594
+ }
595
+
596
+ // GitHub sync (non-blocking, opt-in)
597
+ await runSafely("postUnit", "github-sync", async () => {
598
+ const { runGitHubSync } = await import("../github-sync/sync.js");
599
+ await runGitHubSync(s.basePath, unit.type, unit.id);
600
+ });
601
+
602
+ return "continue";
603
+ }
604
+
469
605
  /**
470
606
  * Pre-verification processing: parallel worker signal check, cache invalidation,
471
607
  * auto-commit, doctor run, state rebuild, worktree sync, artifact verification.
@@ -476,7 +612,7 @@ export async function autoCommitUnit(
476
612
  * - "retry" — artifact verification failed, s.pendingVerificationRetry set for loop re-iteration
477
613
  */
478
614
  export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreVerificationOpts): Promise<"dispatched" | "continue" | "retry"> {
479
- const { s, ctx, pi, buildSnapshotOpts, stopAuto, pauseAuto } = pctx;
615
+ const { s, ctx, pi, stopAuto, pauseAuto } = pctx;
480
616
 
481
617
  // ── Parallel worker signal check ──
482
618
  const milestoneLock = process.env.GSD_MILESTONE_LOCK;
@@ -502,135 +638,22 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
502
638
  await new Promise(r => setTimeout(r, 100));
503
639
  }
504
640
 
505
- const prefs = loadEffectiveGSDPreferences()?.preferences;
506
- const uokFlags = resolveUokFlags(prefs);
507
-
508
641
  // Turn-level git action (commit | snapshot | status-only)
509
642
  if (s.currentUnit) {
510
643
  const unit = s.currentUnit;
511
- const turnAction: TurnGitActionMode = uokFlags.gitops ? uokFlags.gitopsTurnAction : "commit";
512
- const traceId = s.currentTraceId ?? `turn:${unit.startedAt}`;
513
- const turnId = s.currentTurnId ?? `${unit.type}/${unit.id}/${unit.startedAt}`;
514
- s.lastGitActionFailure = null;
515
- s.lastGitActionStatus = null;
516
- try {
517
- let taskContext: TaskCommitContext | undefined;
518
-
519
- if (turnAction === "commit" && s.currentUnit.type === "execute-task") {
520
- taskContext = await buildTaskCommitContextForUnit(s.basePath, s.currentUnit.id);
521
- }
522
-
523
- // Invalidate the nativeHasChanges cache before auto-commit (#1853).
524
- // The cache has a 10-second TTL and is keyed by basePath. A stale
525
- // `false` result causes autoCommit to skip staging entirely, leaving
526
- // code files only in the working tree where they are destroyed by
527
- // `git worktree remove --force` during teardown.
528
- _resetHasChangesCache();
529
-
530
- const skipLifecycleCommit =
531
- turnAction === "commit" && LIFECYCLE_ONLY_UNITS.has(s.currentUnit.type);
532
-
533
- if (skipLifecycleCommit) {
534
- debugLog("postUnit", {
535
- phase: "git-action-skipped",
536
- reason: "lifecycle-only-unit",
537
- unitType: s.currentUnit.type,
538
- unitId: s.currentUnit.id,
539
- });
540
- } else {
541
- const gitResult = runTurnGitAction({
542
- basePath: s.basePath,
543
- action: turnAction,
544
- unitType: s.currentUnit.type,
545
- unitId: s.currentUnit.id,
546
- taskContext,
547
- });
548
-
549
- if (uokFlags.gitops) {
550
- writeTurnGitTransaction({
551
- basePath: s.basePath,
552
- traceId,
553
- turnId,
554
- unitType: unit.type,
555
- unitId: unit.id,
556
- stage: "publish",
557
- action: turnAction,
558
- push: uokFlags.gitopsTurnPush,
559
- status: gitResult.status,
560
- error: gitResult.error,
561
- metadata: {
562
- dirty: gitResult.dirty,
563
- commitMessage: gitResult.commitMessage,
564
- snapshotLabel: gitResult.snapshotLabel,
565
- },
566
- });
567
- }
568
-
569
- if (gitResult.status === "failed") {
570
- s.lastGitActionFailure = gitResult.error ?? `git ${turnAction} failed`;
571
- s.lastGitActionStatus = "failed";
572
- if (uokFlags.gitops && uokFlags.gates) {
573
- const parsed = parseUnitId(unit.id);
574
- const gateRunner = new UokGateRunner();
575
- gateRunner.register({
576
- id: "closeout-git-action",
577
- type: "closeout",
578
- execute: async () => ({
579
- outcome: "fail",
580
- failureClass: "git",
581
- rationale: `turn git action "${turnAction}" failed`,
582
- findings: gitResult.error ?? "unknown git failure",
583
- }),
584
- });
585
- await gateRunner.run("closeout-git-action", {
586
- basePath: s.basePath,
587
- traceId,
588
- turnId,
589
- milestoneId: parsed.milestone ?? undefined,
590
- sliceId: parsed.slice ?? undefined,
591
- taskId: parsed.task ?? undefined,
592
- unitType: unit.type,
593
- unitId: unit.id,
594
- });
595
- }
596
-
597
- const failureMsg = `Git ${turnAction} failed: ${(gitResult.error ?? "unknown error").split("\n")[0]}`;
598
- ctx.ui.notify(failureMsg, "error");
599
- debugLog("postUnit", {
600
- phase: "git-action-failed-blocking",
601
- action: turnAction,
602
- error: gitResult.error ?? "unknown error",
603
- });
604
- await pauseAuto(ctx, pi);
605
- return "dispatched";
606
- }
607
-
608
- s.lastGitActionStatus = "ok";
609
-
610
- if (turnAction === "commit" && gitResult.commitMessage) {
611
- ctx.ui.notify(`Committed: ${gitResult.commitMessage.split("\n")[0]}`, "info");
612
- } else if (turnAction === "snapshot" && gitResult.snapshotLabel) {
613
- ctx.ui.notify(`Snapshot recorded: ${gitResult.snapshotLabel}`, "info");
614
- }
615
- }
616
- } catch (e) {
617
- const message = e instanceof Error ? e.message : String(e);
618
- s.lastGitActionFailure = message;
619
- s.lastGitActionStatus = "failed";
620
- debugLog("postUnit", { phase: "git-action", error: message, action: turnAction });
621
- ctx.ui.notify(`Git ${turnAction} failed: ${message.split("\n")[0]}`, uokFlags.gitops ? "error" : "warning");
622
- if (uokFlags.gitops) {
623
- await pauseAuto(ctx, pi);
644
+ if (shouldDeferCloseoutGitAction(unit.type)) {
645
+ debugLog("postUnit", {
646
+ phase: "git-action-deferred-until-verification",
647
+ unitType: unit.type,
648
+ unitId: unit.id,
649
+ });
650
+ } else {
651
+ const gitActionResult = await runCloseoutGitAction(pctx, unit);
652
+ if (gitActionResult === "dispatched") {
624
653
  return "dispatched";
625
654
  }
626
655
  }
627
656
 
628
- // GitHub sync (non-blocking, opt-in)
629
- await runSafely("postUnit", "github-sync", async () => {
630
- const { runGitHubSync } = await import("../github-sync/sync.js");
631
- await runGitHubSync(s.basePath, unit.type, unit.id);
632
- });
633
-
634
657
  // Prune dead bg-shell processes
635
658
  await runSafely("postUnit", "prune-bg-shell", async () => {
636
659
  const { pruneDeadProcesses } = await import("../bg-shell/process-manager.js");
@@ -866,7 +889,7 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
866
889
  if (safetyConfig.enabled) {
867
890
  const { milestone: sMid, slice: sSid, task: sTid } = parseUnitId(s.currentUnit.id);
868
891
 
869
- // File change validation (execute-task only, after auto-commit)
892
+ // File change validation (execute-task only, after unit execution)
870
893
  if (safetyConfig.file_change_validation && s.currentUnit.type === "execute-task" && sMid && sSid && sTid && isDbAvailable()) {
871
894
  try {
872
895
  const taskRow = getTask(sMid, sSid, sTid);
@@ -902,15 +925,42 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
902
925
  const bashCalls = actual.filter(e => e.kind === "bash");
903
926
  if (sMid && sSid && sTid && isDbAvailable()) {
904
927
  const taskRow = getTask(sMid, sSid, sTid);
905
- const claimedCommands = getVerificationEvidence(sMid, sSid, sTid)
906
- .map((row) => row.command)
907
- .filter((command): command is string => typeof command === "string" && command.trim().length > 0);
908
- if (taskRow?.status === "complete" && claimedCommands.length > 0 && bashCalls.length === 0) {
909
- logWarning("safety", "task claimed verification command evidence but no execution tool calls were recorded");
910
- ctx.ui.notify(
911
- `Safety: task ${sTid} claimed command evidence but no execution tool calls were recorded`,
912
- "warning",
913
- );
928
+ if (taskRow?.status === "complete") {
929
+ const claimedEvidence: ClaimedEvidence[] = getVerificationEvidence(sMid, sSid, sTid)
930
+ .map((row) => ({
931
+ command: row.command,
932
+ exitCode: row.exit_code,
933
+ verdict: row.verdict,
934
+ }))
935
+ .filter((row) => typeof row.command === "string" && row.command.trim().length > 0);
936
+ const mismatches = crossReferenceEvidence(claimedEvidence, actual);
937
+
938
+ for (const mismatch of mismatches) {
939
+ const logMessage = `evidence-xref: ${mismatch.reason}`;
940
+ if (mismatch.severity === "error") {
941
+ logError("safety", logMessage);
942
+ } else {
943
+ logWarning("safety", logMessage);
944
+ }
945
+ }
946
+
947
+ if (claimedEvidence.length > 0 && bashCalls.length === 0) {
948
+ logWarning("safety", "task claimed verification command evidence but no execution tool calls were recorded");
949
+ ctx.ui.notify(
950
+ `Safety: task ${sTid} claimed command evidence but no execution tool calls were recorded`,
951
+ "warning",
952
+ );
953
+ }
954
+
955
+ const blockingMismatch = mismatches.find((mismatch) => mismatch.severity === "error");
956
+ if (blockingMismatch) {
957
+ ctx.ui.notify(
958
+ `Safety: task ${sTid} claimed passing verification that failed in recorded execution`,
959
+ "error",
960
+ );
961
+ await pauseAuto(ctx, pi);
962
+ return "dispatched";
963
+ }
914
964
  }
915
965
  }
916
966
  } catch (e) {
@@ -1169,6 +1219,13 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
1169
1219
  const { s, ctx, pi, buildSnapshotOpts, lockBase, stopAuto, pauseAuto, updateProgressWidget } = pctx;
1170
1220
 
1171
1221
  if (s.currentUnit) {
1222
+ if (shouldDeferCloseoutGitAction(s.currentUnit.type)) {
1223
+ const gitActionResult = await runCloseoutGitAction(pctx, s.currentUnit);
1224
+ if (gitActionResult === "dispatched") {
1225
+ return "stopped";
1226
+ }
1227
+ }
1228
+
1172
1229
  try {
1173
1230
  const codebasePrefs = loadEffectiveGSDPreferences()?.preferences?.codebase;
1174
1231
  const refresh = ensureCodebaseMapFresh(
@@ -806,14 +806,22 @@ export async function inlineDecisionsFromDb(
806
806
  try {
807
807
  const { isDbAvailable } = await import("./gsd-db.js");
808
808
  if (isDbAvailable()) {
809
- const { queryDecisions, formatDecisionsForPrompt } = await import("./context-store.js");
809
+ // ADR-013 Phase 6 cutover (Stage 1): read decisions from the `memories`
810
+ // table. Both `queryDecisions` (legacy) and `queryDecisionsFromMemories`
811
+ // return identical Decision[] for active rows once Phase 5 dual-write is
812
+ // caught up. Switching the read here lets the destructive Phase 6 step
813
+ // (#5755) retire the legacy `decisions` table without changing prompt
814
+ // contents. Projection regen (`DECISIONS.md`) still sources from the
815
+ // legacy table — that switch lands separately to handle superseded
816
+ // history cleanly.
817
+ const { queryDecisionsFromMemories, formatDecisionsForPrompt } = await import("./context-store.js");
810
818
 
811
819
  // First query: try with both milestoneId and scope (if scope provided)
812
- let decisions = queryDecisions({ milestoneId, scope });
820
+ let decisions = queryDecisionsFromMemories({ milestoneId, scope });
813
821
 
814
822
  // Cascade: if empty AND scope was provided, retry without scope
815
823
  if (decisions.length === 0 && scope) {
816
- decisions = queryDecisions({ milestoneId });
824
+ decisions = queryDecisionsFromMemories({ milestoneId });
817
825
  }
818
826
 
819
827
  if (decisions.length > 0) {
@@ -3189,7 +3197,7 @@ export async function buildParallelResearchSlicesPrompt(
3189
3197
  subagentSections.push([
3190
3198
  `### ${slice.id}: ${slice.title}`,
3191
3199
  "",
3192
- `Use this as the prompt for a \`subagent\` call${modelSuffix} (agent: \`gsd-executor\` or the default agent):`,
3200
+ `Use this as the prompt for a \`subagent\` call${modelSuffix} (agent: \`scout\`):`,
3193
3201
  "",
3194
3202
  "```",
3195
3203
  slicePrompt,
@@ -3271,7 +3279,7 @@ export async function buildGateEvaluatePrompt(
3271
3279
  subagentSections.push([
3272
3280
  `### ${def.id}: ${def.question}`,
3273
3281
  "",
3274
- `Use this as the prompt for a \`subagent\` call${modelSuffix}:`,
3282
+ `Use this as the prompt for a \`subagent\` call${modelSuffix} (agent: \`tester\`):`,
3275
3283
  "",
3276
3284
  "```",
3277
3285
  subPrompt,
@@ -40,6 +40,7 @@ import { join } from "node:path";
40
40
  import { resolveUokFlags } from "./uok/flags.js";
41
41
  import { UokGateRunner } from "./uok/gate-runner.js";
42
42
  import { verificationRetryKey } from "./auto/verification-retry-policy.js";
43
+ import { decideVerificationVerdict } from "./verification-verdict.js";
43
44
 
44
45
  export interface VerificationContext {
45
46
  s: AutoSession;
@@ -49,12 +50,6 @@ export interface VerificationContext {
49
50
 
50
51
  export type VerificationResult = "continue" | "retry" | "pause";
51
52
 
52
- function isInfraVerificationFailure(stderr: string): boolean {
53
- return /\b(ENOENT|ENOTFOUND|ETIMEDOUT|ECONNRESET|EAI_AGAIN|spawn\s+\S+\s+ENOENT|command not found)\b/i.test(
54
- stderr,
55
- );
56
- }
57
-
58
53
  /**
59
54
  * Post-unit guard for `validate-milestone` units (#4094).
60
55
  *
@@ -196,7 +191,7 @@ async function countIncompleteSlices(basePath: string, milestoneId: string): Pro
196
191
  /**
197
192
  * Run the verification gate for the current execute-task unit.
198
193
  * Returns:
199
- * - "continue" — gate passed (or no checks configured), proceed normally
194
+ * - "continue" — host-owned verification passed, proceed normally
200
195
  * - "retry" — gate failed with retries remaining, s.pendingVerificationRetry set for loop re-iteration
201
196
  * - "pause" — gate failed with retries exhausted, pauseAuto already called
202
197
  */
@@ -260,6 +255,11 @@ export async function runPostUnitVerification(
260
255
  }
261
256
  }
262
257
 
258
+ const verdict = decideVerificationVerdict(s.currentUnit.type, result);
259
+ if (!verdict.passed) {
260
+ result.passed = false;
261
+ }
262
+
263
263
  if (uokFlags.gates) {
264
264
  const gateRunner = new UokGateRunner();
265
265
  gateRunner.register({
@@ -272,10 +272,12 @@ export async function runPostUnitVerification(
272
272
  : "verification",
273
273
  rationale: result.passed
274
274
  ? "verification checks passed"
275
- : "verification checks failed",
275
+ : verdict.reason === "no-host-checks"
276
+ ? "no runnable host-owned verification checks discovered"
277
+ : "verification checks failed",
276
278
  findings: result.passed
277
279
  ? ""
278
- : formatFailureContext(result),
280
+ : verdict.failureContext || formatFailureContext(result),
279
281
  }),
280
282
  });
281
283
 
@@ -343,13 +345,18 @@ export async function runPostUnitVerification(
343
345
  writeVerificationJSON(result, tasksDir, tid, s.currentUnit.id);
344
346
  } else {
345
347
  const nextAttempt = attempt + 1;
348
+ const includeRetryMetadata =
349
+ !result.passed &&
350
+ verdict.retryable &&
351
+ autoFixEnabled &&
352
+ nextAttempt <= maxRetries;
346
353
  writeVerificationJSON(
347
354
  result,
348
355
  tasksDir,
349
356
  tid,
350
357
  s.currentUnit.id,
351
- nextAttempt,
352
- maxRetries,
358
+ includeRetryMetadata ? nextAttempt : undefined,
359
+ includeRetryMetadata ? maxRetries : undefined,
353
360
  );
354
361
  }
355
362
  }
@@ -358,26 +365,6 @@ export async function runPostUnitVerification(
358
365
  }
359
366
  }
360
367
 
361
- const advisoryFailure =
362
- !result.passed &&
363
- (result.discoverySource === "package-json" ||
364
- result.checks.some((check) =>
365
- isInfraVerificationFailure(check.stderr),
366
- ));
367
-
368
- if (advisoryFailure) {
369
- s.verificationRetryCount.delete(retryKey);
370
- s.verificationRetryFailureHashes.delete(retryKey);
371
- s.pendingVerificationRetry = null;
372
- ctx.ui.notify(
373
- result.discoverySource === "package-json"
374
- ? "Verification failed in auto-discovered package.json checks — treating as advisory."
375
- : "Verification failed due to infrastructure/runtime environment issues — treating as advisory.",
376
- "warning",
377
- );
378
- return "continue";
379
- }
380
-
381
368
  // ── Post-execution checks (run after main verification passes for execute-task units) ──
382
369
  let postExecChecks: PostExecutionCheckJSON[] | undefined;
383
370
  let postExecBlockingFailure = false;
@@ -572,6 +559,17 @@ export async function runPostUnitVerification(
572
559
  s.verificationRetryFailureHashes.delete(retryKey);
573
560
  s.pendingVerificationRetry = null;
574
561
  return "continue";
562
+ } else if (verdict.reason === "no-host-checks") {
563
+ s.verificationRetryCount.delete(retryKey);
564
+ s.verificationRetryFailureHashes.delete(retryKey);
565
+ s.pendingVerificationRetry = null;
566
+ ctx.ui.notify(
567
+ "Verification gate FAILED — no runnable host-owned verification checks were discovered. Pausing for human review.",
568
+ "error",
569
+ );
570
+ process.stderr.write(`verification-gate: ${verdict.failureContext}\n`);
571
+ await pauseAuto(ctx, pi);
572
+ return "pause";
575
573
  } else if (postExecBlockingFailure) {
576
574
  // Post-execution failures are cross-task consistency issues — retrying the same task won't fix them.
577
575
  // Skip retry and pause immediately for human review.
@@ -589,7 +587,7 @@ export async function runPostUnitVerification(
589
587
  s.verificationRetryCount.set(retryKey, nextAttempt);
590
588
  s.pendingVerificationRetry = {
591
589
  unitId: s.currentUnit.id,
592
- failureContext: formatFailureContext(result),
590
+ failureContext: verdict.failureContext || formatFailureContext(result),
593
591
  attempt: nextAttempt,
594
592
  };
595
593
  const failedCmds = result.checks
@@ -623,9 +621,13 @@ export async function runPostUnitVerification(
623
621
  return "pause";
624
622
  }
625
623
  } catch (err) {
626
- // Gate errors are non-fatal
627
624
  logWarning("engine", `verification-gate error: ${(err as Error).message}`);
628
- return "continue";
625
+ ctx.ui.notify(
626
+ `Verification gate errored before producing an authoritative verdict: ${(err as Error).message}`,
627
+ "error",
628
+ );
629
+ await pauseAuto(ctx, pi);
630
+ return "pause";
629
631
  }
630
632
  }
631
633