@cleocode/core 2026.9.8 → 2026.9.10

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 (569) hide show
  1. package/dist/audit/reconstruct.d.ts +13 -43
  2. package/dist/audit/reconstruct.d.ts.map +1 -1
  3. package/dist/audit/reconstruct.js +295 -290
  4. package/dist/audit/reconstruct.js.map +1 -1
  5. package/dist/bootstrap.d.ts.map +1 -1
  6. package/dist/bootstrap.js +8 -2
  7. package/dist/bootstrap.js.map +1 -1
  8. package/dist/compliance/protocol-rules.d.ts.map +1 -1
  9. package/dist/compliance/protocol-rules.js +3 -1
  10. package/dist/compliance/protocol-rules.js.map +1 -1
  11. package/dist/dispatch/mutate-projection.d.ts.map +1 -1
  12. package/dist/dispatch/mutate-projection.js +12 -0
  13. package/dist/dispatch/mutate-projection.js.map +1 -1
  14. package/dist/dispatch/mvi-projection.d.ts +29 -6
  15. package/dist/dispatch/mvi-projection.d.ts.map +1 -1
  16. package/dist/dispatch/mvi-projection.js +109 -139
  17. package/dist/dispatch/mvi-projection.js.map +1 -1
  18. package/dist/docs/docs-read-model.d.ts +3 -2
  19. package/dist/docs/docs-read-model.d.ts.map +1 -1
  20. package/dist/docs/docs-read-model.js +4 -3
  21. package/dist/docs/docs-read-model.js.map +1 -1
  22. package/dist/docs/export-document.js +5129 -2297
  23. package/dist/docs/export-document.js.map +3 -3
  24. package/dist/docs/projection.d.ts +67 -0
  25. package/dist/docs/projection.d.ts.map +1 -0
  26. package/dist/docs/projection.js +370 -0
  27. package/dist/docs/projection.js.map +1 -0
  28. package/dist/doctor/acceptance-drift.d.ts +6 -2
  29. package/dist/doctor/acceptance-drift.d.ts.map +1 -1
  30. package/dist/doctor/acceptance-drift.js +43 -5
  31. package/dist/doctor/acceptance-drift.js.map +1 -1
  32. package/dist/doctor/knowledge-summary.d.ts +41 -0
  33. package/dist/doctor/knowledge-summary.d.ts.map +1 -0
  34. package/dist/doctor/knowledge-summary.js +81 -0
  35. package/dist/doctor/knowledge-summary.js.map +1 -0
  36. package/dist/doctor/knowledge.d.ts +192 -0
  37. package/dist/doctor/knowledge.d.ts.map +1 -0
  38. package/dist/doctor/knowledge.js +1864 -0
  39. package/dist/doctor/knowledge.js.map +1 -0
  40. package/dist/git/work-tree.d.ts +169 -0
  41. package/dist/git/work-tree.d.ts.map +1 -0
  42. package/dist/git/work-tree.js +341 -0
  43. package/dist/git/work-tree.js.map +1 -0
  44. package/dist/hooks/handlers/task-hooks.d.ts +8 -5
  45. package/dist/hooks/handlers/task-hooks.d.ts.map +1 -1
  46. package/dist/hooks/handlers/task-hooks.js +15 -60
  47. package/dist/hooks/handlers/task-hooks.js.map +1 -1
  48. package/dist/identity/cleo-identity.d.ts +13 -0
  49. package/dist/identity/cleo-identity.d.ts.map +1 -1
  50. package/dist/identity/cleo-identity.js +20 -6
  51. package/dist/identity/cleo-identity.js.map +1 -1
  52. package/dist/injection.d.ts.map +1 -1
  53. package/dist/injection.js +29 -13
  54. package/dist/injection.js.map +1 -1
  55. package/dist/internal.d.ts +3 -0
  56. package/dist/internal.d.ts.map +1 -1
  57. package/dist/internal.js +4 -0
  58. package/dist/internal.js.map +1 -1
  59. package/dist/lifecycle/verification/satisfies-validator.d.ts.map +1 -1
  60. package/dist/lifecycle/verification/satisfies-validator.js +124 -120
  61. package/dist/lifecycle/verification/satisfies-validator.js.map +1 -1
  62. package/dist/llm/plugin-facade.js +4047 -1726
  63. package/dist/llm/plugin-facade.js.map +3 -3
  64. package/dist/memory/brain-backfill.d.ts +27 -12
  65. package/dist/memory/brain-backfill.d.ts.map +1 -1
  66. package/dist/memory/brain-backfill.js +325 -134
  67. package/dist/memory/brain-backfill.js.map +1 -1
  68. package/dist/memory/brain-doctor.d.ts +21 -0
  69. package/dist/memory/brain-doctor.d.ts.map +1 -1
  70. package/dist/memory/brain-doctor.js +111 -53
  71. package/dist/memory/brain-doctor.js.map +1 -1
  72. package/dist/memory/brain-links.d.ts.map +1 -1
  73. package/dist/memory/brain-links.js +31 -4
  74. package/dist/memory/brain-links.js.map +1 -1
  75. package/dist/memory/brain-reasoning.d.ts.map +1 -1
  76. package/dist/memory/brain-reasoning.js +28 -2
  77. package/dist/memory/brain-reasoning.js.map +1 -1
  78. package/dist/memory/brain-reconciler.d.ts +14 -19
  79. package/dist/memory/brain-reconciler.d.ts.map +1 -1
  80. package/dist/memory/brain-reconciler.js +70 -97
  81. package/dist/memory/brain-reconciler.js.map +1 -1
  82. package/dist/memory/brain-search.d.ts +2 -0
  83. package/dist/memory/brain-search.d.ts.map +1 -1
  84. package/dist/memory/brain-search.js +59 -29
  85. package/dist/memory/brain-search.js.map +1 -1
  86. package/dist/memory/brain-similarity.d.ts.map +1 -1
  87. package/dist/memory/brain-similarity.js +5 -4
  88. package/dist/memory/brain-similarity.js.map +1 -1
  89. package/dist/memory/brain-stub-prune.d.ts +31 -9
  90. package/dist/memory/brain-stub-prune.d.ts.map +1 -1
  91. package/dist/memory/brain-stub-prune.js +65 -18
  92. package/dist/memory/brain-stub-prune.js.map +1 -1
  93. package/dist/memory/brain-writer-handlers.d.ts +10 -1
  94. package/dist/memory/brain-writer-handlers.d.ts.map +1 -1
  95. package/dist/memory/brain-writer-handlers.js +23 -3
  96. package/dist/memory/brain-writer-handlers.js.map +1 -1
  97. package/dist/memory/brain-writer-thread.d.ts +11 -1
  98. package/dist/memory/brain-writer-thread.d.ts.map +1 -1
  99. package/dist/memory/brain-writer-thread.js +87 -20
  100. package/dist/memory/brain-writer-thread.js.map +1 -1
  101. package/dist/memory/brain-writer-worker.js +8 -1
  102. package/dist/memory/brain-writer-worker.js.map +1 -1
  103. package/dist/memory/decision-cross-link.d.ts +39 -0
  104. package/dist/memory/decision-cross-link.d.ts.map +1 -1
  105. package/dist/memory/decision-cross-link.js +316 -0
  106. package/dist/memory/decision-cross-link.js.map +1 -1
  107. package/dist/memory/decisions.d.ts +4 -1
  108. package/dist/memory/decisions.d.ts.map +1 -1
  109. package/dist/memory/decisions.js +7 -24
  110. package/dist/memory/decisions.js.map +1 -1
  111. package/dist/memory/dispatch-trace.d.ts +12 -5
  112. package/dist/memory/dispatch-trace.d.ts.map +1 -1
  113. package/dist/memory/dispatch-trace.js +17 -7
  114. package/dist/memory/dispatch-trace.js.map +1 -1
  115. package/dist/memory/eligibility.d.ts +51 -0
  116. package/dist/memory/eligibility.d.ts.map +1 -0
  117. package/dist/memory/eligibility.js +77 -0
  118. package/dist/memory/eligibility.js.map +1 -0
  119. package/dist/memory/engine-compat.d.ts +2 -0
  120. package/dist/memory/engine-compat.d.ts.map +1 -1
  121. package/dist/memory/engine-compat.js +10 -0
  122. package/dist/memory/engine-compat.js.map +1 -1
  123. package/dist/memory/extraction-gate.d.ts +21 -7
  124. package/dist/memory/extraction-gate.d.ts.map +1 -1
  125. package/dist/memory/extraction-gate.js +178 -216
  126. package/dist/memory/extraction-gate.js.map +1 -1
  127. package/dist/memory/graph-auto-populate.d.ts +38 -5
  128. package/dist/memory/graph-auto-populate.d.ts.map +1 -1
  129. package/dist/memory/graph-auto-populate.js +182 -60
  130. package/dist/memory/graph-auto-populate.js.map +1 -1
  131. package/dist/memory/graph-memory-bridge.d.ts +14 -1
  132. package/dist/memory/graph-memory-bridge.d.ts.map +1 -1
  133. package/dist/memory/graph-memory-bridge.js +66 -28
  134. package/dist/memory/graph-memory-bridge.js.map +1 -1
  135. package/dist/memory/index.d.ts +1 -19
  136. package/dist/memory/index.d.ts.map +1 -1
  137. package/dist/memory/index.js +1 -50
  138. package/dist/memory/index.js.map +1 -1
  139. package/dist/memory/learnings.d.ts.map +1 -1
  140. package/dist/memory/learnings.js +2 -23
  141. package/dist/memory/learnings.js.map +1 -1
  142. package/dist/memory/manifest-filter.d.ts +21 -0
  143. package/dist/memory/manifest-filter.d.ts.map +1 -0
  144. package/dist/memory/manifest-filter.js +51 -0
  145. package/dist/memory/manifest-filter.js.map +1 -0
  146. package/dist/memory/manifest-ingestion.d.ts +28 -30
  147. package/dist/memory/manifest-ingestion.d.ts.map +1 -1
  148. package/dist/memory/manifest-ingestion.js +104 -235
  149. package/dist/memory/manifest-ingestion.js.map +1 -1
  150. package/dist/memory/observer-reflector.d.ts +2 -0
  151. package/dist/memory/observer-reflector.d.ts.map +1 -1
  152. package/dist/memory/observer-reflector.js +6 -49
  153. package/dist/memory/observer-reflector.js.map +1 -1
  154. package/dist/memory/patterns.d.ts +10 -0
  155. package/dist/memory/patterns.d.ts.map +1 -1
  156. package/dist/memory/patterns.js +25 -33
  157. package/dist/memory/patterns.js.map +1 -1
  158. package/dist/memory/pipeline-manifest-sqlite.d.ts +72 -9
  159. package/dist/memory/pipeline-manifest-sqlite.d.ts.map +1 -1
  160. package/dist/memory/pipeline-manifest-sqlite.js +508 -412
  161. package/dist/memory/pipeline-manifest-sqlite.js.map +1 -1
  162. package/dist/memory/retrieval/build-retrieval-bundle.d.ts.map +1 -1
  163. package/dist/memory/retrieval/build-retrieval-bundle.js +16 -15
  164. package/dist/memory/retrieval/build-retrieval-bundle.js.map +1 -1
  165. package/dist/memory/retrieval/fetch.d.ts +7 -0
  166. package/dist/memory/retrieval/fetch.d.ts.map +1 -1
  167. package/dist/memory/retrieval/fetch.js +14 -11
  168. package/dist/memory/retrieval/fetch.js.map +1 -1
  169. package/dist/memory/retrieval/observe.d.ts +7 -1
  170. package/dist/memory/retrieval/observe.d.ts.map +1 -1
  171. package/dist/memory/retrieval/observe.js +58 -4
  172. package/dist/memory/retrieval/observe.js.map +1 -1
  173. package/dist/memory/retrieval/search.d.ts +14 -1
  174. package/dist/memory/retrieval/search.d.ts.map +1 -1
  175. package/dist/memory/retrieval/search.js +133 -69
  176. package/dist/memory/retrieval/search.js.map +1 -1
  177. package/dist/memory/retrieval/telemetry.d.ts +31 -0
  178. package/dist/memory/retrieval/telemetry.d.ts.map +1 -0
  179. package/dist/memory/retrieval/telemetry.js +53 -0
  180. package/dist/memory/retrieval/telemetry.js.map +1 -0
  181. package/dist/nexus/analyze-orchestrator.d.ts +41 -9
  182. package/dist/nexus/analyze-orchestrator.d.ts.map +1 -1
  183. package/dist/nexus/analyze-orchestrator.js +257 -58
  184. package/dist/nexus/analyze-orchestrator.js.map +1 -1
  185. package/dist/nexus/api-contracts.d.ts +2 -6
  186. package/dist/nexus/api-contracts.d.ts.map +1 -1
  187. package/dist/nexus/api-contracts.js +11 -2
  188. package/dist/nexus/api-contracts.js.map +1 -1
  189. package/dist/nexus/context.d.ts.map +1 -1
  190. package/dist/nexus/context.js +14 -37
  191. package/dist/nexus/context.js.map +1 -1
  192. package/dist/nexus/diff.d.ts.map +1 -1
  193. package/dist/nexus/diff.js +2 -1
  194. package/dist/nexus/diff.js.map +1 -1
  195. package/dist/nexus/identity.d.ts +29 -3
  196. package/dist/nexus/identity.d.ts.map +1 -1
  197. package/dist/nexus/identity.js +63 -12
  198. package/dist/nexus/identity.js.map +1 -1
  199. package/dist/nexus/impact.d.ts +21 -19
  200. package/dist/nexus/impact.d.ts.map +1 -1
  201. package/dist/nexus/impact.js +96 -192
  202. package/dist/nexus/impact.js.map +1 -1
  203. package/dist/nexus/knowledge.d.ts +80 -0
  204. package/dist/nexus/knowledge.d.ts.map +1 -0
  205. package/dist/nexus/knowledge.js +786 -0
  206. package/dist/nexus/knowledge.js.map +1 -0
  207. package/dist/nexus/living-brain.d.ts +8 -5
  208. package/dist/nexus/living-brain.d.ts.map +1 -1
  209. package/dist/nexus/living-brain.js +252 -188
  210. package/dist/nexus/living-brain.js.map +1 -1
  211. package/dist/nexus/registry.d.ts +11 -2
  212. package/dist/nexus/registry.d.ts.map +1 -1
  213. package/dist/nexus/registry.js +144 -128
  214. package/dist/nexus/registry.js.map +1 -1
  215. package/dist/nexus/source-roots.d.ts +16 -0
  216. package/dist/nexus/source-roots.d.ts.map +1 -0
  217. package/dist/nexus/source-roots.js +174 -0
  218. package/dist/nexus/source-roots.js.map +1 -0
  219. package/dist/nexus/task-evidence.d.ts +22 -0
  220. package/dist/nexus/task-evidence.d.ts.map +1 -0
  221. package/dist/nexus/task-evidence.js +210 -0
  222. package/dist/nexus/task-evidence.js.map +1 -0
  223. package/dist/nexus/tasks-bridge.d.ts +14 -7
  224. package/dist/nexus/tasks-bridge.d.ts.map +1 -1
  225. package/dist/nexus/tasks-bridge.js +81 -40
  226. package/dist/nexus/tasks-bridge.js.map +1 -1
  227. package/dist/orchestrate/lifecycle-ops.d.ts.map +1 -1
  228. package/dist/orchestrate/lifecycle-ops.js +170 -120
  229. package/dist/orchestrate/lifecycle-ops.js.map +1 -1
  230. package/dist/orchestrate/query-ops.d.ts +6 -7
  231. package/dist/orchestrate/query-ops.d.ts.map +1 -1
  232. package/dist/orchestrate/query-ops.js +509 -532
  233. package/dist/orchestrate/query-ops.js.map +1 -1
  234. package/dist/orchestration/bootstrap.d.ts +15 -1
  235. package/dist/orchestration/bootstrap.d.ts.map +1 -1
  236. package/dist/orchestration/bootstrap.js +21 -10
  237. package/dist/orchestration/bootstrap.js.map +1 -1
  238. package/dist/orchestration/index.d.ts +35 -3
  239. package/dist/orchestration/index.d.ts.map +1 -1
  240. package/dist/orchestration/index.js +64 -35
  241. package/dist/orchestration/index.js.map +1 -1
  242. package/dist/orchestration/lead-rollup.d.ts +24 -1
  243. package/dist/orchestration/lead-rollup.d.ts.map +1 -1
  244. package/dist/orchestration/lead-rollup.js +31 -11
  245. package/dist/orchestration/lead-rollup.js.map +1 -1
  246. package/dist/orchestration/spawn-prompt.d.ts.map +1 -1
  247. package/dist/orchestration/spawn-prompt.js +25 -45
  248. package/dist/orchestration/spawn-prompt.js.map +1 -1
  249. package/dist/orchestration/unblock.d.ts +15 -1
  250. package/dist/orchestration/unblock.d.ts.map +1 -1
  251. package/dist/orchestration/unblock.js +23 -12
  252. package/dist/orchestration/unblock.js.map +1 -1
  253. package/dist/orchestration/validate-spawn.d.ts.map +1 -1
  254. package/dist/orchestration/validate-spawn.js +3 -3
  255. package/dist/orchestration/validate-spawn.js.map +1 -1
  256. package/dist/orchestration/waves.d.ts +29 -13
  257. package/dist/orchestration/waves.d.ts.map +1 -1
  258. package/dist/orchestration/waves.js +43 -55
  259. package/dist/orchestration/waves.js.map +1 -1
  260. package/dist/paths.d.ts +17 -163
  261. package/dist/paths.d.ts.map +1 -1
  262. package/dist/paths.js +145 -496
  263. package/dist/paths.js.map +1 -1
  264. package/dist/project-info.d.ts +2 -11
  265. package/dist/project-info.d.ts.map +1 -1
  266. package/dist/project-info.js +3 -31
  267. package/dist/project-info.js.map +1 -1
  268. package/dist/project-scope.d.ts +239 -0
  269. package/dist/project-scope.d.ts.map +1 -0
  270. package/dist/project-scope.js +473 -0
  271. package/dist/project-scope.js.map +1 -0
  272. package/dist/release/pr-evidence.d.ts +23 -4
  273. package/dist/release/pr-evidence.d.ts.map +1 -1
  274. package/dist/release/pr-evidence.js +65 -9
  275. package/dist/release/pr-evidence.js.map +1 -1
  276. package/dist/render/nexus/graph/index.d.ts.map +1 -1
  277. package/dist/render/nexus/graph/index.js +4 -2
  278. package/dist/render/nexus/graph/index.js.map +1 -1
  279. package/dist/render/orchestration/audit-reconstruct.d.ts +11 -0
  280. package/dist/render/orchestration/audit-reconstruct.d.ts.map +1 -1
  281. package/dist/render/orchestration/audit-reconstruct.js +21 -6
  282. package/dist/render/orchestration/audit-reconstruct.js.map +1 -1
  283. package/dist/render/tasks/show.js +2 -1
  284. package/dist/render/tasks/show.js.map +1 -1
  285. package/dist/resources/spawn-wrapper.d.ts +82 -14
  286. package/dist/resources/spawn-wrapper.d.ts.map +1 -1
  287. package/dist/resources/spawn-wrapper.js +585 -27
  288. package/dist/resources/spawn-wrapper.js.map +1 -1
  289. package/dist/selfimprove/fix-gen.js +4055 -1734
  290. package/dist/selfimprove/fix-gen.js.map +3 -3
  291. package/dist/sequence/index.d.ts.map +1 -1
  292. package/dist/sequence/index.js +76 -68
  293. package/dist/sequence/index.js.map +1 -1
  294. package/dist/session/canon-lint.d.ts +24 -3
  295. package/dist/session/canon-lint.d.ts.map +1 -1
  296. package/dist/session/canon-lint.js +72 -31
  297. package/dist/session/canon-lint.js.map +1 -1
  298. package/dist/session/engine-ops.d.ts +1 -4
  299. package/dist/session/engine-ops.d.ts.map +1 -1
  300. package/dist/session/engine-ops.js +42 -6
  301. package/dist/session/engine-ops.js.map +1 -1
  302. package/dist/sessions/briefing.d.ts +11 -1
  303. package/dist/sessions/briefing.d.ts.map +1 -1
  304. package/dist/sessions/briefing.js +76 -8
  305. package/dist/sessions/briefing.js.map +1 -1
  306. package/dist/sessions/handoff.d.ts +3 -1
  307. package/dist/sessions/handoff.d.ts.map +1 -1
  308. package/dist/sessions/handoff.js +2 -0
  309. package/dist/sessions/handoff.js.map +1 -1
  310. package/dist/shutdown-deadline.d.ts +24 -33
  311. package/dist/shutdown-deadline.d.ts.map +1 -1
  312. package/dist/shutdown-deadline.js +71 -14
  313. package/dist/shutdown-deadline.js.map +1 -1
  314. package/dist/shutdown.d.ts +21 -10
  315. package/dist/shutdown.d.ts.map +1 -1
  316. package/dist/shutdown.js +89 -42
  317. package/dist/shutdown.js.map +1 -1
  318. package/dist/skills/index.d.ts +1 -1
  319. package/dist/skills/index.d.ts.map +1 -1
  320. package/dist/skills/index.js +1 -1
  321. package/dist/skills/index.js.map +1 -1
  322. package/dist/skills/orchestrator/validator.d.ts +87 -20
  323. package/dist/skills/orchestrator/validator.d.ts.map +1 -1
  324. package/dist/skills/orchestrator/validator.js +330 -241
  325. package/dist/skills/orchestrator/validator.js.map +1 -1
  326. package/dist/store/agent-resolver.d.ts.map +1 -1
  327. package/dist/store/agent-resolver.js +16 -9
  328. package/dist/store/agent-resolver.js.map +1 -1
  329. package/dist/store/attachment-store.d.ts +4 -1
  330. package/dist/store/attachment-store.d.ts.map +1 -1
  331. package/dist/store/attachment-store.js +20 -3
  332. package/dist/store/attachment-store.js.map +1 -1
  333. package/dist/store/background-jobs.d.ts +426 -105
  334. package/dist/store/background-jobs.d.ts.map +1 -1
  335. package/dist/store/background-jobs.js +1208 -252
  336. package/dist/store/background-jobs.js.map +1 -1
  337. package/dist/store/background-ops.d.ts +145 -8
  338. package/dist/store/background-ops.d.ts.map +1 -1
  339. package/dist/store/background-ops.js +459 -15
  340. package/dist/store/background-ops.js.map +1 -1
  341. package/dist/store/backup-inspect.d.ts +48 -4
  342. package/dist/store/backup-inspect.d.ts.map +1 -1
  343. package/dist/store/backup-inspect.js +384 -4
  344. package/dist/store/backup-inspect.js.map +1 -1
  345. package/dist/store/converters.d.ts.map +1 -1
  346. package/dist/store/converters.js +19 -2
  347. package/dist/store/converters.js.map +1 -1
  348. package/dist/store/data-accessor.d.ts +9 -1
  349. package/dist/store/data-accessor.d.ts.map +1 -1
  350. package/dist/store/data-accessor.js +22 -5
  351. package/dist/store/data-accessor.js.map +1 -1
  352. package/dist/store/data-safety-central.d.ts.map +1 -1
  353. package/dist/store/data-safety-central.js +272 -232
  354. package/dist/store/data-safety-central.js.map +1 -1
  355. package/dist/store/db-helpers.d.ts +19 -3
  356. package/dist/store/db-helpers.d.ts.map +1 -1
  357. package/dist/store/db-helpers.js +42 -58
  358. package/dist/store/db-helpers.js.map +1 -1
  359. package/dist/store/dual-scope-db.d.ts +63 -4
  360. package/dist/store/dual-scope-db.d.ts.map +1 -1
  361. package/dist/store/dual-scope-db.js +166 -19
  362. package/dist/store/dual-scope-db.js.map +1 -1
  363. package/dist/store/exodus/archive.d.ts +21 -7
  364. package/dist/store/exodus/archive.d.ts.map +1 -1
  365. package/dist/store/exodus/archive.js +60 -18
  366. package/dist/store/exodus/archive.js.map +1 -1
  367. package/dist/store/exodus/migrate.d.ts.map +1 -1
  368. package/dist/store/exodus/migrate.js +27 -23
  369. package/dist/store/exodus/migrate.js.map +1 -1
  370. package/dist/store/exodus/on-open.d.ts +16 -27
  371. package/dist/store/exodus/on-open.d.ts.map +1 -1
  372. package/dist/store/exodus/on-open.js +121 -169
  373. package/dist/store/exodus/on-open.js.map +1 -1
  374. package/dist/store/exodus/recovery.d.ts +90 -0
  375. package/dist/store/exodus/recovery.d.ts.map +1 -0
  376. package/dist/store/exodus/recovery.js +434 -0
  377. package/dist/store/exodus/recovery.js.map +1 -0
  378. package/dist/store/exodus/types.d.ts +15 -0
  379. package/dist/store/exodus/types.d.ts.map +1 -1
  380. package/dist/store/memory-accessor.d.ts +52 -3
  381. package/dist/store/memory-accessor.d.ts.map +1 -1
  382. package/dist/store/memory-accessor.js +122 -7
  383. package/dist/store/memory-accessor.js.map +1 -1
  384. package/dist/store/nexus-sqlite.d.ts +24 -6
  385. package/dist/store/nexus-sqlite.d.ts.map +1 -1
  386. package/dist/store/nexus-sqlite.js +39 -20
  387. package/dist/store/nexus-sqlite.js.map +1 -1
  388. package/dist/store/ports/domain-binding.d.ts.map +1 -1
  389. package/dist/store/ports/domain-binding.js +27 -5
  390. package/dist/store/ports/domain-binding.js.map +1 -1
  391. package/dist/store/schema/background-jobs.d.ts +181 -2
  392. package/dist/store/schema/background-jobs.d.ts.map +1 -1
  393. package/dist/store/schema/background-jobs.js +27 -3
  394. package/dist/store/schema/background-jobs.js.map +1 -1
  395. package/dist/store/schema/cleo-global/skills.d.ts +2 -2
  396. package/dist/store/schema/cleo-project/lifecycle.d.ts +2 -2
  397. package/dist/store/schema/cleo-project/runtime.d.ts +1 -1
  398. package/dist/store/schema/cleo-project/tasks-core.d.ts +1 -1
  399. package/dist/store/schema/lifecycle.d.ts +2 -2
  400. package/dist/store/schema/skills-schema.d.ts +2 -2
  401. package/dist/store/schema/tasks.d.ts +1 -1
  402. package/dist/store/session-store.d.ts +3 -1
  403. package/dist/store/session-store.d.ts.map +1 -1
  404. package/dist/store/session-store.js +252 -207
  405. package/dist/store/session-store.js.map +1 -1
  406. package/dist/store/sqlite-data-accessor.d.ts +39 -4
  407. package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
  408. package/dist/store/sqlite-data-accessor.js +689 -527
  409. package/dist/store/sqlite-data-accessor.js.map +1 -1
  410. package/dist/store/tasks-sqlite.d.ts +3 -1
  411. package/dist/store/tasks-sqlite.d.ts.map +1 -1
  412. package/dist/store/tasks-sqlite.js +309 -279
  413. package/dist/store/tasks-sqlite.js.map +1 -1
  414. package/dist/store/validation-schemas.d.ts +8 -8
  415. package/dist/store/writer-lease.d.ts +80 -8
  416. package/dist/store/writer-lease.d.ts.map +1 -1
  417. package/dist/store/writer-lease.js +503 -134
  418. package/dist/store/writer-lease.js.map +1 -1
  419. package/dist/system/audit.d.ts +2 -1
  420. package/dist/system/audit.d.ts.map +1 -1
  421. package/dist/system/audit.js +118 -71
  422. package/dist/system/audit.js.map +1 -1
  423. package/dist/system/backup.d.ts.map +1 -1
  424. package/dist/system/backup.js +5 -5
  425. package/dist/system/backup.js.map +1 -1
  426. package/dist/system/inject-generate.js +1 -1
  427. package/dist/task-work/index.d.ts +3 -0
  428. package/dist/task-work/index.d.ts.map +1 -1
  429. package/dist/task-work/index.js +34 -37
  430. package/dist/task-work/index.js.map +1 -1
  431. package/dist/tasks/ac-coverage-gate.d.ts.map +1 -1
  432. package/dist/tasks/ac-coverage-gate.js +4 -0
  433. package/dist/tasks/ac-coverage-gate.js.map +1 -1
  434. package/dist/tasks/ac-immutability.d.ts +7 -4
  435. package/dist/tasks/ac-immutability.d.ts.map +1 -1
  436. package/dist/tasks/ac-immutability.js +19 -17
  437. package/dist/tasks/ac-immutability.js.map +1 -1
  438. package/dist/tasks/ac-table.d.ts +5 -5
  439. package/dist/tasks/ac-table.d.ts.map +1 -1
  440. package/dist/tasks/ac-table.js +26 -12
  441. package/dist/tasks/ac-table.js.map +1 -1
  442. package/dist/tasks/acceptance-input.d.ts +52 -0
  443. package/dist/tasks/acceptance-input.d.ts.map +1 -0
  444. package/dist/tasks/acceptance-input.js +259 -0
  445. package/dist/tasks/acceptance-input.js.map +1 -0
  446. package/dist/tasks/add.d.ts +67 -29
  447. package/dist/tasks/add.d.ts.map +1 -1
  448. package/dist/tasks/add.js +255 -97
  449. package/dist/tasks/add.js.map +1 -1
  450. package/dist/tasks/complete.d.ts +16 -1
  451. package/dist/tasks/complete.d.ts.map +1 -1
  452. package/dist/tasks/complete.js +152 -55
  453. package/dist/tasks/complete.js.map +1 -1
  454. package/dist/tasks/decompose.d.ts +103 -0
  455. package/dist/tasks/decompose.d.ts.map +1 -0
  456. package/dist/tasks/decompose.js +224 -0
  457. package/dist/tasks/decompose.js.map +1 -0
  458. package/dist/tasks/delete.d.ts +5 -8
  459. package/dist/tasks/delete.d.ts.map +1 -1
  460. package/dist/tasks/delete.js +4 -3
  461. package/dist/tasks/delete.js.map +1 -1
  462. package/dist/tasks/dependency-check.d.ts +59 -0
  463. package/dist/tasks/dependency-check.d.ts.map +1 -1
  464. package/dist/tasks/dependency-check.js +73 -0
  465. package/dist/tasks/dependency-check.js.map +1 -1
  466. package/dist/tasks/deps-ready.d.ts +13 -1
  467. package/dist/tasks/deps-ready.d.ts.map +1 -1
  468. package/dist/tasks/deps-ready.js +16 -5
  469. package/dist/tasks/deps-ready.js.map +1 -1
  470. package/dist/tasks/engine-converters.d.ts +7 -0
  471. package/dist/tasks/engine-converters.d.ts.map +1 -1
  472. package/dist/tasks/engine-converters.js +8 -1
  473. package/dist/tasks/engine-converters.js.map +1 -1
  474. package/dist/tasks/evidence.d.ts +88 -27
  475. package/dist/tasks/evidence.d.ts.map +1 -1
  476. package/dist/tasks/evidence.js +344 -43
  477. package/dist/tasks/evidence.js.map +1 -1
  478. package/dist/tasks/find.d.ts +16 -12
  479. package/dist/tasks/find.d.ts.map +1 -1
  480. package/dist/tasks/find.js +107 -93
  481. package/dist/tasks/find.js.map +1 -1
  482. package/dist/tasks/gate-preview.d.ts +73 -0
  483. package/dist/tasks/gate-preview.d.ts.map +1 -0
  484. package/dist/tasks/gate-preview.js +119 -0
  485. package/dist/tasks/gate-preview.js.map +1 -0
  486. package/dist/tasks/gate-runner.d.ts +88 -14
  487. package/dist/tasks/gate-runner.d.ts.map +1 -1
  488. package/dist/tasks/gate-runner.js +719 -250
  489. package/dist/tasks/gate-runner.js.map +1 -1
  490. package/dist/tasks/hierarchy-policy.d.ts +8 -0
  491. package/dist/tasks/hierarchy-policy.d.ts.map +1 -1
  492. package/dist/tasks/hierarchy-policy.js +9 -2
  493. package/dist/tasks/hierarchy-policy.js.map +1 -1
  494. package/dist/tasks/hierarchy.d.ts +67 -1
  495. package/dist/tasks/hierarchy.d.ts.map +1 -1
  496. package/dist/tasks/hierarchy.js +81 -1
  497. package/dist/tasks/hierarchy.js.map +1 -1
  498. package/dist/tasks/index.d.ts +3 -0
  499. package/dist/tasks/index.d.ts.map +1 -1
  500. package/dist/tasks/index.js +3 -0
  501. package/dist/tasks/index.js.map +1 -1
  502. package/dist/tasks/infer-add-params.d.ts +1 -20
  503. package/dist/tasks/infer-add-params.d.ts.map +1 -1
  504. package/dist/tasks/infer-add-params.js +2 -203
  505. package/dist/tasks/infer-add-params.js.map +1 -1
  506. package/dist/tasks/list.d.ts +7 -2
  507. package/dist/tasks/list.d.ts.map +1 -1
  508. package/dist/tasks/list.js +24 -36
  509. package/dist/tasks/list.js.map +1 -1
  510. package/dist/tasks/ops.d.ts +6 -61
  511. package/dist/tasks/ops.d.ts.map +1 -1
  512. package/dist/tasks/ops.js +5 -53
  513. package/dist/tasks/ops.js.map +1 -1
  514. package/dist/tasks/plan.d.ts +15 -1
  515. package/dist/tasks/plan.d.ts.map +1 -1
  516. package/dist/tasks/plan.js +23 -25
  517. package/dist/tasks/plan.js.map +1 -1
  518. package/dist/tasks/population.d.ts +46 -0
  519. package/dist/tasks/population.d.ts.map +1 -0
  520. package/dist/tasks/population.js +88 -0
  521. package/dist/tasks/population.js.map +1 -0
  522. package/dist/tasks/reconcile-scope.d.ts +157 -0
  523. package/dist/tasks/reconcile-scope.d.ts.map +1 -0
  524. package/dist/tasks/reconcile-scope.js +242 -0
  525. package/dist/tasks/reconcile-scope.js.map +1 -0
  526. package/dist/tasks/req.d.ts +51 -12
  527. package/dist/tasks/req.d.ts.map +1 -1
  528. package/dist/tasks/req.js +125 -69
  529. package/dist/tasks/req.js.map +1 -1
  530. package/dist/tasks/session-scope.d.ts +6 -31
  531. package/dist/tasks/session-scope.d.ts.map +1 -1
  532. package/dist/tasks/session-scope.js +3 -17
  533. package/dist/tasks/session-scope.js.map +1 -1
  534. package/dist/tasks/severity-attestation.d.ts +39 -10
  535. package/dist/tasks/severity-attestation.d.ts.map +1 -1
  536. package/dist/tasks/severity-attestation.js +75 -36
  537. package/dist/tasks/severity-attestation.js.map +1 -1
  538. package/dist/tasks/task-next.d.ts +5 -0
  539. package/dist/tasks/task-next.d.ts.map +1 -1
  540. package/dist/tasks/task-next.js +10 -9
  541. package/dist/tasks/task-next.js.map +1 -1
  542. package/dist/tasks/task-reparent.d.ts.map +1 -1
  543. package/dist/tasks/task-reparent.js +3 -13
  544. package/dist/tasks/task-reparent.js.map +1 -1
  545. package/dist/tasks/update.d.ts +20 -43
  546. package/dist/tasks/update.d.ts.map +1 -1
  547. package/dist/tasks/update.js +64 -49
  548. package/dist/tasks/update.js.map +1 -1
  549. package/dist/validation/engine-ops.d.ts +17 -1
  550. package/dist/validation/engine-ops.d.ts.map +1 -1
  551. package/dist/validation/engine-ops.js +149 -11
  552. package/dist/validation/engine-ops.js.map +1 -1
  553. package/dist/workgraph/reparent-preflight.d.ts +1 -1
  554. package/dist/workgraph/reparent-preflight.d.ts.map +1 -1
  555. package/dist/workgraph/reparent-preflight.js +6 -1
  556. package/dist/workgraph/reparent-preflight.js.map +1 -1
  557. package/dist/workgraph/scaffold-apply.d.ts.map +1 -1
  558. package/dist/workgraph/scaffold-apply.js +81 -76
  559. package/dist/workgraph/scaffold-apply.js.map +1 -1
  560. package/dist/workgraph/structure-validation.d.ts +1 -1
  561. package/dist/workgraph/structure-validation.d.ts.map +1 -1
  562. package/dist/workgraph/structure-validation.js +6 -1
  563. package/dist/workgraph/structure-validation.js.map +1 -1
  564. package/migrations/drizzle-tasks/20260919180000_t12263-job-ownership-fences/migration.sql +34 -0
  565. package/migrations/drizzle-tasks/20260919184500_t12265-durable-pending-proposals/migration.sql +4 -0
  566. package/package.json +18 -13
  567. package/schemas/config.schema.json +2 -2
  568. package/schemas/project-context.schema.json +12 -0
  569. package/templates/CLEO-INJECTION.md +114 -101
@@ -26,6 +26,7 @@ import { readFile, stat } from 'node:fs/promises';
26
26
  import { dirname, isAbsolute, join, resolve as resolvePath } from 'node:path';
27
27
  import { EvidenceParseError, ExitCode, GATE_EVIDENCE_REQUIREMENTS, parseEvidenceString, validateEvidenceForGate, } from '@cleocode/contracts';
28
28
  import { CleoError } from '../errors.js';
29
+ import { describeUnusableEvidenceGitRoot, E_EVIDENCE_GIT_ROOT, findNestedGitWorkTrees, findWorkTreeContainingCommit, gitToplevel, isGitWorkTree, resolveDeclaredEvidenceGitRoot, } from '../git/work-tree.js';
29
30
  import { pushWarning } from '../output.js';
30
31
  import { getEffectiveHead } from '../worktree/effective-head.js';
31
32
  import { DISABLE_ENV, describeMemoryLimit } from './heavy-tool-limit.js';
@@ -132,26 +133,69 @@ export function parseEvidence(raw) {
132
133
  throw err;
133
134
  }
134
135
  }
136
+ /**
137
+ * Validate a single parsed atom against the filesystem / git / tools.
138
+ *
139
+ * @param parsed - Parsed atom from {@link parseEvidence}
140
+ * @param projectRoot - Absolute path to project root (for resolving files, git)
141
+ * @param taskId - Optional CLEO task ID. When provided, enables branch-scope
142
+ * check (T9178), content-intersect check (T9245), worktree-aware HEAD
143
+ * resolution (T-WT-3), and git-show file fallback for branch-only files
144
+ * (T11959).
145
+ * @param siblingCommitSha - Optional sha of a sibling `commit:` atom from the
146
+ * same evidence string. When provided, `files:` paths are resolved against
147
+ * that commit's tree FIRST (T12107 / gh#1195) before falling back to the
148
+ * worktree and git refs — covers a merged commit whose files are not yet
149
+ * present in the local checkout.
150
+ * @param context - Current task, gate and acceptance criteria used to validate scoped proof.
151
+ * @returns Validation outcome with canonicalised form on success
152
+ * @example
153
+ * ```ts
154
+ * const result = await validateAtom(parsedAtom, projectRoot, task.id, undefined, context);
155
+ * ```
156
+ *
157
+ * @task T832
158
+ * @task T11959
159
+ * @task T12107
160
+ * @adr ADR-051 §3
161
+ */
135
162
  export async function validateAtom(parsed, projectRoot,
136
163
  /** T9178: When provided, validates commit is on task/<taskId> branch. */
137
164
  taskId,
138
165
  /** T12107: sha of a sibling `commit:` atom in the same evidence string. */
139
- siblingCommitSha) {
166
+ siblingCommitSha, context) {
167
+ if (parsed.kind === 'pr' && (!Number.isInteger(parsed.prNumber) || parsed.prNumber <= 0)) {
168
+ return {
169
+ ok: false,
170
+ codeName: 'E_EVIDENCE_INVALID',
171
+ reason: 'PR number must be a positive integer.',
172
+ };
173
+ }
140
174
  // gh#1365: resolve the tree under test ONCE, here, and pass it down as a
141
175
  // required parameter. Resolving inside each validator would centralise the
142
176
  // logic but still let two of them resolve independently and drift — which is
143
177
  // exactly how gh#1419 happened (a revalidation key derived from one root
144
178
  // while the validation answered about another). A required parameter makes
145
179
  // that divergence unrepresentable rather than merely discouraged.
180
+ // T12308: hand the resolver the SHA this evidence is about so a CLEO root
181
+ // that parents several repositories resolves itself. `files:` atoms carry no
182
+ // SHA of their own, so the sibling `commit:`/`pr:` anchor supplies it — the
183
+ // same anchor `validateFiles` already uses to read bytes at that commit.
184
+ const executionRootHints = {
185
+ ...(parsed.kind === 'commit' ? { commitSha: parsed.sha } : {}),
186
+ ...(parsed.kind === 'files' && (context?.artifactCommitSha ?? siblingCommitSha) !== undefined
187
+ ? { commitSha: (context?.artifactCommitSha ?? siblingCommitSha) }
188
+ : {}),
189
+ };
146
190
  const roots = {
147
191
  storeRoot: projectRoot,
148
- executionRoot: resolveEvidenceExecutionRoot(projectRoot),
192
+ executionRoot: resolveEvidenceExecutionRoot(projectRoot, undefined, executionRootHints),
149
193
  };
150
194
  switch (parsed.kind) {
151
195
  case 'commit':
152
196
  return validateCommit(parsed.sha, roots, taskId);
153
197
  case 'files':
154
- return validateFiles(parsed.paths, roots, taskId, siblingCommitSha);
198
+ return validateFiles(parsed.paths, roots, taskId, context?.artifactCommitSha ?? siblingCommitSha, Boolean(context?.artifactCommitSha));
155
199
  case 'test-run':
156
200
  return validateTestRun(parsed.path, roots);
157
201
  case 'tool':
@@ -173,7 +217,7 @@ siblingCommitSha) {
173
217
  // db, which is CLEO's own record, not the repository's.
174
218
  return validateDecision(parsed.decisionId, roots);
175
219
  case 'pr':
176
- return validatePrAtom(parsed.prNumber, roots);
220
+ return validatePrAtom(parsed.prNumber, roots, context);
177
221
  case 'satisfies': {
178
222
  // ADR-079-r2: 5-check validator pipeline shipped by T10507.
179
223
  // Delegates to the dedicated validator module to keep the dispatch
@@ -309,15 +353,17 @@ function resolveIntegrationBranches(env, projectContext) {
309
353
  *
310
354
  * Checking the remote-tracking ref preserves verification after a merged local
311
355
  * integration branch has been cleaned up.
356
+ *
357
+ * @param executionRoot - The repo the evidence is about, never the store root.
312
358
  */
313
- async function findReachableIntegrationBranch(sha, branches, projectRoot) {
359
+ async function findReachableIntegrationBranch(sha, branches, executionRoot) {
314
360
  for (const branch of branches) {
315
361
  const refs = [`refs/heads/${branch}`, `refs/remotes/origin/${branch}`];
316
362
  for (const ref of refs) {
317
- const exists = await runCommand('git', ['rev-parse', '--verify', ref], projectRoot);
363
+ const exists = await runCommand('git', ['rev-parse', '--verify', ref], executionRoot);
318
364
  if (exists.exitCode !== 0)
319
365
  continue;
320
- const reachable = await runCommand('git', ['merge-base', '--is-ancestor', sha, ref], projectRoot);
366
+ const reachable = await runCommand('git', ['merge-base', '--is-ancestor', sha, ref], executionRoot);
321
367
  if (reachable.exitCode === 0)
322
368
  return branch;
323
369
  }
@@ -333,6 +379,17 @@ async function validateCommit(sha, roots, taskId) {
333
379
  codeName: 'E_EVIDENCE_INVALID',
334
380
  };
335
381
  }
382
+ // gh#1462: without a work tree every git call below fails, and the first one
383
+ // (`cat-file`) reported it as "Commit not found in repository" — which says
384
+ // the commit is absent, not that git never ran in a repository at all. Same
385
+ // distinction `pr:` needs, so it fails the same way.
386
+ if (!isGitWorkTree(executionRoot)) {
387
+ return {
388
+ ok: false,
389
+ reason: describeUnusableEvidenceGitRoot(projectRoot, executionRoot),
390
+ codeName: E_EVIDENCE_GIT_ROOT,
391
+ };
392
+ }
336
393
  const exists = await runCommand('git', ['cat-file', '-e', `${sha}^{commit}`], executionRoot);
337
394
  if (exists.exitCode !== 0) {
338
395
  return {
@@ -352,7 +409,7 @@ async function validateCommit(sha, roots, taskId) {
352
409
  const { loadProjectContext } = await import('../agents/variable-substitution.js');
353
410
  const projectCtx = loadProjectContext(projectRoot);
354
411
  const integrationBranches = resolveIntegrationBranches(process.env, projectCtx.loaded ? projectCtx.context : null);
355
- const reachable = await runCommand('git', ['merge-base', '--is-ancestor', sha, effectiveHead], projectRoot);
412
+ const reachable = await runCommand('git', ['merge-base', '--is-ancestor', sha, effectiveHead], executionRoot);
356
413
  if (reachable.exitCode !== 0) {
357
414
  // DHQ-083 companion (a): when the commit is not reachable from the task
358
415
  // branch HEAD, also accept commits reachable from the configured
@@ -398,7 +455,7 @@ async function validateCommit(sha, roots, taskId) {
398
455
  const branchRef = `task/${taskId}`;
399
456
  const branchExists = await runCommand('git', ['rev-parse', '--verify', branchRef], executionRoot);
400
457
  if (branchExists.exitCode === 0) {
401
- const onBranch = await runCommand('git', ['merge-base', '--is-ancestor', sha, branchRef], projectRoot);
458
+ const onBranch = await runCommand('git', ['merge-base', '--is-ancestor', sha, branchRef], executionRoot);
402
459
  if (onBranch.exitCode !== 0) {
403
460
  // T11959 / DHQ-083 companion (a): also accept commits reachable from
404
461
  // the configured integration branches. This covers:
@@ -406,7 +463,7 @@ async function validateCommit(sha, roots, taskId) {
406
463
  // - post-merge task branches that have been cleaned up (the SHA is now
407
464
  // only reachable from main, not from `task/<id>` which no longer exists)
408
465
  // gh#1116 / T12028: reuses the integrationBranches resolved above.
409
- const reachableIntegrationBranch = await findReachableIntegrationBranch(sha, integrationBranches, projectRoot);
466
+ const reachableIntegrationBranch = await findReachableIntegrationBranch(sha, integrationBranches, executionRoot);
410
467
  if (reachableIntegrationBranch === null) {
411
468
  const branchList = integrationBranches.join(', ');
412
469
  return {
@@ -418,14 +475,16 @@ async function validateCommit(sha, roots, taskId) {
418
475
  }
419
476
  }
420
477
  // T9245: content-intersect check — diff MUST touch at least one AC file.
421
- const intersectResult = await checkCommitContentIntersect(sha, taskId, projectRoot);
478
+ // gh#1462: the task row comes from the store, the diff from the repo under
479
+ // test — both roots, like every other mixed read on this switch.
480
+ const intersectResult = await checkCommitContentIntersect(sha, taskId, roots);
422
481
  if (!intersectResult.ok) {
423
482
  return intersectResult;
424
483
  }
425
484
  }
426
- const short = await runCommand('git', ['rev-parse', '--short', sha], projectRoot);
485
+ const short = await runCommand('git', ['rev-parse', '--short', sha], executionRoot);
427
486
  const shortSha = short.stdout.trim() || sha.slice(0, 7);
428
- const full = await runCommand('git', ['rev-parse', sha], projectRoot);
487
+ const full = await runCommand('git', ['rev-parse', sha], executionRoot);
429
488
  const fullSha = full.stdout.trim() || sha;
430
489
  return { ok: true, atom: { kind: 'commit', sha: fullSha, shortSha } };
431
490
  }
@@ -850,27 +909,26 @@ export function resolveCanonicalProjectRoot(projectRoot) {
850
909
  * Fix (T-WT-2): This function calls `resolveCanonicalProjectRoot(projectRoot)`
851
910
  * to obtain the main repo root before opening `tasks.db`. The canonical main
852
911
  * DB is authoritative for all task metadata reads during gate verification.
853
- * Git operations (`gitShowFiles`) continue to use `projectRoot` as `cwd` —
854
- * git resolves commits correctly from any directory sharing the object store.
912
+ * Git operations (`gitShowFiles`) use `executionRoot` — the repo the evidence is
913
+ * about, which gh#1462 showed is not necessarily the store root.
855
914
  *
856
915
  * @param sha - Commit SHA whose diff is inspected.
857
916
  * @param taskId - CLEO task ID used to load AC file declarations.
858
- * @param projectRoot - Absolute path for git operations (may be worktree path).
859
- * DB reads use `resolveCanonicalProjectRoot(projectRoot)` internally.
917
+ * @param roots - Store root for the task row, execution root for the diff.
860
918
  *
861
919
  * @internal
862
920
  * @task T9245
863
921
  * @task T-WT-2
922
+ * @task gh#1462
864
923
  * @adr ADR-051-worktree-extension
865
924
  */
866
- async function checkCommitContentIntersect(sha, taskId, projectRoot) {
925
+ async function checkCommitContentIntersect(sha, taskId, roots) {
926
+ const { storeRoot, executionRoot } = roots;
867
927
  // BUG-C FIX (T-WT-2 / E-WORKTREE-IVTR): always read task metadata from the
868
- // canonical (main) repository DB. When projectRoot is a worktree path the
928
+ // canonical (main) repository DB. When the store root is a worktree path the
869
929
  // worktree's tasks.db is a stale spawn-time snapshot; the main DB is the
870
- // authoritative source for task.files and task.acceptance. git operations
871
- // below (gitShowFiles) still use the original projectRoot — git shares its
872
- // object store across worktrees, so any dir in the shared tree works.
873
- const canonicalRoot = resolveCanonicalProjectRoot(projectRoot);
930
+ // authoritative source for task.files and task.acceptance.
931
+ const canonicalRoot = resolveCanonicalProjectRoot(storeRoot);
874
932
  // Best-effort load — DB unavailability MUST NOT block verify.
875
933
  let task = null;
876
934
  try {
@@ -906,7 +964,7 @@ async function checkCommitContentIntersect(sha, taskId, projectRoot) {
906
964
  * is still reported; it just cannot fail the gate.
907
965
  */
908
966
  const enforcing = provenance === 'declared';
909
- const diffFiles = await gitShowFiles(sha, projectRoot);
967
+ const diffFiles = await gitShowFiles(sha, executionRoot);
910
968
  const acPreview = `${acFiles.slice(0, 5).join(', ')}${acFiles.length > 5 ? '…' : ''}`;
911
969
  if (diffFiles.length === 0) {
912
970
  const reason = `Commit ${sha.slice(0, 7)} touches no files — cannot satisfy implemented gate ` +
@@ -1026,7 +1084,7 @@ async function gitShowFileContentAtCommit(relPath, sha, projectRoot) {
1026
1084
  * @task T11959
1027
1085
  * @task T12107
1028
1086
  */
1029
- async function validateFiles(paths, roots, taskId, commitSha) {
1087
+ async function validateFiles(paths, roots, taskId, commitSha, requireCommitArtifact = false) {
1030
1088
  const { storeRoot: projectRoot, executionRoot } = roots;
1031
1089
  if (paths.length === 0) {
1032
1090
  return {
@@ -1051,6 +1109,13 @@ async function validateFiles(paths, roots, taskId, commitSha) {
1051
1109
  if (commitSha) {
1052
1110
  content = await gitShowFileContentAtCommit(p, commitSha, executionRoot);
1053
1111
  }
1112
+ if (requireCommitArtifact && content === null) {
1113
+ return {
1114
+ ok: false,
1115
+ codeName: 'E_EVIDENCE_INSUFFICIENT',
1116
+ reason: `Cannot inspect ${p} at PR merge ${commitSha}; fetch that commit before recording evidence. Current checkout bytes cannot substitute.`,
1117
+ };
1118
+ }
1054
1119
  if (content === null && existsSync(abs)) {
1055
1120
  // Happy path — file exists on disk.
1056
1121
  const st = await stat(abs);
@@ -1109,11 +1174,38 @@ async function validateFiles(paths, roots, taskId, commitSha) {
1109
1174
  * pointing at a tmpdir, a non-git cwd) falls back to `projectRoot`, so
1110
1175
  * single-checkout behaviour is bit-for-bit unchanged.
1111
1176
  *
1177
+ * gh#1462 added the other direction: when `projectRoot` is a PARENT of the
1178
+ * checkout, neither the caller's cwd (which is that parent) nor `projectRoot`
1179
+ * is a git work tree, and `gh`/`git` run there fail with "fatal: not a git
1180
+ * repository". The checkout is a direct child in that layout, so it is used
1181
+ * when exactly one child is a work tree.
1182
+ *
1183
+ * T12308 closed the two holes that layout still had. A root parenting SEVERAL
1184
+ * repositories — the reported case had thirteen — had no resolution at all,
1185
+ * and the remediation the failure printed (`GIT_DIR`/`GIT_WORK_TREE`) could not
1186
+ * work, because the guard asks whether the CURRENT DIRECTORY is in a work tree
1187
+ * and the CLEO root never is. So the order is now, most to least specific:
1188
+ *
1189
+ * 1. an operator declaration (`CLEO_EVIDENCE_GIT_ROOT`, `GIT_WORK_TREE`, or
1190
+ * `evidence.gitRoot` in `.cleo/project-context.json`) — honoured by
1191
+ * RUNNING there, which is what the old advice never did;
1192
+ * 2. the caller's own checkout, when it belongs to this project;
1193
+ * 3. `projectRoot` itself;
1194
+ * 4. the one child checkout, or — when the evidence names a commit — the one
1195
+ * child checkout that actually CONTAINS it, which resolves a multi-repo
1196
+ * root with no configuration at all.
1197
+ *
1198
+ * Anything still ambiguous keeps `projectRoot`, and the atom reports the
1199
+ * candidates by name rather than attesting against a guessed repository.
1200
+ *
1112
1201
  * @param projectRoot - Absolute CLEO store root.
1113
1202
  * @param cwd - Directory the CLI was invoked from. Defaults to `process.cwd()`.
1203
+ * @param hints - What the evidence itself reveals about which repo it is about.
1114
1204
  * @returns Absolute path of the tree to execute in.
1115
1205
  *
1116
1206
  * @task T12112 (gh#1220, gh#1226, gh#1230)
1207
+ * @task gh#1462
1208
+ * @task T12308
1117
1209
  */
1118
1210
  export function resolveEvidenceExecutionRoot(projectRoot,
1119
1211
  // This function's entire purpose is to recover the tree the CLI was INVOKED
@@ -1124,20 +1216,27 @@ export function resolveEvidenceExecutionRoot(projectRoot,
1124
1216
  // while every test stayed green. The bare cwd is the only correct source,
1125
1217
  // and it only LOCATES a candidate; the same-project check below decides
1126
1218
  // whether it is honoured.
1127
- cwd = process.cwd()) {
1128
- let toplevel;
1219
+ cwd = process.cwd(), // CWD-OK: the caller's invocation dir is the subject, not a stand-in for the project root (gh#1220)
1220
+ hints = {}) {
1221
+ // T12308: an explicit declaration outranks every inference. Returned even
1222
+ // when it is NOT a work tree, so the guard can reject it BY NAME — silently
1223
+ // falling back would measure a different checkout than the operator named
1224
+ // and attest the result as if it were theirs.
1225
+ const declared = resolveDeclaredEvidenceGitRoot(projectRoot);
1226
+ if (declared !== null)
1227
+ return gitToplevel(declared.path) ?? declared.path;
1228
+ let toplevel = null;
1129
1229
  try {
1130
- toplevel = execFileSync('git', ['rev-parse', '--show-toplevel'], {
1131
- cwd,
1132
- encoding: 'utf-8',
1133
- stdio: ['ignore', 'pipe', 'ignore'],
1134
- }).trim();
1230
+ toplevel =
1231
+ execFileSync('git', ['rev-parse', '--show-toplevel'], {
1232
+ cwd,
1233
+ encoding: 'utf-8',
1234
+ stdio: ['ignore', 'pipe', 'ignore'],
1235
+ }).trim() || null;
1135
1236
  }
1136
1237
  catch {
1137
- return projectRoot; // not a git checkout — nothing better to offer
1238
+ toplevel = null; // not a git checkout — try the store root and its children
1138
1239
  }
1139
- if (!toplevel)
1140
- return projectRoot;
1141
1240
  const sameProject = (a, b) => {
1142
1241
  const norm = (x) => {
1143
1242
  try {
@@ -1150,8 +1249,27 @@ cwd = process.cwd()) {
1150
1249
  return norm(a) === norm(b);
1151
1250
  };
1152
1251
  // Only redirect when the caller's tree is a worktree OF this project.
1153
- if (sameProject(resolveCanonicalProjectRoot(toplevel), projectRoot))
1252
+ if (toplevel !== null && sameProject(resolveCanonicalProjectRoot(toplevel), projectRoot)) {
1154
1253
  return toplevel;
1254
+ }
1255
+ // The ordinary single-project layout: the CLEO root IS the checkout.
1256
+ if (isGitWorkTree(projectRoot))
1257
+ return projectRoot;
1258
+ // gh#1462: the CLEO root is a PARENT of the checkout. `gh` and `git` run
1259
+ // from `projectRoot` fail with "fatal: not a git repository", which reads as
1260
+ // a broken tool or an unsatisfiable atom. Use the one direct child that is a
1261
+ // work tree; ambiguity is reported, not guessed.
1262
+ const candidates = findNestedGitWorkTrees(projectRoot);
1263
+ if (candidates.length === 1)
1264
+ return candidates[0];
1265
+ // T12308: several children. The atom itself can still identify one — a SHA
1266
+ // exists in exactly one of these repositories in every layout that is not a
1267
+ // fork of itself. Zero or several matches stay unresolved.
1268
+ if (candidates.length > 1 && hints.commitSha !== undefined) {
1269
+ const byCommit = findWorkTreeContainingCommit(candidates, hints.commitSha);
1270
+ if (byCommit !== null)
1271
+ return byCommit;
1272
+ }
1155
1273
  return projectRoot;
1156
1274
  }
1157
1275
  async function validateTestRun(path, roots) {
@@ -1703,7 +1821,14 @@ async function validateDecision(decisionId, roots) {
1703
1821
  *
1704
1822
  * @task T9764
1705
1823
  */
1706
- async function validatePrAtom(prNumber, roots) {
1824
+ async function validatePrAtom(prNumber, roots, context) {
1825
+ if (!context) {
1826
+ return {
1827
+ ok: false,
1828
+ codeName: 'E_EVIDENCE_INSUFFICIENT',
1829
+ reason: 'PR evidence requires current task, gate, and acceptance-criterion context.',
1830
+ };
1831
+ }
1707
1832
  // Dynamic import keeps the verification module free of a static dependency
1708
1833
  // on the release subtree, mirroring the pattern used by validateDecision.
1709
1834
  const { resolvePrEvidenceAtom } = await import('../release/pr-evidence.js');
@@ -1727,6 +1852,33 @@ async function validatePrAtom(prNumber, roots) {
1727
1852
  if (!result.ok) {
1728
1853
  return { ok: false, reason: result.reason, codeName: result.codeName };
1729
1854
  }
1855
+ if (result.changedFileCount !== result.changedPaths.length || result.changedPaths.length === 0) {
1856
+ return {
1857
+ ok: false,
1858
+ codeName: 'E_EVIDENCE_INSUFFICIENT',
1859
+ reason: `PR #${prNumber} changed-file coverage is incomplete or empty (${result.changedPaths.length}/${result.changedFileCount}); inspect the full diff before recording evidence.`,
1860
+ };
1861
+ }
1862
+ const declaredFiles = context.task.files ?? [];
1863
+ const taskMention = new RegExp(`(^|[^A-Za-z0-9])${context.task.id}([^A-Za-z0-9]|$)`);
1864
+ const explicitlyLinked = taskMention.test(`${result.title}\n${result.body}\n${result.headRefName}`);
1865
+ const scopeIntersects = declaredFiles.length > 0 && diffIntersectsAc(result.changedPaths, declaredFiles);
1866
+ if ((!explicitlyLinked && !scopeIntersects) || (declaredFiles.length > 0 && !scopeIntersects)) {
1867
+ return {
1868
+ ok: false,
1869
+ codeName: 'E_EVIDENCE_CONTENT_MISMATCH',
1870
+ reason: `PR #${prNumber} does not establish a relationship to task ${context.task.id}. Changed artifacts: ${result.changedPaths.join(', ')}. Declare task files or cite the exact task in the PR; declared scope must intersect the diff.`,
1871
+ };
1872
+ }
1873
+ if (context.gates.includes('implemented') &&
1874
+ classifyEvidenceTask(context) === 'code' &&
1875
+ result.changedPaths.every(isDocumentArtifact)) {
1876
+ return {
1877
+ ok: false,
1878
+ codeName: 'E_EVIDENCE_CONTENT_MISMATCH',
1879
+ reason: `PR #${prNumber} changes only documentation and cannot implement code task ${context.task.id}. Changed artifacts: ${result.changedPaths.join(', ')}.`,
1880
+ };
1881
+ }
1730
1882
  return {
1731
1883
  ok: true,
1732
1884
  atom: {
@@ -1736,9 +1888,79 @@ async function validatePrAtom(prNumber, roots) {
1736
1888
  mergedAt: result.mergedAt,
1737
1889
  successCount: result.successCount,
1738
1890
  totalChecks: result.totalChecks,
1891
+ changedPaths: result.changedPaths,
1892
+ taskId: context.task.id,
1739
1893
  },
1740
1894
  };
1741
1895
  }
1896
+ function isDocumentArtifact(path) {
1897
+ return (/\.(md|mdx|rst|adoc|txt)$/i.test(path) ||
1898
+ /(?:^|\/)(README|LICENSE|CHANGELOG)(?:\.[^/]*)?$/i.test(path));
1899
+ }
1900
+ function classifyEvidenceTask(context) {
1901
+ if (context.task.kind === 'research' || context.task.kind === 'spike')
1902
+ return 'research';
1903
+ if (context.task.files?.length && context.task.files.every(isDocumentArtifact))
1904
+ return 'documentation';
1905
+ if (context.task.kind === 'bug')
1906
+ return 'code';
1907
+ if (context.task.labels?.some((label) => label === 'docs' || label === 'documentation'))
1908
+ return 'documentation';
1909
+ return 'code';
1910
+ }
1911
+ /**
1912
+ * Require explicit criterion linkage to inspected artifacts and real gate results.
1913
+ * @param context - Current task classification, requested gates and criterion records.
1914
+ * @param gate - Individual gate whose evidence is being assessed.
1915
+ * @param atoms - Independently validated artifacts, results and criterion references.
1916
+ * @returns A refusal reason when proof is insufficient, otherwise null.
1917
+ * @remarks PR state and CI provenance alone cannot establish every acceptance gate.
1918
+ * @example
1919
+ * ```ts
1920
+ * const refusal = checkTaskEvidenceContext(context, "implemented", validatedAtoms);
1921
+ * ```
1922
+ */
1923
+ export function checkTaskEvidenceContext(context, gate, atoms) {
1924
+ const filePaths = atoms.flatMap((atom) => atom.kind === 'files' ? atom.files.map((file) => file.path) : []);
1925
+ const prAtoms = atoms.filter((atom) => atom.kind === 'pr');
1926
+ if (prAtoms.length > 1)
1927
+ return 'Record one PR and its merge-pinned artifacts per verification attempt; multiple merge identities are ambiguous.';
1928
+ for (const atom of prAtoms) {
1929
+ if (atom.taskId !== context.task.id || !atom.changedPaths?.length)
1930
+ return 'PR provenance lacks verified task scope; re-verify with current task context.';
1931
+ if (gate === 'implemented' && !filePaths.some((path) => atom.changedPaths?.includes(path)))
1932
+ return `PR #${atom.prNumber} requires files evidence for an artifact actually changed by that PR.`;
1933
+ if (gate === 'implemented' &&
1934
+ classifyEvidenceTask(context) === 'code' &&
1935
+ atom.changedPaths.every(isDocumentArtifact))
1936
+ return 'Documentation-only PR evidence cannot implement the current code task.';
1937
+ }
1938
+ if (!['implemented', 'testsPassed', 'qaPassed'].includes(gate))
1939
+ return null;
1940
+ if (classifyEvidenceTask(context) === 'code' &&
1941
+ (gate === 'testsPassed' || gate === 'qaPassed') &&
1942
+ !atoms.some((atom) => atom.kind === 'test-run' || (atom.kind === 'tool' && !atom.notApplicable))) {
1943
+ return `Code task ${context.task.id} requires an actual verification result for ${gate}; absence of a toolchain is not a passing result.`;
1944
+ }
1945
+ const linked = atoms.filter((atom) => atom.kind === 'satisfies' && atom.targetTaskId === context.task.id);
1946
+ if (context.criteria.length > 0 && linked.length === 0)
1947
+ return `Task ${context.task.id} requires explicit criterion linkage: add satisfies:${context.task.id}#AC<n> alongside artifacts and verification results.`;
1948
+ for (const atom of linked) {
1949
+ if (!context.criteria.some((criterion) => criterion.id === atom.resolvedAcUuid))
1950
+ return `Criterion ${atom.resolvedAcUuid ?? atom.targetAcAlias} is not in the current task criteria.`;
1951
+ }
1952
+ if (linked.length > 0 &&
1953
+ gate === 'implemented' &&
1954
+ filePaths.length === 0 &&
1955
+ !atoms.some((atom) => atom.kind === 'decision'))
1956
+ return 'Criterion implementation evidence requires inspected files or a sourced research decision.';
1957
+ if (classifyEvidenceTask(context) === 'code' &&
1958
+ gate === 'implemented' &&
1959
+ atoms.some((atom) => atom.kind === 'decision') &&
1960
+ !atoms.some((atom) => atom.kind === 'commit' || atom.kind === 'pr'))
1961
+ return 'A research decision alone cannot implement a code-fix task.';
1962
+ return null;
1963
+ }
1742
1964
  /**
1743
1965
  * Check that the provided evidence atoms satisfy the callsite-coverage
1744
1966
  * requirement for tasks carrying the `callsite-coverage` label.
@@ -1881,11 +2103,13 @@ export function checkGateEvidenceMinimumDetailed(gate, atoms) {
1881
2103
  * @param capturedBy - Agent identifier
1882
2104
  * @param override - True when CLEO_OWNER_OVERRIDE is set
1883
2105
  * @param overrideReason - Reason supplied with the override
2106
+ * @param context - Task and criterion context used to capture durable proof bindings.
2107
+ * @param gate - Gate associated with the captured result and artifact references.
1884
2108
  * @returns Canonical GateEvidence ready to persist
1885
2109
  *
1886
2110
  * @task T832
1887
2111
  */
1888
- export function composeGateEvidence(atoms, capturedBy, override, overrideReason) {
2112
+ export function composeGateEvidence(atoms, capturedBy, override, overrideReason, context, gate) {
1889
2113
  const result = {
1890
2114
  atoms,
1891
2115
  capturedAt: new Date().toISOString(),
@@ -1896,6 +2120,34 @@ export function composeGateEvidence(atoms, capturedBy, override, overrideReason)
1896
2120
  if (overrideReason)
1897
2121
  result.overrideReason = overrideReason;
1898
2122
  }
2123
+ if (context && gate) {
2124
+ const linkedIds = new Set(atoms.flatMap((atom) => atom.kind === 'satisfies' && atom.targetTaskId === context.task.id && atom.resolvedAcUuid
2125
+ ? [atom.resolvedAcUuid]
2126
+ : []));
2127
+ const artifactPaths = atoms.flatMap((atom) => atom.kind === 'files' ? atom.files.map((file) => file.path) : []);
2128
+ const resultAtomIndices = atoms.flatMap((atom, index) => {
2129
+ if (gate === 'implemented')
2130
+ return atom.kind === 'commit' || atom.kind === 'pr' || atom.kind === 'decision'
2131
+ ? [index]
2132
+ : [];
2133
+ return atom.kind === 'tool' || atom.kind === 'test-run' ? [index] : [];
2134
+ });
2135
+ result.scope = {
2136
+ taskId: context.task.id,
2137
+ gate,
2138
+ classification: classifyEvidenceTask(context),
2139
+ criteria: context.criteria
2140
+ .filter((criterion) => linkedIds.has(criterion.id))
2141
+ .map((criterion) => ({
2142
+ criterionId: criterion.id,
2143
+ criterionHash: createHash('sha256').update(criterion.text).digest('hex'),
2144
+ artifactPaths: artifactPaths.length
2145
+ ? artifactPaths
2146
+ : (context.task.verification?.evidence?.implemented?.scope?.criteria.find((link) => link.criterionId === criterion.id)?.artifactPaths ?? []),
2147
+ resultAtomIndices,
2148
+ })),
2149
+ };
2150
+ }
1899
2151
  return result;
1900
2152
  }
1901
2153
  /**
@@ -1964,6 +2216,42 @@ export function isHardAtom(atom) {
1964
2216
  * @adr ADR-051 §5 / §8 (Decision 8)
1965
2217
  */
1966
2218
  export async function revalidateEvidence(evidence, projectRoot, gate, taskId, options) {
2219
+ if (taskId && gate && !evidence.override) {
2220
+ const { getTaskAccessor } = await import('../store/data-accessor.js');
2221
+ const accessor = await getTaskAccessor(resolveCanonicalProjectRoot(projectRoot));
2222
+ const task = await accessor.loadSingleTask(taskId);
2223
+ if (!task)
2224
+ throw new CleoError(ExitCode.NOT_FOUND, `Evidence task ${taskId} is missing`);
2225
+ const context = {
2226
+ task,
2227
+ gates: [gate],
2228
+ criteria: await accessor.getAcRows(taskId),
2229
+ };
2230
+ let reason = checkTaskEvidenceContext(context, gate, evidence.atoms);
2231
+ if (!reason &&
2232
+ !(classifyEvidenceTask(context) !== 'code' &&
2233
+ isDecisionOnlyImplementation(task.verification?.evidence?.implemented) &&
2234
+ DECISION_ONLY_INAPPLICABLE_GATES.includes(gate))) {
2235
+ reason = checkGateEvidenceMinimum(gate, evidence.atoms);
2236
+ }
2237
+ if (!reason && evidence.scope) {
2238
+ if (evidence.scope.taskId !== taskId || evidence.scope.gate !== gate)
2239
+ reason = 'Evidence scope does not match the current task and gate.';
2240
+ for (const link of evidence.scope.criteria) {
2241
+ const criterion = context.criteria.find((row) => row.id === link.criterionId);
2242
+ if (!criterion ||
2243
+ createHash('sha256').update(criterion.text).digest('hex') !== link.criterionHash)
2244
+ reason = `Criterion ${link.criterionId} changed after verification; record fresh evidence.`;
2245
+ }
2246
+ }
2247
+ if (reason)
2248
+ return {
2249
+ stillValid: false,
2250
+ failedAtoms: [
2251
+ { atom: evidence.atoms[0] ?? { kind: 'note', note: 'missing evidence' }, reason },
2252
+ ],
2253
+ };
2254
+ }
1967
2255
  // T9245: critical-gate override rejection.
1968
2256
  // When the gate is `implemented` or `testsPassed`, evidence that has no
1969
2257
  // non-override hard atom is rejected outright. We define "override-only"
@@ -2004,7 +2292,11 @@ export async function revalidateEvidence(evidence, projectRoot, gate, taskId, op
2004
2292
  const startedAt = Date.now();
2005
2293
  const cached = await revalidateCommitAtom(atom.sha, {
2006
2294
  storeRoot: projectRoot,
2007
- executionRoot: resolveEvidenceExecutionRoot(projectRoot),
2295
+ // T12308: same hint as validation used, so a multi-repo root cannot
2296
+ // verify green and then re-validate against a different sibling.
2297
+ executionRoot: resolveEvidenceExecutionRoot(projectRoot, undefined, {
2298
+ commitSha: atom.sha,
2299
+ }),
2008
2300
  });
2009
2301
  if (!cached.check.ok)
2010
2302
  failed.push({ atom, reason: cached.check.reason });
@@ -2022,12 +2314,21 @@ export async function revalidateEvidence(evidence, projectRoot, gate, taskId, op
2022
2314
  // checked FIRST (the file may still be absent from the local checkout
2023
2315
  // at complete time when local main was never fast-forwarded).
2024
2316
  const siblingCommit = evidence.atoms.find((a) => a.kind === 'commit');
2025
- const siblingCommitSha = siblingCommit?.sha;
2317
+ const siblingPr = evidence.atoms.find((atom) => atom.kind === 'pr');
2318
+ const siblingCommitSha = siblingPr?.mergeCommitSha ?? siblingCommit?.sha;
2319
+ const executionRoot = resolveEvidenceExecutionRoot(projectRoot, undefined, siblingCommitSha === undefined ? {} : { commitSha: siblingCommitSha });
2026
2320
  for (const f of atom.files) {
2027
- const abs = isAbsolute(f.path) ? f.path : resolvePath(projectRoot, f.path);
2321
+ const abs = isAbsolute(f.path) ? f.path : resolvePath(executionRoot, f.path);
2028
2322
  let content = null;
2029
2323
  if (siblingCommitSha) {
2030
- content = await gitShowFileContentAtCommit(f.path, siblingCommitSha, projectRoot);
2324
+ content = await gitShowFileContentAtCommit(f.path, siblingCommitSha, executionRoot);
2325
+ }
2326
+ if (siblingPr && content === null) {
2327
+ failed.push({
2328
+ atom,
2329
+ reason: `Cannot inspect ${f.path} at recorded PR merge ${siblingCommitSha}; current checkout bytes cannot substitute.`,
2330
+ });
2331
+ continue;
2031
2332
  }
2032
2333
  if (!content && existsSync(abs)) {
2033
2334
  content = await readFile(abs);
@@ -2036,7 +2337,7 @@ export async function revalidateEvidence(evidence, projectRoot, gate, taskId, op
2036
2337
  // T11959: git-show fallback — file may have been on task branch at
2037
2338
  // verify time and is now on main after merge, or it may still only
2038
2339
  // exist on the branch. Use the same resolution path as validateFiles.
2039
- content = await gitShowFileContent(f.path, taskId, projectRoot);
2340
+ content = await gitShowFileContent(f.path, taskId, executionRoot);
2040
2341
  }
2041
2342
  if (!content) {
2042
2343
  failed.push({ atom, reason: `File removed since verify: ${f.path}` });