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
  */
@@ -37,8 +37,7 @@ import { debugLog } from "./debug-logger.js";
37
37
  import { runSafely } from "./auto-utils.js";
38
38
  import { getEvidence, clearEvidenceFromDisk } from "./safety/evidence-collector.js";
39
39
  import { validateFileChanges } from "./safety/file-change-validator.js";
40
- // crossReferenceEvidence available for future use when verification_evidence is stored in DB
41
- // import { crossReferenceEvidence, type ClaimedEvidence } from "./safety/evidence-cross-ref.js";
40
+ import { crossReferenceEvidence } from "./safety/evidence-cross-ref.js";
42
41
  import { validateContent } from "./safety/content-validator.js";
43
42
  import { resolveSafetyHarnessConfig } from "./safety/safety-harness.js";
44
43
  import { resolveExpectedArtifactPath as resolveArtifactForContent } from "./auto-artifact-paths.js";
@@ -171,6 +170,9 @@ export function _shouldDispatchQuickTaskForTest(state) {
171
170
  !!state.currentUnit &&
172
171
  state.currentUnit.type !== "quick-task";
173
172
  }
173
+ export function shouldDeferCloseoutGitAction(unitType) {
174
+ return unitType === "execute-task";
175
+ }
174
176
  /** Unit types that only touch `.gsd/` internal state files (no code changes).
175
177
  * Auto-commit is skipped for these — their state files are picked up by the
176
178
  * next actual task commit via `smartStage()`. */
@@ -360,6 +362,124 @@ export async function autoCommitUnit(basePath, unitType, unitId, ctx) {
360
362
  return null;
361
363
  }
362
364
  }
365
+ async function runCloseoutGitAction(pctx, unit) {
366
+ const { s, ctx, pi, pauseAuto } = pctx;
367
+ const prefs = loadEffectiveGSDPreferences()?.preferences;
368
+ const uokFlags = resolveUokFlags(prefs);
369
+ const turnAction = uokFlags.gitops ? uokFlags.gitopsTurnAction : "commit";
370
+ const traceId = s.currentTraceId ?? `turn:${unit.startedAt}`;
371
+ const turnId = s.currentTurnId ?? `${unit.type}/${unit.id}/${unit.startedAt}`;
372
+ s.lastGitActionFailure = null;
373
+ s.lastGitActionStatus = null;
374
+ try {
375
+ let taskContext;
376
+ if (turnAction === "commit" && unit.type === "execute-task") {
377
+ taskContext = await buildTaskCommitContextForUnit(s.basePath, unit.id);
378
+ }
379
+ // Invalidate the nativeHasChanges cache before auto-commit (#1853).
380
+ // The cache has a 10-second TTL and is keyed by basePath. A stale
381
+ // `false` result causes autoCommit to skip staging entirely.
382
+ _resetHasChangesCache();
383
+ const skipLifecycleCommit = turnAction === "commit" && LIFECYCLE_ONLY_UNITS.has(unit.type);
384
+ if (skipLifecycleCommit) {
385
+ debugLog("postUnit", {
386
+ phase: "git-action-skipped",
387
+ reason: "lifecycle-only-unit",
388
+ unitType: unit.type,
389
+ unitId: unit.id,
390
+ });
391
+ }
392
+ else {
393
+ const gitResult = runTurnGitAction({
394
+ basePath: s.basePath,
395
+ action: turnAction,
396
+ unitType: unit.type,
397
+ unitId: unit.id,
398
+ taskContext,
399
+ });
400
+ if (uokFlags.gitops) {
401
+ writeTurnGitTransaction({
402
+ basePath: s.basePath,
403
+ traceId,
404
+ turnId,
405
+ unitType: unit.type,
406
+ unitId: unit.id,
407
+ stage: "publish",
408
+ action: turnAction,
409
+ push: uokFlags.gitopsTurnPush,
410
+ status: gitResult.status,
411
+ error: gitResult.error,
412
+ metadata: {
413
+ dirty: gitResult.dirty,
414
+ commitMessage: gitResult.commitMessage,
415
+ snapshotLabel: gitResult.snapshotLabel,
416
+ },
417
+ });
418
+ }
419
+ if (gitResult.status === "failed") {
420
+ s.lastGitActionFailure = gitResult.error ?? `git ${turnAction} failed`;
421
+ s.lastGitActionStatus = "failed";
422
+ if (uokFlags.gitops && uokFlags.gates) {
423
+ const parsed = parseUnitId(unit.id);
424
+ const gateRunner = new UokGateRunner();
425
+ gateRunner.register({
426
+ id: "closeout-git-action",
427
+ type: "closeout",
428
+ execute: async () => ({
429
+ outcome: "fail",
430
+ failureClass: "git",
431
+ rationale: `turn git action "${turnAction}" failed`,
432
+ findings: gitResult.error ?? "unknown git failure",
433
+ }),
434
+ });
435
+ await gateRunner.run("closeout-git-action", {
436
+ basePath: s.basePath,
437
+ traceId,
438
+ turnId,
439
+ milestoneId: parsed.milestone ?? undefined,
440
+ sliceId: parsed.slice ?? undefined,
441
+ taskId: parsed.task ?? undefined,
442
+ unitType: unit.type,
443
+ unitId: unit.id,
444
+ });
445
+ }
446
+ const failureMsg = `Git ${turnAction} failed: ${(gitResult.error ?? "unknown error").split("\n")[0]}`;
447
+ ctx.ui.notify(failureMsg, "error");
448
+ debugLog("postUnit", {
449
+ phase: "git-action-failed-blocking",
450
+ action: turnAction,
451
+ error: gitResult.error ?? "unknown error",
452
+ });
453
+ await pauseAuto(ctx, pi);
454
+ return "dispatched";
455
+ }
456
+ s.lastGitActionStatus = "ok";
457
+ if (turnAction === "commit" && gitResult.commitMessage) {
458
+ ctx.ui.notify(`Committed: ${gitResult.commitMessage.split("\n")[0]}`, "info");
459
+ }
460
+ else if (turnAction === "snapshot" && gitResult.snapshotLabel) {
461
+ ctx.ui.notify(`Snapshot recorded: ${gitResult.snapshotLabel}`, "info");
462
+ }
463
+ }
464
+ }
465
+ catch (e) {
466
+ const message = e instanceof Error ? e.message : String(e);
467
+ s.lastGitActionFailure = message;
468
+ s.lastGitActionStatus = "failed";
469
+ debugLog("postUnit", { phase: "git-action", error: message, action: turnAction });
470
+ ctx.ui.notify(`Git ${turnAction} failed: ${message.split("\n")[0]}`, uokFlags.gitops ? "error" : "warning");
471
+ if (uokFlags.gitops) {
472
+ await pauseAuto(ctx, pi);
473
+ return "dispatched";
474
+ }
475
+ }
476
+ // GitHub sync (non-blocking, opt-in)
477
+ await runSafely("postUnit", "github-sync", async () => {
478
+ const { runGitHubSync } = await import("../github-sync/sync.js");
479
+ await runGitHubSync(s.basePath, unit.type, unit.id);
480
+ });
481
+ return "continue";
482
+ }
363
483
  /**
364
484
  * Pre-verification processing: parallel worker signal check, cache invalidation,
365
485
  * auto-commit, doctor run, state rebuild, worktree sync, artifact verification.
@@ -370,7 +490,7 @@ export async function autoCommitUnit(basePath, unitType, unitId, ctx) {
370
490
  * - "retry" — artifact verification failed, s.pendingVerificationRetry set for loop re-iteration
371
491
  */
372
492
  export async function postUnitPreVerification(pctx, opts) {
373
- const { s, ctx, pi, buildSnapshotOpts, stopAuto, pauseAuto } = pctx;
493
+ const { s, ctx, pi, stopAuto, pauseAuto } = pctx;
374
494
  // ── Parallel worker signal check ──
375
495
  const milestoneLock = process.env.GSD_MILESTONE_LOCK;
376
496
  if (milestoneLock) {
@@ -392,125 +512,22 @@ export async function postUnitPreVerification(pctx, opts) {
392
512
  if (!opts?.skipSettleDelay) {
393
513
  await new Promise(r => setTimeout(r, 100));
394
514
  }
395
- const prefs = loadEffectiveGSDPreferences()?.preferences;
396
- const uokFlags = resolveUokFlags(prefs);
397
515
  // Turn-level git action (commit | snapshot | status-only)
398
516
  if (s.currentUnit) {
399
517
  const unit = s.currentUnit;
400
- const turnAction = uokFlags.gitops ? uokFlags.gitopsTurnAction : "commit";
401
- const traceId = s.currentTraceId ?? `turn:${unit.startedAt}`;
402
- const turnId = s.currentTurnId ?? `${unit.type}/${unit.id}/${unit.startedAt}`;
403
- s.lastGitActionFailure = null;
404
- s.lastGitActionStatus = null;
405
- try {
406
- let taskContext;
407
- if (turnAction === "commit" && s.currentUnit.type === "execute-task") {
408
- taskContext = await buildTaskCommitContextForUnit(s.basePath, s.currentUnit.id);
409
- }
410
- // Invalidate the nativeHasChanges cache before auto-commit (#1853).
411
- // The cache has a 10-second TTL and is keyed by basePath. A stale
412
- // `false` result causes autoCommit to skip staging entirely, leaving
413
- // code files only in the working tree where they are destroyed by
414
- // `git worktree remove --force` during teardown.
415
- _resetHasChangesCache();
416
- const skipLifecycleCommit = turnAction === "commit" && LIFECYCLE_ONLY_UNITS.has(s.currentUnit.type);
417
- if (skipLifecycleCommit) {
418
- debugLog("postUnit", {
419
- phase: "git-action-skipped",
420
- reason: "lifecycle-only-unit",
421
- unitType: s.currentUnit.type,
422
- unitId: s.currentUnit.id,
423
- });
424
- }
425
- else {
426
- const gitResult = runTurnGitAction({
427
- basePath: s.basePath,
428
- action: turnAction,
429
- unitType: s.currentUnit.type,
430
- unitId: s.currentUnit.id,
431
- taskContext,
432
- });
433
- if (uokFlags.gitops) {
434
- writeTurnGitTransaction({
435
- basePath: s.basePath,
436
- traceId,
437
- turnId,
438
- unitType: unit.type,
439
- unitId: unit.id,
440
- stage: "publish",
441
- action: turnAction,
442
- push: uokFlags.gitopsTurnPush,
443
- status: gitResult.status,
444
- error: gitResult.error,
445
- metadata: {
446
- dirty: gitResult.dirty,
447
- commitMessage: gitResult.commitMessage,
448
- snapshotLabel: gitResult.snapshotLabel,
449
- },
450
- });
451
- }
452
- if (gitResult.status === "failed") {
453
- s.lastGitActionFailure = gitResult.error ?? `git ${turnAction} failed`;
454
- s.lastGitActionStatus = "failed";
455
- if (uokFlags.gitops && uokFlags.gates) {
456
- const parsed = parseUnitId(unit.id);
457
- const gateRunner = new UokGateRunner();
458
- gateRunner.register({
459
- id: "closeout-git-action",
460
- type: "closeout",
461
- execute: async () => ({
462
- outcome: "fail",
463
- failureClass: "git",
464
- rationale: `turn git action "${turnAction}" failed`,
465
- findings: gitResult.error ?? "unknown git failure",
466
- }),
467
- });
468
- await gateRunner.run("closeout-git-action", {
469
- basePath: s.basePath,
470
- traceId,
471
- turnId,
472
- milestoneId: parsed.milestone ?? undefined,
473
- sliceId: parsed.slice ?? undefined,
474
- taskId: parsed.task ?? undefined,
475
- unitType: unit.type,
476
- unitId: unit.id,
477
- });
478
- }
479
- const failureMsg = `Git ${turnAction} failed: ${(gitResult.error ?? "unknown error").split("\n")[0]}`;
480
- ctx.ui.notify(failureMsg, "error");
481
- debugLog("postUnit", {
482
- phase: "git-action-failed-blocking",
483
- action: turnAction,
484
- error: gitResult.error ?? "unknown error",
485
- });
486
- await pauseAuto(ctx, pi);
487
- return "dispatched";
488
- }
489
- s.lastGitActionStatus = "ok";
490
- if (turnAction === "commit" && gitResult.commitMessage) {
491
- ctx.ui.notify(`Committed: ${gitResult.commitMessage.split("\n")[0]}`, "info");
492
- }
493
- else if (turnAction === "snapshot" && gitResult.snapshotLabel) {
494
- ctx.ui.notify(`Snapshot recorded: ${gitResult.snapshotLabel}`, "info");
495
- }
496
- }
518
+ if (shouldDeferCloseoutGitAction(unit.type)) {
519
+ debugLog("postUnit", {
520
+ phase: "git-action-deferred-until-verification",
521
+ unitType: unit.type,
522
+ unitId: unit.id,
523
+ });
497
524
  }
498
- catch (e) {
499
- const message = e instanceof Error ? e.message : String(e);
500
- s.lastGitActionFailure = message;
501
- s.lastGitActionStatus = "failed";
502
- debugLog("postUnit", { phase: "git-action", error: message, action: turnAction });
503
- ctx.ui.notify(`Git ${turnAction} failed: ${message.split("\n")[0]}`, uokFlags.gitops ? "error" : "warning");
504
- if (uokFlags.gitops) {
505
- await pauseAuto(ctx, pi);
525
+ else {
526
+ const gitActionResult = await runCloseoutGitAction(pctx, unit);
527
+ if (gitActionResult === "dispatched") {
506
528
  return "dispatched";
507
529
  }
508
530
  }
509
- // GitHub sync (non-blocking, opt-in)
510
- await runSafely("postUnit", "github-sync", async () => {
511
- const { runGitHubSync } = await import("../github-sync/sync.js");
512
- await runGitHubSync(s.basePath, unit.type, unit.id);
513
- });
514
531
  // Prune dead bg-shell processes
515
532
  await runSafely("postUnit", "prune-bg-shell", async () => {
516
533
  const { pruneDeadProcesses } = await import("../bg-shell/process-manager.js");
@@ -723,7 +740,7 @@ export async function postUnitPreVerification(pctx, opts) {
723
740
  const safetyConfig = resolveSafetyHarnessConfig(prefs?.safety_harness);
724
741
  if (safetyConfig.enabled) {
725
742
  const { milestone: sMid, slice: sSid, task: sTid } = parseUnitId(s.currentUnit.id);
726
- // File change validation (execute-task only, after auto-commit)
743
+ // File change validation (execute-task only, after unit execution)
727
744
  if (safetyConfig.file_change_validation && s.currentUnit.type === "execute-task" && sMid && sSid && sTid && isDbAvailable()) {
728
745
  try {
729
746
  const taskRow = getTask(sMid, sSid, sTid);
@@ -756,12 +773,34 @@ export async function postUnitPreVerification(pctx, opts) {
756
773
  const bashCalls = actual.filter(e => e.kind === "bash");
757
774
  if (sMid && sSid && sTid && isDbAvailable()) {
758
775
  const taskRow = getTask(sMid, sSid, sTid);
759
- const claimedCommands = getVerificationEvidence(sMid, sSid, sTid)
760
- .map((row) => row.command)
761
- .filter((command) => typeof command === "string" && command.trim().length > 0);
762
- if (taskRow?.status === "complete" && claimedCommands.length > 0 && bashCalls.length === 0) {
763
- logWarning("safety", "task claimed verification command evidence but no execution tool calls were recorded");
764
- ctx.ui.notify(`Safety: task ${sTid} claimed command evidence but no execution tool calls were recorded`, "warning");
776
+ if (taskRow?.status === "complete") {
777
+ const claimedEvidence = getVerificationEvidence(sMid, sSid, sTid)
778
+ .map((row) => ({
779
+ command: row.command,
780
+ exitCode: row.exit_code,
781
+ verdict: row.verdict,
782
+ }))
783
+ .filter((row) => typeof row.command === "string" && row.command.trim().length > 0);
784
+ const mismatches = crossReferenceEvidence(claimedEvidence, actual);
785
+ for (const mismatch of mismatches) {
786
+ const logMessage = `evidence-xref: ${mismatch.reason}`;
787
+ if (mismatch.severity === "error") {
788
+ logError("safety", logMessage);
789
+ }
790
+ else {
791
+ logWarning("safety", logMessage);
792
+ }
793
+ }
794
+ if (claimedEvidence.length > 0 && bashCalls.length === 0) {
795
+ logWarning("safety", "task claimed verification command evidence but no execution tool calls were recorded");
796
+ ctx.ui.notify(`Safety: task ${sTid} claimed command evidence but no execution tool calls were recorded`, "warning");
797
+ }
798
+ const blockingMismatch = mismatches.find((mismatch) => mismatch.severity === "error");
799
+ if (blockingMismatch) {
800
+ ctx.ui.notify(`Safety: task ${sTid} claimed passing verification that failed in recorded execution`, "error");
801
+ await pauseAuto(ctx, pi);
802
+ return "dispatched";
803
+ }
765
804
  }
766
805
  }
767
806
  }
@@ -992,6 +1031,12 @@ export async function postUnitPreVerification(pctx, opts) {
992
1031
  export async function postUnitPostVerification(pctx) {
993
1032
  const { s, ctx, pi, buildSnapshotOpts, lockBase, stopAuto, pauseAuto, updateProgressWidget } = pctx;
994
1033
  if (s.currentUnit) {
1034
+ if (shouldDeferCloseoutGitAction(s.currentUnit.type)) {
1035
+ const gitActionResult = await runCloseoutGitAction(pctx, s.currentUnit);
1036
+ if (gitActionResult === "dispatched") {
1037
+ return "stopped";
1038
+ }
1039
+ }
995
1040
  try {
996
1041
  const codebasePrefs = loadEffectiveGSDPreferences()?.preferences?.codebase;
997
1042
  const refresh = ensureCodebaseMapFresh(s.basePath, codebasePrefs
@@ -728,12 +728,20 @@ export async function inlineDecisionsFromDb(base, milestoneId, scope, level) {
728
728
  try {
729
729
  const { isDbAvailable } = await import("./gsd-db.js");
730
730
  if (isDbAvailable()) {
731
- const { queryDecisions, formatDecisionsForPrompt } = await import("./context-store.js");
731
+ // ADR-013 Phase 6 cutover (Stage 1): read decisions from the `memories`
732
+ // table. Both `queryDecisions` (legacy) and `queryDecisionsFromMemories`
733
+ // return identical Decision[] for active rows once Phase 5 dual-write is
734
+ // caught up. Switching the read here lets the destructive Phase 6 step
735
+ // (#5755) retire the legacy `decisions` table without changing prompt
736
+ // contents. Projection regen (`DECISIONS.md`) still sources from the
737
+ // legacy table — that switch lands separately to handle superseded
738
+ // history cleanly.
739
+ const { queryDecisionsFromMemories, formatDecisionsForPrompt } = await import("./context-store.js");
732
740
  // First query: try with both milestoneId and scope (if scope provided)
733
- let decisions = queryDecisions({ milestoneId, scope });
741
+ let decisions = queryDecisionsFromMemories({ milestoneId, scope });
734
742
  // Cascade: if empty AND scope was provided, retry without scope
735
743
  if (decisions.length === 0 && scope) {
736
- decisions = queryDecisions({ milestoneId });
744
+ decisions = queryDecisionsFromMemories({ milestoneId });
737
745
  }
738
746
  if (decisions.length > 0) {
739
747
  // Use compact format for non-full levels to save ~35% tokens
@@ -2769,7 +2777,7 @@ export async function buildParallelResearchSlicesPrompt(mid, midTitle, slices, b
2769
2777
  subagentSections.push([
2770
2778
  `### ${slice.id}: ${slice.title}`,
2771
2779
  "",
2772
- `Use this as the prompt for a \`subagent\` call${modelSuffix} (agent: \`gsd-executor\` or the default agent):`,
2780
+ `Use this as the prompt for a \`subagent\` call${modelSuffix} (agent: \`scout\`):`,
2773
2781
  "",
2774
2782
  "```",
2775
2783
  slicePrompt,
@@ -2838,7 +2846,7 @@ export async function buildGateEvaluatePrompt(mid, midTitle, sid, sTitle, base,
2838
2846
  subagentSections.push([
2839
2847
  `### ${def.id}: ${def.question}`,
2840
2848
  "",
2841
- `Use this as the prompt for a \`subagent\` call${modelSuffix}:`,
2849
+ `Use this as the prompt for a \`subagent\` call${modelSuffix} (agent: \`tester\`):`,
2842
2850
  "",
2843
2851
  "```",
2844
2852
  subPrompt,
@@ -18,9 +18,7 @@ import { join } from "node:path";
18
18
  import { resolveUokFlags } from "./uok/flags.js";
19
19
  import { UokGateRunner } from "./uok/gate-runner.js";
20
20
  import { verificationRetryKey } from "./auto/verification-retry-policy.js";
21
- function isInfraVerificationFailure(stderr) {
22
- return /\b(ENOENT|ENOTFOUND|ETIMEDOUT|ECONNRESET|EAI_AGAIN|spawn\s+\S+\s+ENOENT|command not found)\b/i.test(stderr);
23
- }
21
+ import { decideVerificationVerdict } from "./verification-verdict.js";
24
22
  /**
25
23
  * Post-unit guard for `validate-milestone` units (#4094).
26
24
  *
@@ -128,7 +126,7 @@ async function countIncompleteSlices(basePath, milestoneId) {
128
126
  /**
129
127
  * Run the verification gate for the current execute-task unit.
130
128
  * Returns:
131
- * - "continue" — gate passed (or no checks configured), proceed normally
129
+ * - "continue" — host-owned verification passed, proceed normally
132
130
  * - "retry" — gate failed with retries remaining, s.pendingVerificationRetry set for loop re-iteration
133
131
  * - "pause" — gate failed with retries exhausted, pauseAuto already called
134
132
  */
@@ -178,6 +176,10 @@ export async function runPostUnitVerification(vctx, pauseAuto) {
178
176
  process.stderr.write(` [${w.severity}] ${w.name}: ${w.title}\n`);
179
177
  }
180
178
  }
179
+ const verdict = decideVerificationVerdict(s.currentUnit.type, result);
180
+ if (!verdict.passed) {
181
+ result.passed = false;
182
+ }
181
183
  if (uokFlags.gates) {
182
184
  const gateRunner = new UokGateRunner();
183
185
  gateRunner.register({
@@ -190,10 +192,12 @@ export async function runPostUnitVerification(vctx, pauseAuto) {
190
192
  : "verification",
191
193
  rationale: result.passed
192
194
  ? "verification checks passed"
193
- : "verification checks failed",
195
+ : verdict.reason === "no-host-checks"
196
+ ? "no runnable host-owned verification checks discovered"
197
+ : "verification checks failed",
194
198
  findings: result.passed
195
199
  ? ""
196
- : formatFailureContext(result),
200
+ : verdict.failureContext || formatFailureContext(result),
197
201
  }),
198
202
  });
199
203
  await gateRunner.run("verification-gate", {
@@ -251,7 +255,11 @@ export async function runPostUnitVerification(vctx, pauseAuto) {
251
255
  }
252
256
  else {
253
257
  const nextAttempt = attempt + 1;
254
- writeVerificationJSON(result, tasksDir, tid, s.currentUnit.id, nextAttempt, maxRetries);
258
+ const includeRetryMetadata = !result.passed &&
259
+ verdict.retryable &&
260
+ autoFixEnabled &&
261
+ nextAttempt <= maxRetries;
262
+ writeVerificationJSON(result, tasksDir, tid, s.currentUnit.id, includeRetryMetadata ? nextAttempt : undefined, includeRetryMetadata ? maxRetries : undefined);
255
263
  }
256
264
  }
257
265
  }
@@ -259,18 +267,6 @@ export async function runPostUnitVerification(vctx, pauseAuto) {
259
267
  logWarning("engine", `verification-evidence write error: ${evidenceErr.message}`);
260
268
  }
261
269
  }
262
- const advisoryFailure = !result.passed &&
263
- (result.discoverySource === "package-json" ||
264
- result.checks.some((check) => isInfraVerificationFailure(check.stderr)));
265
- if (advisoryFailure) {
266
- s.verificationRetryCount.delete(retryKey);
267
- s.verificationRetryFailureHashes.delete(retryKey);
268
- s.pendingVerificationRetry = null;
269
- ctx.ui.notify(result.discoverySource === "package-json"
270
- ? "Verification failed in auto-discovered package.json checks — treating as advisory."
271
- : "Verification failed due to infrastructure/runtime environment issues — treating as advisory.", "warning");
272
- return "continue";
273
- }
274
270
  // ── Post-execution checks (run after main verification passes for execute-task units) ──
275
271
  let postExecChecks;
276
272
  let postExecBlockingFailure = false;
@@ -430,6 +426,15 @@ export async function runPostUnitVerification(vctx, pauseAuto) {
430
426
  s.pendingVerificationRetry = null;
431
427
  return "continue";
432
428
  }
429
+ else if (verdict.reason === "no-host-checks") {
430
+ s.verificationRetryCount.delete(retryKey);
431
+ s.verificationRetryFailureHashes.delete(retryKey);
432
+ s.pendingVerificationRetry = null;
433
+ ctx.ui.notify("Verification gate FAILED — no runnable host-owned verification checks were discovered. Pausing for human review.", "error");
434
+ process.stderr.write(`verification-gate: ${verdict.failureContext}\n`);
435
+ await pauseAuto(ctx, pi);
436
+ return "pause";
437
+ }
433
438
  else if (postExecBlockingFailure) {
434
439
  // Post-execution failures are cross-task consistency issues — retrying the same task won't fix them.
435
440
  // Skip retry and pause immediately for human review.
@@ -445,7 +450,7 @@ export async function runPostUnitVerification(vctx, pauseAuto) {
445
450
  s.verificationRetryCount.set(retryKey, nextAttempt);
446
451
  s.pendingVerificationRetry = {
447
452
  unitId: s.currentUnit.id,
448
- failureContext: formatFailureContext(result),
453
+ failureContext: verdict.failureContext || formatFailureContext(result),
449
454
  attempt: nextAttempt,
450
455
  };
451
456
  const failedCmds = result.checks
@@ -475,9 +480,10 @@ export async function runPostUnitVerification(vctx, pauseAuto) {
475
480
  }
476
481
  }
477
482
  catch (err) {
478
- // Gate errors are non-fatal
479
483
  logWarning("engine", `verification-gate error: ${err.message}`);
480
- return "continue";
484
+ ctx.ui.notify(`Verification gate errored before producing an authoritative verdict: ${err.message}`, "error");
485
+ await pauseAuto(ctx, pi);
486
+ return "pause";
481
487
  }
482
488
  }
483
489
  /**