@cleocode/core 2026.10.27 → 2026.11.1-canary.3

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 (520) hide show
  1. package/README.md +2 -0
  2. package/binaries/README.md +30 -10
  3. package/dist/.source-stamp.json +4 -0
  4. package/dist/audit/reconstruct.d.ts.map +1 -1
  5. package/dist/audit/reconstruct.js +3 -13
  6. package/dist/audit/reconstruct.js.map +1 -1
  7. package/dist/build-provenance.json +2 -2
  8. package/dist/cloud/nexus-cloud.d.ts.map +1 -1
  9. package/dist/cloud/nexus-cloud.js +3 -3
  10. package/dist/cloud/nexus-cloud.js.map +1 -1
  11. package/dist/cloud/nexus-device.d.ts +1 -1
  12. package/dist/cloud/nexus-link.d.ts +18 -2
  13. package/dist/cloud/nexus-link.d.ts.map +1 -1
  14. package/dist/cloud/nexus-link.js +26 -6
  15. package/dist/cloud/nexus-link.js.map +1 -1
  16. package/dist/cloud/nexus-vault.d.ts.map +1 -1
  17. package/dist/cloud/nexus-vault.js +3 -9
  18. package/dist/cloud/nexus-vault.js.map +1 -1
  19. package/dist/decide/sites/registry.d.ts +40 -0
  20. package/dist/decide/sites/registry.d.ts.map +1 -1
  21. package/dist/decide/sites/registry.js +38 -0
  22. package/dist/decide/sites/registry.js.map +1 -1
  23. package/dist/docs/adr-allocator.d.ts +3 -3
  24. package/dist/docs/adr-allocator.d.ts.map +1 -1
  25. package/dist/docs/adr-allocator.js +8 -5
  26. package/dist/docs/adr-allocator.js.map +1 -1
  27. package/dist/docs/blob-consolidation.d.ts.map +1 -1
  28. package/dist/docs/blob-consolidation.js +3 -2
  29. package/dist/docs/blob-consolidation.js.map +1 -1
  30. package/dist/docs/derive-links.d.ts +12 -0
  31. package/dist/docs/derive-links.d.ts.map +1 -1
  32. package/dist/docs/derive-links.js +19 -0
  33. package/dist/docs/derive-links.js.map +1 -1
  34. package/dist/docs/doc-name-rule.d.ts +105 -0
  35. package/dist/docs/doc-name-rule.d.ts.map +1 -0
  36. package/dist/docs/doc-name-rule.js +212 -0
  37. package/dist/docs/doc-name-rule.js.map +1 -0
  38. package/dist/docs/docs-inconsistency-detector.d.ts.map +1 -1
  39. package/dist/docs/docs-inconsistency-detector.js +4 -3
  40. package/dist/docs/docs-inconsistency-detector.js.map +1 -1
  41. package/dist/docs/docs-ops.d.ts +27 -5
  42. package/dist/docs/docs-ops.d.ts.map +1 -1
  43. package/dist/docs/docs-ops.js +149 -17
  44. package/dist/docs/docs-ops.js.map +1 -1
  45. package/dist/docs/docs-read-model.d.ts +45 -15
  46. package/dist/docs/docs-read-model.d.ts.map +1 -1
  47. package/dist/docs/docs-read-model.js +22 -102
  48. package/dist/docs/docs-read-model.js.map +1 -1
  49. package/dist/docs/docs-update.d.ts.map +1 -1
  50. package/dist/docs/docs-update.js +8 -2
  51. package/dist/docs/docs-update.js.map +1 -1
  52. package/dist/docs/doctor.d.ts +71 -4
  53. package/dist/docs/doctor.d.ts.map +1 -1
  54. package/dist/docs/doctor.js +324 -23
  55. package/dist/docs/doctor.js.map +1 -1
  56. package/dist/docs/duplicate-check.d.ts +138 -0
  57. package/dist/docs/duplicate-check.d.ts.map +1 -0
  58. package/dist/docs/duplicate-check.js +286 -0
  59. package/dist/docs/duplicate-check.js.map +1 -0
  60. package/dist/docs/fetch-result.d.ts.map +1 -1
  61. package/dist/docs/fetch-result.js +27 -0
  62. package/dist/docs/fetch-result.js.map +1 -1
  63. package/dist/docs/import/scanner.d.ts +2 -0
  64. package/dist/docs/import/scanner.d.ts.map +1 -1
  65. package/dist/docs/import/scanner.js +3 -1
  66. package/dist/docs/import/scanner.js.map +1 -1
  67. package/dist/docs/lineage.d.ts +66 -0
  68. package/dist/docs/lineage.d.ts.map +1 -0
  69. package/dist/docs/lineage.js +190 -0
  70. package/dist/docs/lineage.js.map +1 -0
  71. package/dist/docs/modernize/apply.d.ts +139 -0
  72. package/dist/docs/modernize/apply.d.ts.map +1 -0
  73. package/dist/docs/modernize/apply.js +760 -0
  74. package/dist/docs/modernize/apply.js.map +1 -0
  75. package/dist/docs/modernize/guard.d.ts +92 -0
  76. package/dist/docs/modernize/guard.d.ts.map +1 -0
  77. package/dist/docs/modernize/guard.js +214 -0
  78. package/dist/docs/modernize/guard.js.map +1 -0
  79. package/dist/docs/modernize/index.d.ts +68 -0
  80. package/dist/docs/modernize/index.d.ts.map +1 -0
  81. package/dist/docs/modernize/index.js +97 -0
  82. package/dist/docs/modernize/index.js.map +1 -0
  83. package/dist/docs/modernize/judge.d.ts +85 -0
  84. package/dist/docs/modernize/judge.d.ts.map +1 -0
  85. package/dist/docs/modernize/judge.js +186 -0
  86. package/dist/docs/modernize/judge.js.map +1 -0
  87. package/dist/docs/modernize/plan.d.ts +77 -0
  88. package/dist/docs/modernize/plan.d.ts.map +1 -0
  89. package/dist/docs/modernize/plan.js +510 -0
  90. package/dist/docs/modernize/plan.js.map +1 -0
  91. package/dist/docs/modernize/restore.d.ts +41 -0
  92. package/dist/docs/modernize/restore.d.ts.map +1 -0
  93. package/dist/docs/modernize/restore.js +103 -0
  94. package/dist/docs/modernize/restore.js.map +1 -0
  95. package/dist/docs/modernize/rules.d.ts +197 -0
  96. package/dist/docs/modernize/rules.d.ts.map +1 -0
  97. package/dist/docs/modernize/rules.js +510 -0
  98. package/dist/docs/modernize/rules.js.map +1 -0
  99. package/dist/docs/modernize/store.d.ts +64 -0
  100. package/dist/docs/modernize/store.d.ts.map +1 -0
  101. package/dist/docs/modernize/store.js +390 -0
  102. package/dist/docs/modernize/store.js.map +1 -0
  103. package/dist/docs/publications-store.d.ts +16 -2
  104. package/dist/docs/publications-store.d.ts.map +1 -1
  105. package/dist/docs/publications-store.js +23 -2
  106. package/dist/docs/publications-store.js.map +1 -1
  107. package/dist/docs/publish-pr.d.ts.map +1 -1
  108. package/dist/docs/publish-pr.js +12 -1
  109. package/dist/docs/publish-pr.js.map +1 -1
  110. package/dist/docs/rot.d.ts +94 -0
  111. package/dist/docs/rot.d.ts.map +1 -0
  112. package/dist/docs/rot.js +298 -0
  113. package/dist/docs/rot.js.map +1 -0
  114. package/dist/docs/similarity-check.d.ts +2 -0
  115. package/dist/docs/similarity-check.d.ts.map +1 -1
  116. package/dist/docs/similarity-check.js +6 -49
  117. package/dist/docs/similarity-check.js.map +1 -1
  118. package/dist/docs/slug-rename.d.ts +84 -0
  119. package/dist/docs/slug-rename.d.ts.map +1 -0
  120. package/dist/docs/slug-rename.js +166 -0
  121. package/dist/docs/slug-rename.js.map +1 -0
  122. package/dist/docs/slug-similarity.d.ts +24 -0
  123. package/dist/docs/slug-similarity.d.ts.map +1 -0
  124. package/dist/docs/slug-similarity.js +63 -0
  125. package/dist/docs/slug-similarity.js.map +1 -0
  126. package/dist/docs/wikilinks.d.ts +20 -4
  127. package/dist/docs/wikilinks.d.ts.map +1 -1
  128. package/dist/docs/wikilinks.js +52 -7
  129. package/dist/docs/wikilinks.js.map +1 -1
  130. package/dist/docs/writer-registry.d.ts.map +1 -1
  131. package/dist/docs/writer-registry.js +33 -0
  132. package/dist/docs/writer-registry.js.map +1 -1
  133. package/dist/doctor/type-matrix-repair.d.ts +194 -0
  134. package/dist/doctor/type-matrix-repair.d.ts.map +1 -0
  135. package/dist/doctor/type-matrix-repair.js +662 -0
  136. package/dist/doctor/type-matrix-repair.js.map +1 -0
  137. package/dist/gateway-client/generated/index.d.ts +2 -2
  138. package/dist/gateway-client/generated/index.d.ts.map +1 -1
  139. package/dist/gateway-client/generated/index.js +1 -1
  140. package/dist/gateway-client/generated/index.js.map +1 -1
  141. package/dist/gateway-client/generated/namespaces.gen.d.ts +5 -0
  142. package/dist/gateway-client/generated/namespaces.gen.d.ts.map +1 -1
  143. package/dist/gateway-client/generated/namespaces.gen.js +6 -1
  144. package/dist/gateway-client/generated/namespaces.gen.js.map +1 -1
  145. package/dist/gateway-client/generated/sdk.gen.d.ts +21 -1
  146. package/dist/gateway-client/generated/sdk.gen.d.ts.map +1 -1
  147. package/dist/gateway-client/generated/sdk.gen.js +55 -0
  148. package/dist/gateway-client/generated/sdk.gen.js.map +1 -1
  149. package/dist/gateway-client/generated/types.gen.d.ts +222 -0
  150. package/dist/gateway-client/generated/types.gen.d.ts.map +1 -1
  151. package/dist/gc/gc-subsystem.d.ts +4 -0
  152. package/dist/gc/gc-subsystem.d.ts.map +1 -1
  153. package/dist/gc/gc-subsystem.js +16 -0
  154. package/dist/gc/gc-subsystem.js.map +1 -1
  155. package/dist/git/work-tree.d.ts +48 -0
  156. package/dist/git/work-tree.d.ts.map +1 -1
  157. package/dist/git/work-tree.js +63 -1
  158. package/dist/git/work-tree.js.map +1 -1
  159. package/dist/hooks/handlers/session-hooks.d.ts +5 -4
  160. package/dist/hooks/handlers/session-hooks.d.ts.map +1 -1
  161. package/dist/hooks/handlers/session-hooks.js +5 -21
  162. package/dist/hooks/handlers/session-hooks.js.map +1 -1
  163. package/dist/internal.d.ts +5 -1
  164. package/dist/internal.d.ts.map +1 -1
  165. package/dist/internal.js +5 -1
  166. package/dist/internal.js.map +1 -1
  167. package/dist/memory/brain-doctor.d.ts.map +1 -1
  168. package/dist/memory/brain-doctor.js +6 -1
  169. package/dist/memory/brain-doctor.js.map +1 -1
  170. package/dist/memory/engine-compat.d.ts.map +1 -1
  171. package/dist/memory/engine-compat.js +6 -0
  172. package/dist/memory/engine-compat.js.map +1 -1
  173. package/dist/nexus/analyze-orchestrator.d.ts +24 -0
  174. package/dist/nexus/analyze-orchestrator.d.ts.map +1 -1
  175. package/dist/nexus/analyze-orchestrator.js +43 -5
  176. package/dist/nexus/analyze-orchestrator.js.map +1 -1
  177. package/dist/nexus/user-profile.d.ts +2 -1
  178. package/dist/nexus/user-profile.d.ts.map +1 -1
  179. package/dist/nexus/user-profile.js +13 -5
  180. package/dist/nexus/user-profile.js.map +1 -1
  181. package/dist/orchestrate/admission.d.ts +8 -0
  182. package/dist/orchestrate/admission.d.ts.map +1 -1
  183. package/dist/orchestrate/admission.js +3 -1
  184. package/dist/orchestrate/admission.js.map +1 -1
  185. package/dist/orchestrate/query-ops.d.ts.map +1 -1
  186. package/dist/orchestrate/query-ops.js +15 -4
  187. package/dist/orchestrate/query-ops.js.map +1 -1
  188. package/dist/orchestrate/spawn-ops.d.ts.map +1 -1
  189. package/dist/orchestrate/spawn-ops.js +43 -2
  190. package/dist/orchestrate/spawn-ops.js.map +1 -1
  191. package/dist/orchestration/index.d.ts +6 -0
  192. package/dist/orchestration/index.d.ts.map +1 -1
  193. package/dist/orchestration/index.js +3 -0
  194. package/dist/orchestration/index.js.map +1 -1
  195. package/dist/orchestration/spawn-prompt.d.ts.map +1 -1
  196. package/dist/orchestration/spawn-prompt.js +3 -1
  197. package/dist/orchestration/spawn-prompt.js.map +1 -1
  198. package/dist/paths.d.ts +43 -1
  199. package/dist/paths.d.ts.map +1 -1
  200. package/dist/paths.js +51 -1
  201. package/dist/paths.js.map +1 -1
  202. package/dist/project-lifecycle.d.ts.map +1 -1
  203. package/dist/project-lifecycle.js +31 -0
  204. package/dist/project-lifecycle.js.map +1 -1
  205. package/dist/release/authorization.d.ts +156 -0
  206. package/dist/release/authorization.d.ts.map +1 -0
  207. package/dist/release/authorization.js +460 -0
  208. package/dist/release/authorization.js.map +1 -0
  209. package/dist/release/ci-evidence.d.ts +55 -4
  210. package/dist/release/ci-evidence.d.ts.map +1 -1
  211. package/dist/release/ci-evidence.js +251 -22
  212. package/dist/release/ci-evidence.js.map +1 -1
  213. package/dist/release/ci-run-evidence.d.ts +107 -0
  214. package/dist/release/ci-run-evidence.d.ts.map +1 -0
  215. package/dist/release/ci-run-evidence.js +234 -0
  216. package/dist/release/ci-run-evidence.js.map +1 -0
  217. package/dist/release/cut.d.ts +231 -0
  218. package/dist/release/cut.d.ts.map +1 -0
  219. package/dist/release/cut.js +775 -0
  220. package/dist/release/cut.js.map +1 -0
  221. package/dist/release/deploy-evidence.d.ts +173 -0
  222. package/dist/release/deploy-evidence.d.ts.map +1 -0
  223. package/dist/release/deploy-evidence.js +381 -0
  224. package/dist/release/deploy-evidence.js.map +1 -0
  225. package/dist/release/engine-ops.d.ts.map +1 -1
  226. package/dist/release/engine-ops.js +10 -4
  227. package/dist/release/engine-ops.js.map +1 -1
  228. package/dist/release/index.d.ts +3 -0
  229. package/dist/release/index.d.ts.map +1 -1
  230. package/dist/release/index.js +2 -0
  231. package/dist/release/index.js.map +1 -1
  232. package/dist/release/invariants/archive-reason-invariant.d.ts.map +1 -1
  233. package/dist/release/invariants/archive-reason-invariant.js +4 -1
  234. package/dist/release/invariants/archive-reason-invariant.js.map +1 -1
  235. package/dist/release/open.d.ts.map +1 -1
  236. package/dist/release/open.js +2 -6
  237. package/dist/release/open.js.map +1 -1
  238. package/dist/release/plan.d.ts +34 -2
  239. package/dist/release/plan.d.ts.map +1 -1
  240. package/dist/release/plan.js +30 -6
  241. package/dist/release/plan.js.map +1 -1
  242. package/dist/release/preflight-skip.d.ts +4 -19
  243. package/dist/release/preflight-skip.d.ts.map +1 -1
  244. package/dist/release/preflight-skip.js +5 -110
  245. package/dist/release/preflight-skip.js.map +1 -1
  246. package/dist/release/reconcile-latest.d.ts +29 -0
  247. package/dist/release/reconcile-latest.d.ts.map +1 -0
  248. package/dist/release/reconcile-latest.js +71 -0
  249. package/dist/release/reconcile-latest.js.map +1 -0
  250. package/dist/release/reconcile.d.ts +3 -1
  251. package/dist/release/reconcile.d.ts.map +1 -1
  252. package/dist/release/reconcile.js +18 -6
  253. package/dist/release/reconcile.js.map +1 -1
  254. package/dist/release/release-published.d.ts +27 -0
  255. package/dist/release/release-published.d.ts.map +1 -0
  256. package/dist/release/release-published.js +35 -0
  257. package/dist/release/release-published.js.map +1 -0
  258. package/dist/release/tty-confirm.d.ts +23 -0
  259. package/dist/release/tty-confirm.d.ts.map +1 -0
  260. package/dist/release/tty-confirm.js +50 -0
  261. package/dist/release/tty-confirm.js.map +1 -0
  262. package/dist/remote/index.js +1 -1
  263. package/dist/remote/index.js.map +1 -1
  264. package/dist/resources/admission-ledger.d.ts +103 -1
  265. package/dist/resources/admission-ledger.d.ts.map +1 -1
  266. package/dist/resources/admission-ledger.js +200 -17
  267. package/dist/resources/admission-ledger.js.map +1 -1
  268. package/dist/resources/agent-lease.d.ts +82 -0
  269. package/dist/resources/agent-lease.d.ts.map +1 -0
  270. package/dist/resources/agent-lease.js +167 -0
  271. package/dist/resources/agent-lease.js.map +1 -0
  272. package/dist/resources/governor.d.ts +27 -1
  273. package/dist/resources/governor.d.ts.map +1 -1
  274. package/dist/resources/governor.js +88 -4
  275. package/dist/resources/governor.js.map +1 -1
  276. package/dist/resources/hook-footprint.d.ts +13 -1
  277. package/dist/resources/hook-footprint.d.ts.map +1 -1
  278. package/dist/resources/hook-footprint.js +33 -7
  279. package/dist/resources/hook-footprint.js.map +1 -1
  280. package/dist/resources/run-governed.d.ts +25 -0
  281. package/dist/resources/run-governed.d.ts.map +1 -1
  282. package/dist/resources/run-governed.js +44 -3
  283. package/dist/resources/run-governed.js.map +1 -1
  284. package/dist/sagas/reconcile.d.ts.map +1 -1
  285. package/dist/sagas/reconcile.js +11 -0
  286. package/dist/sagas/reconcile.js.map +1 -1
  287. package/dist/scaffold/ensure-dirs.d.ts.map +1 -1
  288. package/dist/scaffold/ensure-dirs.js +8 -6
  289. package/dist/scaffold/ensure-dirs.js.map +1 -1
  290. package/dist/security/override-cap-setting.d.ts +132 -0
  291. package/dist/security/override-cap-setting.d.ts.map +1 -1
  292. package/dist/security/override-cap-setting.js +88 -10
  293. package/dist/security/override-cap-setting.js.map +1 -1
  294. package/dist/security/owner-override-authority.d.ts.map +1 -1
  295. package/dist/security/owner-override-authority.js +5 -1
  296. package/dist/security/owner-override-authority.js.map +1 -1
  297. package/dist/selfimprove/draft-pr.d.ts.map +1 -1
  298. package/dist/selfimprove/draft-pr.js +4 -1
  299. package/dist/selfimprove/draft-pr.js.map +1 -1
  300. package/dist/session/engine-ops.d.ts.map +1 -1
  301. package/dist/session/engine-ops.js +4 -16
  302. package/dist/session/engine-ops.js.map +1 -1
  303. package/dist/sessions/briefing.d.ts +6 -0
  304. package/dist/sessions/briefing.d.ts.map +1 -1
  305. package/dist/sessions/briefing.js +34 -0
  306. package/dist/sessions/briefing.js.map +1 -1
  307. package/dist/sessions/session-end-snapshot-entry.js +2 -2
  308. package/dist/sessions/session-end-snapshot-entry.js.map +1 -1
  309. package/dist/sessions/session-end-snapshot.js +4 -4
  310. package/dist/sessions/session-end-snapshot.js.map +1 -1
  311. package/dist/skills/injection/token.d.ts.map +1 -1
  312. package/dist/skills/injection/token.js +3 -6
  313. package/dist/skills/injection/token.js.map +1 -1
  314. package/dist/skills/orchestrator/spawn.d.ts.map +1 -1
  315. package/dist/skills/orchestrator/spawn.js +3 -4
  316. package/dist/skills/orchestrator/spawn.js.map +1 -1
  317. package/dist/skills/propose-patch.d.ts.map +1 -1
  318. package/dist/skills/propose-patch.js +4 -1
  319. package/dist/skills/propose-patch.js.map +1 -1
  320. package/dist/spawn/branch-integration.d.ts.map +1 -1
  321. package/dist/spawn/branch-integration.js +5 -2
  322. package/dist/spawn/branch-integration.js.map +1 -1
  323. package/dist/store/attachment-repair.d.ts.map +1 -1
  324. package/dist/store/attachment-repair.js +5 -4
  325. package/dist/store/attachment-repair.js.map +1 -1
  326. package/dist/store/attachment-store.d.ts +6 -0
  327. package/dist/store/attachment-store.d.ts.map +1 -1
  328. package/dist/store/attachment-store.js +19 -2
  329. package/dist/store/attachment-store.js.map +1 -1
  330. package/dist/store/blob-ops.d.ts +1 -1
  331. package/dist/store/blob-ops.d.ts.map +1 -1
  332. package/dist/store/blob-ops.js +7 -3
  333. package/dist/store/blob-ops.js.map +1 -1
  334. package/dist/store/conduit-doc-history.d.ts +15 -1
  335. package/dist/store/conduit-doc-history.d.ts.map +1 -1
  336. package/dist/store/conduit-doc-history.js +16 -10
  337. package/dist/store/conduit-doc-history.js.map +1 -1
  338. package/dist/store/db-helpers.d.ts +29 -0
  339. package/dist/store/db-helpers.d.ts.map +1 -1
  340. package/dist/store/db-helpers.js +89 -1
  341. package/dist/store/db-helpers.js.map +1 -1
  342. package/dist/store/decision-adr-links.d.ts.map +1 -1
  343. package/dist/store/decision-adr-links.js +5 -2
  344. package/dist/store/decision-adr-links.js.map +1 -1
  345. package/dist/store/git-checkpoint.d.ts +11 -1
  346. package/dist/store/git-checkpoint.d.ts.map +1 -1
  347. package/dist/store/git-checkpoint.js +14 -3
  348. package/dist/store/git-checkpoint.js.map +1 -1
  349. package/dist/store/llmtxt-blob-adapter.d.ts.map +1 -1
  350. package/dist/store/llmtxt-blob-adapter.js +4 -1
  351. package/dist/store/llmtxt-blob-adapter.js.map +1 -1
  352. package/dist/store/schema/attachments.d.ts +7 -5
  353. package/dist/store/schema/attachments.d.ts.map +1 -1
  354. package/dist/store/schema/attachments.js +5 -2
  355. package/dist/store/schema/attachments.js.map +1 -1
  356. package/dist/store/session-store.d.ts.map +1 -1
  357. package/dist/store/session-store.js +6 -0
  358. package/dist/store/session-store.js.map +1 -1
  359. package/dist/store/sqlite-backup.d.ts +6 -1
  360. package/dist/store/sqlite-backup.d.ts.map +1 -1
  361. package/dist/store/sqlite-backup.js +9 -5
  362. package/dist/store/sqlite-backup.js.map +1 -1
  363. package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
  364. package/dist/store/sqlite-data-accessor.js +63 -36
  365. package/dist/store/sqlite-data-accessor.js.map +1 -1
  366. package/dist/system/backup.d.ts +29 -0
  367. package/dist/system/backup.d.ts.map +1 -1
  368. package/dist/system/backup.js +240 -37
  369. package/dist/system/backup.js.map +1 -1
  370. package/dist/system/dependencies.d.ts +22 -0
  371. package/dist/system/dependencies.d.ts.map +1 -1
  372. package/dist/system/dependencies.js +49 -2
  373. package/dist/system/dependencies.js.map +1 -1
  374. package/dist/system/health.d.ts.map +1 -1
  375. package/dist/system/health.js +23 -1
  376. package/dist/system/health.js.map +1 -1
  377. package/dist/task-work/claims.d.ts.map +1 -1
  378. package/dist/task-work/claims.js +14 -0
  379. package/dist/task-work/claims.js.map +1 -1
  380. package/dist/task-work/index.d.ts.map +1 -1
  381. package/dist/task-work/index.js +22 -0
  382. package/dist/task-work/index.js.map +1 -1
  383. package/dist/tasks/ac-coverage-gate.d.ts +27 -0
  384. package/dist/tasks/ac-coverage-gate.d.ts.map +1 -1
  385. package/dist/tasks/ac-coverage-gate.js +63 -28
  386. package/dist/tasks/ac-coverage-gate.js.map +1 -1
  387. package/dist/tasks/affected-packages.d.ts +26 -30
  388. package/dist/tasks/affected-packages.d.ts.map +1 -1
  389. package/dist/tasks/affected-packages.js +27 -50
  390. package/dist/tasks/affected-packages.js.map +1 -1
  391. package/dist/tasks/affected-scope.d.ts +13 -3
  392. package/dist/tasks/affected-scope.d.ts.map +1 -1
  393. package/dist/tasks/affected-scope.js +29 -11
  394. package/dist/tasks/affected-scope.js.map +1 -1
  395. package/dist/tasks/change-set.d.ts.map +1 -1
  396. package/dist/tasks/change-set.js +39 -20
  397. package/dist/tasks/change-set.js.map +1 -1
  398. package/dist/tasks/command-gate-cmd.d.ts +33 -0
  399. package/dist/tasks/command-gate-cmd.d.ts.map +1 -0
  400. package/dist/tasks/command-gate-cmd.js +45 -0
  401. package/dist/tasks/command-gate-cmd.js.map +1 -0
  402. package/dist/tasks/complete-ci.d.ts +9 -0
  403. package/dist/tasks/complete-ci.d.ts.map +1 -1
  404. package/dist/tasks/complete-ci.js +21 -4
  405. package/dist/tasks/complete-ci.js.map +1 -1
  406. package/dist/tasks/complete.d.ts.map +1 -1
  407. package/dist/tasks/complete.js +14 -0
  408. package/dist/tasks/complete.js.map +1 -1
  409. package/dist/tasks/component-pr.d.ts +37 -0
  410. package/dist/tasks/component-pr.d.ts.map +1 -1
  411. package/dist/tasks/component-pr.js +106 -14
  412. package/dist/tasks/component-pr.js.map +1 -1
  413. package/dist/tasks/done-plan.d.ts +14 -1
  414. package/dist/tasks/done-plan.d.ts.map +1 -1
  415. package/dist/tasks/done-plan.js +125 -15
  416. package/dist/tasks/done-plan.js.map +1 -1
  417. package/dist/tasks/done-record.d.ts.map +1 -1
  418. package/dist/tasks/done-record.js +4 -0
  419. package/dist/tasks/done-record.js.map +1 -1
  420. package/dist/tasks/engine-converters.d.ts.map +1 -1
  421. package/dist/tasks/engine-converters.js +2 -0
  422. package/dist/tasks/engine-converters.js.map +1 -1
  423. package/dist/tasks/evidence-tree.d.ts +28 -1
  424. package/dist/tasks/evidence-tree.d.ts.map +1 -1
  425. package/dist/tasks/evidence-tree.js +66 -2
  426. package/dist/tasks/evidence-tree.js.map +1 -1
  427. package/dist/tasks/evidence.d.ts +39 -27
  428. package/dist/tasks/evidence.d.ts.map +1 -1
  429. package/dist/tasks/evidence.js +382 -188
  430. package/dist/tasks/evidence.js.map +1 -1
  431. package/dist/tasks/gate-notes.d.ts +17 -0
  432. package/dist/tasks/gate-notes.d.ts.map +1 -1
  433. package/dist/tasks/gate-notes.js.map +1 -1
  434. package/dist/tasks/gate-preview.d.ts +7 -1
  435. package/dist/tasks/gate-preview.d.ts.map +1 -1
  436. package/dist/tasks/gate-preview.js +30 -4
  437. package/dist/tasks/gate-preview.js.map +1 -1
  438. package/dist/tasks/gate-runner.d.ts +42 -0
  439. package/dist/tasks/gate-runner.d.ts.map +1 -1
  440. package/dist/tasks/gate-runner.js +234 -94
  441. package/dist/tasks/gate-runner.js.map +1 -1
  442. package/dist/tasks/manifest-scope.d.ts +115 -0
  443. package/dist/tasks/manifest-scope.d.ts.map +1 -0
  444. package/dist/tasks/manifest-scope.js +352 -0
  445. package/dist/tasks/manifest-scope.js.map +1 -0
  446. package/dist/tasks/merge-close.d.ts +376 -0
  447. package/dist/tasks/merge-close.d.ts.map +1 -0
  448. package/dist/tasks/merge-close.js +1497 -0
  449. package/dist/tasks/merge-close.js.map +1 -0
  450. package/dist/tasks/qa-run-binding.d.ts +5 -0
  451. package/dist/tasks/qa-run-binding.d.ts.map +1 -1
  452. package/dist/tasks/qa-run-binding.js +6 -1
  453. package/dist/tasks/qa-run-binding.js.map +1 -1
  454. package/dist/tasks/req.d.ts.map +1 -1
  455. package/dist/tasks/req.js +29 -1
  456. package/dist/tasks/req.js.map +1 -1
  457. package/dist/tasks/review-verdict.d.ts +159 -0
  458. package/dist/tasks/review-verdict.d.ts.map +1 -0
  459. package/dist/tasks/review-verdict.js +442 -0
  460. package/dist/tasks/review-verdict.js.map +1 -0
  461. package/dist/tasks/task-reparent.d.ts.map +1 -1
  462. package/dist/tasks/task-reparent.js +22 -3
  463. package/dist/tasks/task-reparent.js.map +1 -1
  464. package/dist/tasks/test-report-counts.d.ts +274 -0
  465. package/dist/tasks/test-report-counts.d.ts.map +1 -0
  466. package/dist/tasks/test-report-counts.js +746 -0
  467. package/dist/tasks/test-report-counts.js.map +1 -0
  468. package/dist/tasks/test-run-binding.d.ts +20 -45
  469. package/dist/tasks/test-run-binding.d.ts.map +1 -1
  470. package/dist/tasks/test-run-binding.js +34 -119
  471. package/dist/tasks/test-run-binding.js.map +1 -1
  472. package/dist/tasks/update.d.ts.map +1 -1
  473. package/dist/tasks/update.js +42 -0
  474. package/dist/tasks/update.js.map +1 -1
  475. package/dist/tasks/verification-policy.d.ts +12 -0
  476. package/dist/tasks/verification-policy.d.ts.map +1 -1
  477. package/dist/tasks/verification-policy.js +13 -0
  478. package/dist/tasks/verification-policy.js.map +1 -1
  479. package/dist/tasks/workspace-resolution.d.ts +225 -0
  480. package/dist/tasks/workspace-resolution.d.ts.map +1 -0
  481. package/dist/tasks/workspace-resolution.js +497 -0
  482. package/dist/tasks/workspace-resolution.js.map +1 -0
  483. package/dist/validation/doctor/checks.d.ts +20 -0
  484. package/dist/validation/doctor/checks.d.ts.map +1 -1
  485. package/dist/validation/doctor/checks.js +112 -0
  486. package/dist/validation/doctor/checks.js.map +1 -1
  487. package/dist/validation/engine-ops.d.ts.map +1 -1
  488. package/dist/validation/engine-ops.js +37 -3
  489. package/dist/validation/engine-ops.js.map +1 -1
  490. package/dist/validation/validate-ops.d.ts +4 -0
  491. package/dist/validation/validate-ops.d.ts.map +1 -1
  492. package/dist/validation/validate-ops.js +79 -2
  493. package/dist/validation/validate-ops.js.map +1 -1
  494. package/dist/worktree/list.d.ts +4 -21
  495. package/dist/worktree/list.d.ts.map +1 -1
  496. package/dist/worktree/list.js +14 -44
  497. package/dist/worktree/list.js.map +1 -1
  498. package/dist/worktree/merged-cleanup.d.ts +63 -0
  499. package/dist/worktree/merged-cleanup.d.ts.map +1 -0
  500. package/dist/worktree/merged-cleanup.js +571 -0
  501. package/dist/worktree/merged-cleanup.js.map +1 -0
  502. package/dist/worktree/prune.d.ts +64 -2
  503. package/dist/worktree/prune.d.ts.map +1 -1
  504. package/dist/worktree/prune.js +151 -6
  505. package/dist/worktree/prune.js.map +1 -1
  506. package/dist/worktree/sentinel-index.d.ts +14 -0
  507. package/dist/worktree/sentinel-index.d.ts.map +1 -1
  508. package/dist/worktree/sentinel-index.js +17 -0
  509. package/dist/worktree/sentinel-index.js.map +1 -1
  510. package/dist/worktree/worktree-adopt.d.ts +1 -13
  511. package/dist/worktree/worktree-adopt.d.ts.map +1 -1
  512. package/dist/worktree/worktree-adopt.js +2 -17
  513. package/dist/worktree/worktree-adopt.js.map +1 -1
  514. package/package.json +12 -12
  515. package/schemas/project-context.schema.json +33 -0
  516. package/templates/CLEO-INJECTION.md +6 -16
  517. package/templates/CLEO-REFERENCE.md +27 -4
  518. package/templates/git-hooks/legacy-hashes.json +2 -1
  519. package/templates/git-hooks/pre-commit +43 -11
  520. package/templates/workflows/release-prepare.yml.tmpl +9 -88
@@ -30,7 +30,7 @@ import { constants, mkdirSync, readFileSync, realpathSync, renameSync, writeFile
30
30
  import { lstat, open, readFile, realpath, stat } from 'node:fs/promises';
31
31
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
32
32
  import { isDeepStrictEqual } from 'node:util';
33
- import { acceptanceGateResultSchema, acceptanceGateSchema, testCountReportSchema, testCountSummarySchema, } from '@cleocode/contracts/acceptance-gate-schema.js';
33
+ import { acceptanceGateResultSchema, acceptanceGateSchema, testCountSummarySchema, } from '@cleocode/contracts/acceptance-gate-schema.js';
34
34
  import { findOnPath } from '@cleocode/paths';
35
35
  import { resolveOriginDefault } from '../git/base-ref.js';
36
36
  import { discoveryEnv } from '../git/work-tree.js';
@@ -42,11 +42,13 @@ import { captureWrapped } from '../resources/spawn-wrapper.js';
42
42
  import { createAttachmentStore } from '../store/attachment-store.js';
43
43
  import { registerTeardownAbort } from '../teardown-signal.js';
44
44
  import { acItemToText, acTextHash } from './ac-table.js';
45
+ import { commandGateCmdProblem } from './command-gate-cmd.js';
45
46
  import { splitCommandLine } from './command-line.js';
46
47
  import { resolveCanonicalProjectRoot } from './evidence.js';
47
- import { resolveEvidenceTree } from './evidence-tree.js';
48
- import { buildGateCacheEntryBody, captureGateCacheState, describeGateCacheMiss, evidenceCacheKeyPath, gateCacheEntryPath, readGateCacheEntry, sealGateCacheEntry, } from './gate-result-cache.js';
48
+ import { resolveEvidenceRepository, resolveEvidenceTree } from './evidence-tree.js';
49
+ import { buildGateCacheEntryBody, captureGateCacheState, describeGateCacheMiss, evidenceCacheKeyPath, gateCacheEntryPath, isCacheableGate, readGateCacheEntry, sealGateCacheEntry, } from './gate-result-cache.js';
49
50
  import { planHeavyToolEnv, vitestWorkerCeiling } from './heavy-tool-env.js';
51
+ import { readParsedTestReport, readTestReport, TEST_REPORT_FORMATS, } from './test-report-counts.js';
50
52
  import { resolveSpawnTimeoutMs } from './tool-cache.js';
51
53
  import { acquireGlobalSlot } from './tool-semaphore.js';
52
54
  // ─── Constants ────────────────────────────────────────────────────────────────
@@ -183,6 +185,85 @@ function loadEvidenceCacheKey() {
183
185
  return null;
184
186
  }
185
187
  }
188
+ /**
189
+ * Read (never create) the per-machine evidence cache key, for a read-only
190
+ * probe (T14138). `null` when it does not exist yet or cannot be read.
191
+ */
192
+ function readEvidenceCacheKey() {
193
+ try {
194
+ const hex = readFileSync(evidenceCacheKeyPath(), 'utf-8').trim();
195
+ return /^[0-9a-f]{64}$/.test(hex) ? Buffer.from(hex, 'hex') : null;
196
+ }
197
+ catch {
198
+ return null;
199
+ }
200
+ }
201
+ /**
202
+ * Whether the evidence cache holds a sealed pass for one typed gate in the
203
+ * tree and environment the next evidence write would use (T14138), so
204
+ * `cleo done --plan` can report a pass `cleo verify --run` cached instead of
205
+ * calling it not-run. Read-only: it runs no gate, writes no entry and never
206
+ * creates the machine key. The inputs digest is computed as
207
+ * {@link runTaskGates} computes it, so a hit here is a hit for the write.
208
+ *
209
+ * @param task - Task that owns the gate.
210
+ * @param gate - The typed gate.
211
+ * @param execution - Admitted lifetime whose project root scopes the probe.
212
+ * @param env - Environment the gate would launch with.
213
+ * @returns A hit with its time, a miss naming the key part that moved (when a
214
+ * recent entry explains it), or why the cache cannot be consulted.
215
+ * @task T14138
216
+ */
217
+ export async function probeTypedGateCache(task, gate, execution, env = process.env) {
218
+ if (!TOOL_GATE_KINDS.has(gate.kind) || !isCacheableGate(gate))
219
+ return { status: 'unavailable', reason: `a ${gate.kind} gate is never cached` };
220
+ const root = resolve(execution.identity.projectRoot);
221
+ let gateRoot;
222
+ try {
223
+ gateRoot = gateTreeFor(gate, resolveTypedGateRoot(root, undefined, undefined, task.id), root);
224
+ }
225
+ catch (error) {
226
+ return {
227
+ status: 'unavailable',
228
+ reason: error instanceof Error ? error.message : String(error),
229
+ };
230
+ }
231
+ const state = await captureGateCacheState(gateRoot);
232
+ if (state === null)
233
+ return { status: 'unavailable', reason: 'the gate tree is not a git checkout' };
234
+ const key = readEvidenceCacheKey();
235
+ if (key === null)
236
+ return {
237
+ status: 'unavailable',
238
+ reason: `no readable machine key at ${evidenceCacheKeyPath()}`,
239
+ };
240
+ const { invocation, refusal } = resolveInvocation(gate, gateRoot, { ...env });
241
+ if (refusal !== undefined)
242
+ return { status: 'unavailable', reason: refusal };
243
+ let artifacts;
244
+ try {
245
+ artifacts = await snapshotGateInputs(task, gate, invocation, execution, gateRoot);
246
+ }
247
+ catch (error) {
248
+ return {
249
+ status: 'unavailable',
250
+ reason: `its inputs could not be captured: ${error instanceof Error ? error.message : String(error)}`,
251
+ };
252
+ }
253
+ const inputsHash = gateInputsHash(invocation, artifacts);
254
+ const lookup = readGateCacheEntry(root, gate, state, inputsHash, key);
255
+ if (lookup.status === 'hit')
256
+ return { status: 'hit', cachedAt: lookup.createdAt };
257
+ if (lookup.status === 'invalid')
258
+ return { status: 'unavailable', reason: lookup.reason };
259
+ return {
260
+ status: 'miss',
261
+ why: describeGateCacheMiss(root, gate, state, inputsHash, {
262
+ environmentHash: invocation?.environmentHash ?? null,
263
+ artifacts,
264
+ }),
265
+ };
266
+ }
186
267
  /**
187
268
  * Seal and store a pass this runner EXECUTED itself. Module-private for the same
188
269
  * reason as {@link loadEvidenceCacheKey}: only an observed execution is cached.
@@ -240,7 +321,8 @@ export async function captureGateInputsHash(task, gate, execution, env = process
240
321
  * @task T13693
241
322
  */
242
323
  export async function captureGateInputs(task, gate, execution, env = process.env) {
243
- const tree = resolveTypedGateRoot(resolve(execution.identity.projectRoot), undefined, undefined, task.id);
324
+ const storeRoot = resolve(execution.identity.projectRoot);
325
+ const tree = gateTreeFor(gate, resolveTypedGateRoot(storeRoot, undefined, undefined, task.id), storeRoot);
244
326
  const invocation = executableInvocation(gate, tree, { ...env });
245
327
  const artifacts = await snapshotGateInputs(task, gate, invocation, execution, tree);
246
328
  return {
@@ -305,6 +387,28 @@ env = process.env, taskId = null) {
305
387
  const tree = real(resolveEvidenceTree(projectRoot, taskId, cwd, env, ofThisProject).root);
306
388
  return tree === root ? projectRoot : tree;
307
389
  }
390
+ /**
391
+ * The tree ONE typed gate runs in, captures its inputs in and is revalidated
392
+ * in: the declared evidence repository its `repo` names (T14078), else
393
+ * `tree` ({@link resolveTypedGateRoot}).
394
+ *
395
+ * @param gate - The typed gate.
396
+ * @param tree - The task's typed-gate tree.
397
+ * @param storeRoot - CLEO store root, whose project context declares repositories.
398
+ * @returns The checkout root the gate uses.
399
+ * @throws When `repo` names a repository the project does not declare, or
400
+ * one whose path is not a checkout's root.
401
+ * @task T14078
402
+ */
403
+ export function gateTreeFor(gate, tree, storeRoot) {
404
+ if (gate.repo === undefined)
405
+ return tree;
406
+ const repo = resolveEvidenceRepository(storeRoot, gate.repo);
407
+ // @sync-invariant none:input-shape a typed gate naming an undeclared repository is refused before it runs; nothing is written
408
+ if (!repo.ok)
409
+ throw new Error(`Typed gate ${gate.req ?? gate.description}: ${repo.reason}`);
410
+ return repo.root;
411
+ }
308
412
  /**
309
413
  * Admission for a batch run without a caller-owned lifetime.
310
414
  * An invalid deadline override is not swallowed: {@link runOneGate} resolves it
@@ -381,9 +485,19 @@ export async function runGates(gates, options = {}) {
381
485
  if (cacheMode !== 'off' && (snapshot.length !== 1 || !cacheInputsHash))
382
486
  throw new Error('Typed gate caching requires one gate per batch and its inputs digest');
383
487
  // T12621: the repo state is captured only when the policy reads or writes.
384
- const cacheState = cacheMode !== 'off' && TOOL_GATE_KINDS.has(snapshot[0].kind)
385
- ? await captureGateCacheState(treeRoot)
386
- : null;
488
+ let cacheState = null;
489
+ if (cacheMode !== 'off' && TOOL_GATE_KINDS.has(snapshot[0].kind)) {
490
+ // T14078: keyed to the HEAD of the tree the gate runs in; a refused
491
+ // repository leaves the state unset and the loop reports the refusal.
492
+ let cacheTree = null;
493
+ try {
494
+ cacheTree = gateTreeFor(snapshot[0], treeRoot, projectRoot);
495
+ }
496
+ catch {
497
+ cacheTree = null;
498
+ }
499
+ cacheState = cacheTree === null ? null : await captureGateCacheState(cacheTree);
500
+ }
387
501
  const cacheKey = cacheState ? loadEvidenceCacheKey() : null;
388
502
  const results = [];
389
503
  try {
@@ -401,6 +515,7 @@ export async function runGates(gates, options = {}) {
401
515
  const parsed = acceptanceGateSchema.parse(gate);
402
516
  if (!isDeepStrictEqual(parsed, gate))
403
517
  throw new Error('Gate includes unsupported or noncanonical fields');
518
+ const gateRoot = gateTreeFor(parsed, treeRoot, projectRoot);
404
519
  if (cacheState && cacheKey && (cacheMode === 'use' || cacheMode === 'only')) {
405
520
  const lookup = readGateCacheEntry(projectRoot, parsed, cacheState, cacheInputsHash, cacheKey);
406
521
  if (lookup.status === 'hit') {
@@ -428,7 +543,7 @@ export async function runGates(gates, options = {}) {
428
543
  throw new Error(`${gateNotCachedMessage(parsed, cacheState !== null, cacheKey !== null)}${why ? ` ${why}` : ''}`);
429
544
  }
430
545
  const observed = {
431
- ...(await runOneGate(parsed, i, treeRoot, options.skipManual ?? true, batch, projectRoot)),
546
+ ...(await runOneGate(parsed, i, gateRoot, options.skipManual ?? true, { ...batch, cwd: gateRoot }, projectRoot)),
432
547
  source: 'executed',
433
548
  };
434
549
  if (cacheState && cacheKey && cacheMode !== 'only')
@@ -634,20 +749,30 @@ export async function runTaskGates(task, criteria, options) {
634
749
  // quote) becomes ITS OWN `error` result. Throwing here rejected the whole
635
750
  // batch, and the verify engine turned that into E_GENERAL, discarding
636
751
  // every other gate's verdict.
637
- const { invocation, refusal: invocationRefusal } = resolveInvocation(gate, tree, env);
752
+ // T14078: a gate naming a declared evidence repository runs there; an
753
+ // undeclared one is this gate's error, never a run in another tree.
754
+ let gateRoot = tree;
755
+ let repoRefusal;
756
+ try {
757
+ gateRoot = gateTreeFor(gate, tree, root);
758
+ }
759
+ catch (error) {
760
+ repoRefusal = error instanceof Error ? error.message : String(error);
761
+ }
762
+ const { invocation, refusal: invocationRefusal } = resolveInvocation(gate, gateRoot, env);
638
763
  // T13527: an input that cannot be captured (an ambiguous symlink, an
639
764
  // escape) is THIS gate's error, not a rejection of every gate in the
640
765
  // batch. A cancellation still propagates.
641
766
  let artifacts = [];
642
767
  let snapshotRefusal;
643
768
  try {
644
- artifacts = await snapshotGateInputs(snapshot, gate, invocation, execution, tree);
769
+ artifacts = await snapshotGateInputs(snapshot, gate, invocation, execution, gateRoot);
645
770
  }
646
771
  catch (error) {
647
772
  execution.assertActive();
648
773
  snapshotRefusal = `Typed gate inputs could not be captured: ${error instanceof Error ? error.message : String(error)}`;
649
774
  }
650
- const refusal = invocationRefusal ?? snapshotRefusal;
775
+ const refusal = repoRefusal ?? invocationRefusal ?? snapshotRefusal;
651
776
  const binding = {
652
777
  version: 1,
653
778
  verificationId,
@@ -661,7 +786,7 @@ export async function runTaskGates(task, criteria, options) {
661
786
  ...(invocation ? { invocation } : {}),
662
787
  artifacts,
663
788
  };
664
- const treeBinding = captureTreeBinding(tree, invocation, artifacts);
789
+ const treeBinding = captureTreeBinding(gateRoot, invocation, artifacts);
665
790
  if (treeBinding)
666
791
  binding.tree = treeBinding;
667
792
  const observed = refusal
@@ -698,7 +823,7 @@ export async function runTaskGates(task, criteria, options) {
698
823
  // A gate whose inputs were never captured has nothing to compare.
699
824
  const after = snapshotRefusal
700
825
  ? artifacts
701
- : await snapshotGateInputs(snapshot, gate, invocation, execution, tree);
826
+ : await snapshotGateInputs(snapshot, gate, invocation, execution, gateRoot);
702
827
  if (!isDeepStrictEqual(after, artifacts))
703
828
  throw new Error('Verification inputs changed during execution');
704
829
  }
@@ -924,7 +1049,7 @@ function rebindCommand(task, tree) {
924
1049
  }
925
1050
  /** Revalidate the stored result of the typed gate at `index`; throws when it does not stand. */
926
1051
  async function revalidateTypedResult(scope, index, gate) {
927
- const { task, rows, results, execution, root, tree, env, requirePassing } = scope;
1052
+ const { task, rows, results, execution, root, env, requirePassing } = scope;
928
1053
  execution.assertActive();
929
1054
  const matches = results.filter((result) => result.index === index);
930
1055
  if (matches.length !== 1)
@@ -934,6 +1059,8 @@ async function revalidateTypedResult(scope, index, gate) {
934
1059
  if (stale)
935
1060
  throw new Error(`Typed requirement ${gate.req ?? index} ${stale}`);
936
1061
  const binding = result.binding;
1062
+ // T14078: revalidated in the tree it runs in, its declared repository when it names one.
1063
+ const tree = gateTreeFor(gate, scope.tree, root);
937
1064
  // A refused command is bound without an invocation (T12718 review), so it
938
1065
  // revalidates as the unmet `error` it was recorded as.
939
1066
  const { invocation: resolved } = resolveInvocation(gate, tree, env);
@@ -1246,6 +1373,13 @@ export function executableInvocation(gate, root, env) {
1246
1373
  const [testCommand, ...testArgs] = gate.kind === 'test' ? splitCommandLine(gate.command) : [];
1247
1374
  if (gate.kind === 'test' && testCommand === undefined)
1248
1375
  throw new Error('Test gate command is empty');
1376
+ // T14155: a stored command gate whose cmd carries its arguments would spawn
1377
+ // a program named the whole line and fail with a bare ENOENT. Every run path
1378
+ // (preview, verify --run, done) resolves its invocation here first.
1379
+ const cmdProblem = commandGateCmdProblem(gate, root);
1380
+ // @sync-invariant none:input-shape a stored gate's cmd shape is refused before it spawns; nothing is written
1381
+ if (cmdProblem !== null)
1382
+ throw new Error(cmdProblem);
1249
1383
  const command = gate.kind === 'test'
1250
1384
  ? testCommand
1251
1385
  : gate.kind === 'command'
@@ -1674,6 +1808,37 @@ function matchingBrace(text, start) {
1674
1808
  }
1675
1809
  return -1;
1676
1810
  }
1811
+ /**
1812
+ * A report the shared reader (T14124) read, as gate counts; a refusal is
1813
+ * recorded in `refusals` (prefixed with where it was found) and yields null.
1814
+ */
1815
+ function gateCounts(read, where, refusals) {
1816
+ if (read === null)
1817
+ return null;
1818
+ if (!read.ok) {
1819
+ refusals.push(`${where} is a ${read.format} report that was refused: ${read.reason}`);
1820
+ return null;
1821
+ }
1822
+ const { passed, failed, total, failure } = read.counts;
1823
+ return {
1824
+ numPassedTests: passed,
1825
+ numFailedTests: failed,
1826
+ numTotalTests: total,
1827
+ success: failed === 0 && failure === undefined,
1828
+ };
1829
+ }
1830
+ /**
1831
+ * Whether a read report is a bare counter summary (runner summary or
1832
+ * node --test counters with no per-test results), which a gate accepts only as
1833
+ * its whole stdout or its report file (T14132).
1834
+ */
1835
+ function isBareSummary(read) {
1836
+ if (read.format !== 'runner summary JSON' && read.format !== 'node --test summary JSON')
1837
+ return false;
1838
+ const files = read.report.testResults;
1839
+ return !(Array.isArray(files) &&
1840
+ files.some((f) => Array.isArray(f.assertionResults) && f.assertionResults.length > 0));
1841
+ }
1677
1842
  /** Top-level keys that mark a JSON object as an attempted test-count report. */
1678
1843
  const COUNT_REPORT_KEYS = ['numTotalTests', 'numPassedTests', 'numFailedTests', 'testResults'];
1679
1844
  /**
@@ -1695,57 +1860,12 @@ function looksLikeCountReport(value) {
1695
1860
  value !== null &&
1696
1861
  COUNT_REPORT_KEYS.some((key) => Object.hasOwn(value, key)));
1697
1862
  }
1698
- /**
1699
- * Counts from node:test TAP output (`node --test --test-reporter=tap`, also
1700
- * via `tsx --test`), which ships no JSON reporter (T14051). The run's LAST
1701
- * top-level plan (`1..N` at column 0) must be followed by its `# tests N`,
1702
- * `# pass N` and `# fail N` summary; `# cancelled N` counts as failed. Test
1703
- * console output is re-emitted as indented or `# ` comment lines before that
1704
- * plan, so it cannot forge the summary.
1705
- *
1706
- * @param stdout - The run's captured stdout.
1707
- * @returns Counts, a refusal reason, or null when the output is not TAP.
1708
- * @task T14051
1709
- */
1710
- function tapTestCounts(stdout) {
1711
- const lines = stdout.split(/\r?\n/);
1712
- if (!lines.some((line) => /^TAP version \d+$/.test(line.trim())))
1713
- return null;
1714
- if (lines.some((line) => /^\s*Bail out!/.test(line)))
1715
- return { refusal: 'its TAP output contains "Bail out!"' };
1716
- const plan = lines.findLastIndex((line) => /^1\.\.\d+(?:\s|$)/.test(line));
1717
- if (plan === -1)
1718
- return { refusal: 'its TAP output has no top-level 1..N plan' };
1719
- const summary = new Map();
1720
- for (const line of lines.slice(plan + 1)) {
1721
- const match = line.match(/^# (tests|pass|fail|cancelled) (\d+)$/);
1722
- if (match)
1723
- summary.set(match[1], Number(match[2]));
1724
- }
1725
- const tests = summary.get('tests');
1726
- const pass = summary.get('pass');
1727
- const fail = summary.get('fail');
1728
- if (tests === undefined || pass === undefined || fail === undefined)
1729
- return {
1730
- refusal: 'its TAP output has no "# tests", "# pass" and "# fail" summary after the plan',
1731
- };
1732
- if (tests === 0 || /^1\.\.0(?:\s|$)/.test(lines[plan]))
1733
- return { refusal: 'its TAP output reports zero tests' };
1734
- const failed = fail + (summary.get('cancelled') ?? 0);
1735
- return {
1736
- numPassedTests: pass,
1737
- numFailedTests: failed,
1738
- numTotalTests: tests,
1739
- success: failed === 0,
1740
- };
1741
- }
1742
1863
  /**
1743
1864
  * Structured test counts emitted by THIS gate's own process, or `null`.
1744
1865
  *
1745
- * Validated with {@link testCountReportSchema} — the contract written for
1746
- * exactly this purpose, which cross-checks every summary counter against the
1747
- * individual assertion results, so a report cannot claim a total its own
1748
- * details contradict.
1866
+ * Read by the shared count reader ({@link readParsedTestReport}, T14124),
1867
+ * which cross-checks the counters against the individual test results, so a
1868
+ * report cannot claim a total its own details contradict.
1749
1869
  *
1750
1870
  * The whole captured stream is searched rather than requiring `stdout` to BE
1751
1871
  * the document, because a runner that writes a JSON report to stdout routinely
@@ -1758,7 +1878,8 @@ function tapTestCounts(stdout) {
1758
1878
  *
1759
1879
  * A non-runner command (a wrapper script) may instead print a bare count
1760
1880
  * summary ({@link testCountSummarySchema}) as its last stdout line (T13753),
1761
- * and node:test may print TAP ({@link tapTestCounts}, T14051).
1881
+ * and node:test may print TAP (T14051). TAP and every JSON candidate are read
1882
+ * by the reader `test-run:` uses ({@link readTestReport}, T14124).
1762
1883
  *
1763
1884
  * A candidate that parses as a count report but fails its schema is recorded
1764
1885
  * in `refusals` with its first issue (T14060), so the error can say which
@@ -1768,13 +1889,15 @@ function tapTestCounts(stdout) {
1768
1889
  * @param refusals - Receives why each report-like candidate was refused, in the order the
1769
1890
  * candidates are tried (last stdout line, TAP, then the output newest-first); the
1770
1891
  * first is the one reported.
1892
+ * @param cwd - The run's cwd, where TAP test file names resolve (T14161).
1771
1893
  * @returns Validated counts, or `null` when the run emitted no usable report.
1772
1894
  * @task T12308
1773
1895
  * @task T13753
1774
1896
  * @task T14051
1775
1897
  * @task T14060
1898
+ * @task T14161
1776
1899
  */
1777
- function structuredTestCounts(captured, refusals) {
1900
+ function structuredTestCounts(captured, refusals, cwd) {
1778
1901
  const lastLine = captured.stdout.trimEnd().split('\n').at(-1)?.trim() ?? '';
1779
1902
  if (lastLine.startsWith('{')) {
1780
1903
  try {
@@ -1790,26 +1913,36 @@ function structuredTestCounts(captured, refusals) {
1790
1913
  // Not JSON: fall through to the full-report search.
1791
1914
  }
1792
1915
  }
1793
- const tap = tapTestCounts(captured.stdout);
1794
- if (tap !== null) {
1795
- if ('refusal' in tap) {
1796
- refusals.push(tap.refusal);
1797
- return null;
1798
- }
1799
- return tap;
1800
- }
1916
+ // T14124: TAP through the reader `test-run:` uses.
1917
+ if (/^TAP version \d+$/m.test(captured.stdout))
1918
+ return gateCounts(readTestReport(captured.stdout, { cwd }), 'its TAP output', refusals);
1919
+ // T14124: a report that IS the whole stdout, in any format `test-run:` reads
1920
+ // (JUnit XML and libtest JSON lines included).
1921
+ const whole = readTestReport(captured.stdout.trim());
1922
+ if (whole !== null)
1923
+ return gateCounts(whole, 'its output', refusals);
1801
1924
  const text = `${captured.stdout}\n${captured.stderr}`.trim();
1802
1925
  if (text.length === 0)
1803
1926
  return null;
1927
+ let embeddedSummary = false;
1804
1928
  const tryParse = (candidate) => {
1805
1929
  try {
1806
1930
  const raw = JSON.parse(candidate);
1807
- const report = testCountReportSchema.safeParse(raw);
1808
- if (report.success)
1809
- return report.data;
1810
- if (looksLikeCountReport(raw))
1811
- refusals.push(`a JSON report in its output fails the full report shape (${firstSchemaIssue(report.error)})`);
1812
- return null;
1931
+ // T14124: the reader `test-run:` uses, so every shape it reads counts here.
1932
+ const read = readParsedTestReport(raw);
1933
+ // T14132: a bare counter summary found INSIDE the output (any test can
1934
+ // print `{"total":5,"passed":5}`) proves nothing; only the whole stdout or
1935
+ // the gate's report file may be one. Full reports embedded in noise count.
1936
+ if (read?.ok && isBareSummary(read)) {
1937
+ if (!embeddedSummary)
1938
+ refusals.push(`a ${read.format} object inside its output is not counted; only the whole stdout ` +
1939
+ "or the gate's report file may be a summary-shaped report. Make the summary the " +
1940
+ "command's entire stdout (nothing else printed), or write it to a file the gate " +
1941
+ 'names (--outputFile) or announces ("JSON report written to <path>")');
1942
+ embeddedSummary = true;
1943
+ return null;
1944
+ }
1945
+ return gateCounts(read, 'a JSON report in its output', refusals);
1813
1946
  }
1814
1947
  catch {
1815
1948
  return null;
@@ -1919,23 +2052,26 @@ async function fileStamp(path) {
1919
2052
  * @param before - Each candidate's stamp just before the spawn.
1920
2053
  * @param startedAt - Epoch ms just before the spawn.
1921
2054
  * @param refusals - Receives why a fresh report that failed its schema was refused (T14060).
2055
+ * @param cwd - The run's cwd, where TAP test file names resolve (T14161).
1922
2056
  * @returns Validated counts, or null.
1923
2057
  * @task T13782
1924
2058
  * @task T14060
1925
2059
  */
1926
- async function freshReportCounts(before, startedAt, refusals) {
2060
+ async function freshReportCounts(before, startedAt, refusals, cwd) {
1927
2061
  for (const [path, was] of before) {
1928
2062
  const now = await fileStamp(path);
1929
2063
  if (now === null || now.mtimeMs < startedAt || (was !== null && was.sha256 === now.sha256))
1930
2064
  continue;
1931
2065
  try {
1932
- const raw = JSON.parse(await readFile(path, 'utf-8'));
1933
- if (typeof raw.startTime === 'number' && raw.startTime < startedAt)
2066
+ // T14124: any format `test-run:` reads (JUnit XML, libtest, JSON, TAP).
2067
+ const read = readTestReport(await readFile(path, 'utf-8'), { cwd });
2068
+ if (read?.ok &&
2069
+ typeof read.report.startTime === 'number' &&
2070
+ read.report.startTime < startedAt)
1934
2071
  continue;
1935
- const report = testCountReportSchema.safeParse(raw);
1936
- if (report.success)
1937
- return report.data;
1938
- refusals.push(`${path} fails the full report shape (${firstSchemaIssue(report.error)})`);
2072
+ const counts = gateCounts(read, path, refusals);
2073
+ if (counts !== null)
2074
+ return counts;
1939
2075
  }
1940
2076
  catch {
1941
2077
  // Unreadable or not JSON: not a report of this run.
@@ -1973,13 +2109,13 @@ function describeMissingTestCount(gate, refusal) {
1973
2109
  ? `\`${invoked}\` emitted no machine-readable test report`
1974
2110
  : `\`${invoked}\` emitted a test report that was refused: ${refusal}`;
1975
2111
  return (`minCount ${gate.minCount} cannot be proved: ${found}, and an exit code alone carries no ` +
1976
- `count. Add a reporter to the gate command itself (e.g. \`${invoked} ${flag}\`) so the run ` +
1977
- `that is being attested is the run that is counted. Accepted: a full Vitest/Jest JSON report ` +
1978
- `(numTotalTests, numPassedTests, numFailedTests, numPendingTests, numTodoTests, the four ` +
1979
- `numTotal/Passed/Failed/PendingTestSuites counters, success, testResults[].assertionResults, ` +
1980
- `counters equal to the assertions); a bare summary as the LAST stdout line, at minimum ` +
1981
- `{"numPassedTests":N} (numFailedTests, numTotalTests and success optional, no testResults); ` +
1982
- `or node:test TAP (--test-reporter=tap) with its 1..N plan and # tests/# pass/# fail summary. ` +
2112
+ `count. Formats tried: ${TEST_REPORT_FORMATS.join(', ')}, and a bare count summary as the ` +
2113
+ `LAST stdout line (at minimum {"numPassedTests":N}). These are the formats \`test-run:\` ` +
2114
+ `reads, among them a full Vitest/Jest JSON report (numTotalTests, numPassedTests, ` +
2115
+ `numFailedTests, numPendingTests, numTodoTests, testResults[].assertionResults); a report's ` +
2116
+ `counters must add up and agree with its own test results. Add a reporter ` +
2117
+ `to the gate command itself (e.g. \`${invoked} ${flag}\`) so the run that is being attested ` +
2118
+ `is the run that is counted. ` +
1983
2119
  `A report file this run writes (its --outputFile, or ${REDIRECTED_JSON_REPORT}) is read too, ` +
1984
2120
  `but one produced by a separate invocation is deliberately not accepted \u2014 it is not bound ` +
1985
2121
  `to this execution; record that as \`test-run:<path>\` evidence instead, or drop minCount if ` +
@@ -2021,6 +2157,10 @@ export function outputReportsFailure(stdout) {
2021
2157
  return /\bFAIL\b|failing|Error:|\b[1-9]\d*\s+fail(?:ed|ures?)\b/i.test(stdout.replace(PASSING_TEST_LINE, '').replace(ZERO_FAILURE_COUNT, ''));
2022
2158
  }
2023
2159
  async function runTestGate(gate, index, projectRoot, timeoutMs, context) {
2160
+ // Keep a direct call's refusal (T14155) an error verdict, not a rejection.
2161
+ const cmdProblem = commandGateCmdProblem(gate, projectRoot);
2162
+ if (cmdProblem !== null)
2163
+ return makeResult(index, gate, 'error', 0, undefined, cmdProblem);
2024
2164
  const invocation = executableInvocation(gate, projectRoot, context.env ?? process.env);
2025
2165
  // T13782: a JSON reporter redirected to a file still yields this run's counts.
2026
2166
  const reportFiles = reportFileCandidates(invocation.args, invocation.cwd);
@@ -2035,8 +2175,8 @@ async function runTestGate(gate, index, projectRoot, timeoutMs, context) {
2035
2175
  if (announced !== null && !before.has(announced))
2036
2176
  before.set(announced, null);
2037
2177
  const refusals = [];
2038
- const counts = structuredTestCounts(captured, refusals) ??
2039
- (await freshReportCounts(before, startedAt, refusals));
2178
+ const counts = structuredTestCounts(captured, refusals, invocation.cwd) ??
2179
+ (await freshReportCounts(before, startedAt, refusals, invocation.cwd));
2040
2180
  // T12308: the counts come from this run's own validated report; only their
2041
2181
  // ABSENCE is an error, because inferring a count from exit zero is the thing
2042
2182
  // that must not happen. T13753: the error keeps the run's execution record.