@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
@@ -0,0 +1,760 @@
1
+ /**
2
+ * `cleo docs modernize --apply` and `--rollback` (T14113).
3
+ *
4
+ * Apply executes a {@link DocsModernizePlan}:
5
+ *
6
+ * 0. **Blobs.** Every scanned file's bytes are hash-verified into the blob
7
+ * store before anything else runs; nothing is removed without that.
8
+ * 1. **Bodies.** Each survivor's text gets every `merge-into` row folded in
9
+ * ({@link mergeDocBodies} never drops content) and its relative `.md`
10
+ * links rewritten to `[[slug]]` wikilinks.
11
+ * 2. **Store.** Survivors are stored under their slug (`ingest`); a slug the
12
+ * store already holds is merged into, never replaced (`merge`). A merged
13
+ * or deleted file is recorded where its content went (`fold`) and, when
14
+ * its bytes are not already a doc, stored under its legacy name and
15
+ * superseded by the survivor (`supersede`).
16
+ * 3. **Files.** Survivors are written to their canonical path (`write-file`)
17
+ * and every other scanned path is removed (`remove-file`).
18
+ * 4. **Graph.** Each survivor's body links become `links-to` edges (`link`)
19
+ * and the column-derived edges are rebuilt (always, in `finally`).
20
+ *
21
+ * Write-ahead: every step is appended to `.cleo/modernize/<runId>.ops.jsonl`
22
+ * as `pending` (with the values it will replace) before it acts and as
23
+ * `done` after; replaced bytes are copied under `.cleo/modernize/<runId>/`.
24
+ * {@link rollbackDocsModernize} walks the steps backwards, appends
25
+ * `reversed` per step (so a failed rollback resumes where it stopped), and
26
+ * refuses to overwrite a file edited since the apply.
27
+ *
28
+ * @task T14113
29
+ * @epic T14106
30
+ */
31
+ import { execFileSync } from 'node:child_process';
32
+ import { createHash } from 'node:crypto';
33
+ import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readdirSync, readFileSync, rmdirSync, rmSync, writeSync, } from 'node:fs';
34
+ import { dirname, join } from 'node:path';
35
+ import { docsModernizeJournalLineSchema } from '@cleocode/contracts/docs/modernize.js';
36
+ import { getCleoDirAbsolute } from '../../paths.js';
37
+ import { createAttachmentStore } from '../../store/attachment-store.js';
38
+ import { backfillBlobStoreBytes } from '../../store/blob-keep.js';
39
+ import { writeFileAtomic } from '../../tools/fs.js';
40
+ import { extractWikilinkTargets } from '../derive-links.js';
41
+ import { IMPORT_OWNER_TYPE, IMPORT_PROJECT_OWNER_ID } from '../import/attachment-store-accessor.js';
42
+ import { normalizeSlug } from '../slug-normalize.js';
43
+ import { assertPlanConsistent, assertPlanPaths, parseModernizeReceipt, safeJoin } from './guard.js';
44
+ import { readSupersessionPrior, restoreSupersession, revertDocUpdate } from './restore.js';
45
+ import { applyLinkFixes, finalPaths, linkedSlugs, mergeDocBodies, renameWikilinks, repointMarkdownLinks, rewriteDocLinks, } from './rules.js';
46
+ /** Identity recorded on every store write modernize makes. */
47
+ export const MODERNIZE_ACTOR = 'cleo docs modernize';
48
+ /** Raised when the tree moved since the plan was made. */
49
+ export class DocsModernizeStaleError extends Error {
50
+ /** Stable code for the envelope. */
51
+ code = 'E_MODERNIZE_STALE';
52
+ }
53
+ /** Raised when `--apply` meets uncommitted changes, or cannot prove there are none. */
54
+ export class DocsModernizeDirtyError extends Error {
55
+ /** Stable code for the envelope. */
56
+ code = 'E_MODERNIZE_DIRTY';
57
+ }
58
+ /** Raised when bytes cannot be confirmed or backed up before a step changes them. */
59
+ export class DocsModernizeBlobError extends Error {
60
+ /** Stable code for the envelope. */
61
+ code = 'E_MODERNIZE_BLOB';
62
+ }
63
+ /** Raised when a receipt cannot be found, was already rolled back, or a file changed since. */
64
+ export class DocsModernizeReceiptError extends Error {
65
+ /** Stable code for the envelope. */
66
+ code = 'E_MODERNIZE_RECEIPT';
67
+ }
68
+ /** SHA-256 hex of text. */
69
+ function sha256(text) {
70
+ return createHash('sha256').update(text).digest('hex');
71
+ }
72
+ /** `.cleo/modernize` under the project. */
73
+ function modernizeDir(projectRoot) {
74
+ return join(getCleoDirAbsolute(projectRoot), 'modernize');
75
+ }
76
+ /**
77
+ * Path of a run's receipt.
78
+ *
79
+ * @param projectRoot - Project root.
80
+ * @param runId - Run id.
81
+ * @returns Absolute receipt path.
82
+ */
83
+ export function modernizeReceiptPath(projectRoot, runId) {
84
+ return join(modernizeDir(projectRoot), `${runId}.json`);
85
+ }
86
+ /**
87
+ * Path of a run's write-ahead step journal.
88
+ *
89
+ * @param projectRoot - Project root.
90
+ * @param runId - Run id.
91
+ * @returns Absolute journal path.
92
+ */
93
+ export function journalPath(projectRoot, runId) {
94
+ return join(modernizeDir(projectRoot), `${runId}.ops.jsonl`);
95
+ }
96
+ /** Path of a backed-up blob for a run. */
97
+ function backupPath(projectRoot, runId, sha) {
98
+ return join(modernizeDir(projectRoot), runId, sha);
99
+ }
100
+ /** Copy text into the run's backup folder, keyed by its sha, and confirm it reads back. */
101
+ async function backup(projectRoot, runId, text) {
102
+ const sha = sha256(text);
103
+ const path = backupPath(projectRoot, runId, sha);
104
+ if (!existsSync(path))
105
+ await writeFileAtomic({ path, content: text });
106
+ if (sha256(readFileSync(path, 'utf-8')) !== sha) {
107
+ // @sync-invariant none:local-only a backup that does not read back is refused before the step it guards
108
+ throw new DocsModernizeBlobError(`backup of ${sha} did not read back intact`);
109
+ }
110
+ return sha;
111
+ }
112
+ /** Remove now-empty folders from `dir` up to (not including) `root`. */
113
+ function pruneEmptyDirs(root, dir) {
114
+ let current = dir;
115
+ while (current.startsWith(root) && current !== root) {
116
+ if (!existsSync(current) || readdirSync(current).length > 0)
117
+ return;
118
+ rmdirSync(current);
119
+ current = dirname(current);
120
+ }
121
+ }
122
+ /** Canonical JSON of a plan, for the receipt's plan hash. */
123
+ function planHash(plan) {
124
+ return sha256(JSON.stringify(plan.rows));
125
+ }
126
+ /**
127
+ * A fresh, unused slug for a superseded file: `<plan slug>-superseded[-n]`.
128
+ * Built from the plan's slug, never the old file name, so it follows the
129
+ * naming rule and `cleo docs fetch` resolves it (T14185).
130
+ */
131
+ async function legacySlug(row, projectRoot, used) {
132
+ const store = createAttachmentStore();
133
+ const base = row.slug;
134
+ let slug = `${base}-superseded`;
135
+ let n = 2;
136
+ while (used.has(slug) || (await store.findBySlug(slug, projectRoot)) !== null) {
137
+ slug = `${base}-superseded-${n}`;
138
+ n += 1;
139
+ }
140
+ used.add(slug);
141
+ return slug;
142
+ }
143
+ /** Store text, optionally under a slug, for `ownerId`; returns the attachment id. */
144
+ async function storeText(text, path, slug, kind, ownerId, projectRoot) {
145
+ const bytes = Buffer.from(text, 'utf-8');
146
+ const attachment = {
147
+ kind: 'blob',
148
+ storageKey: '', // computed by AttachmentStore.put
149
+ mime: path.endsWith('.txt') ? 'text/plain' : 'text/markdown',
150
+ size: bytes.byteLength,
151
+ };
152
+ const meta = await createAttachmentStore().put(bytes, attachment, IMPORT_OWNER_TYPE, ownerId, MODERNIZE_ACTOR, projectRoot, { ...(slug ? { slug } : {}), type: kind, skipGraphRebuild: true });
153
+ return meta.id;
154
+ }
155
+ /** The owner a row's store ref goes to: its first task id, else the project sentinel. */
156
+ function ownerOf(row) {
157
+ return row.ownerTasks.find((id) => id.startsWith('T')) ?? IMPORT_PROJECT_OWNER_ID;
158
+ }
159
+ /**
160
+ * Refuse unless the root is a git checkout with no uncommitted changes, so
161
+ * the moves land as one reviewable diff. Fails closed: a git error refuses.
162
+ */
163
+ function assertCleanTree(root) {
164
+ let status;
165
+ try {
166
+ status = execFileSync('git', ['status', '--porcelain', '--', '.'], {
167
+ cwd: root,
168
+ encoding: 'utf-8',
169
+ stdio: ['ignore', 'pipe', 'pipe'],
170
+ });
171
+ }
172
+ catch {
173
+ // @sync-invariant none:input-shape a root git cannot vouch for is refused before any write; --allow-dirty overrides
174
+ throw new DocsModernizeDirtyError(`${root} is not a git checkout git could read; pass --allow-dirty to apply anyway`);
175
+ }
176
+ if (status.trim() !== '') {
177
+ // @sync-invariant none:input-shape a dirty modernize root is refused before any write
178
+ throw new DocsModernizeDirtyError(`${root} has uncommitted changes; commit or stash them so the modernize moves land as one diff`);
179
+ }
180
+ }
181
+ /** The `links-to` targets leaving a slug now. */
182
+ async function linkTargets(slug, projectRoot) {
183
+ const { getDocsWikilinks } = await import('../wikilinks.js');
184
+ const edges = await getDocsWikilinks(slug, { direction: 'out', projectRoot });
185
+ return edges.filter((e) => e.relation === 'links-to').map((e) => e.toSlug);
186
+ }
187
+ /** Append one line and fsync it, so a journal entry survives a crash right after. */
188
+ function appendDurable(path, line) {
189
+ const fd = openSync(path, 'a');
190
+ try {
191
+ writeSync(fd, line);
192
+ fsyncSync(fd);
193
+ }
194
+ finally {
195
+ closeSync(fd);
196
+ }
197
+ }
198
+ /** Write-ahead journal of one run's steps (`<runId>.ops.jsonl`). */
199
+ export class StepJournal {
200
+ path;
201
+ ops = [];
202
+ constructor(path) {
203
+ this.path = path;
204
+ mkdirSync(dirname(path), { recursive: true });
205
+ }
206
+ /** Append a step as `pending` before it acts; returns its index. */
207
+ begin(op) {
208
+ const full = {
209
+ state: 'pending',
210
+ slug: null,
211
+ path: null,
212
+ target: null,
213
+ attachmentId: null,
214
+ ownerId: null,
215
+ beforeSha256: null,
216
+ afterSha256: null,
217
+ ...op,
218
+ };
219
+ this.ops.push(full);
220
+ const i = this.ops.length - 1;
221
+ appendDurable(this.path, `${JSON.stringify({ i, ...full })}\n`);
222
+ return i;
223
+ }
224
+ /** Mark a step `done`, with anything learned while acting. */
225
+ done(i, patch = {}) {
226
+ const current = this.ops[i];
227
+ if (!current)
228
+ return;
229
+ const next = { ...current, ...patch, state: 'done' };
230
+ this.ops[i] = next;
231
+ appendDurable(this.path, `${JSON.stringify({ i, ...next })}\n`);
232
+ }
233
+ /** Every step so far. */
234
+ list() {
235
+ return [...this.ops];
236
+ }
237
+ }
238
+ /**
239
+ * Execute a modernize plan, journaling every step ahead of it.
240
+ *
241
+ * @param options - Plan, project root and clock.
242
+ * @returns The finished receipt.
243
+ * @throws DocsModernizeStaleError when a planned file is missing or changed.
244
+ * @throws DocsModernizeDirtyError when a `tree` run cannot prove a clean checkout.
245
+ * @throws DocsModernizeBlobError when bytes cannot be confirmed or backed up.
246
+ */
247
+ export async function applyDocsModernize(options) {
248
+ const { plan, projectRoot } = options;
249
+ const root = plan.root;
250
+ const tree = plan.mode !== 'store-only';
251
+ assertPlanPaths(plan, projectRoot, options.checkoutRoot);
252
+ assertPlanConsistent(plan);
253
+ if (tree && !options.allowDirty)
254
+ assertCleanTree(root);
255
+ const texts = new Map();
256
+ for (const row of plan.rows) {
257
+ const abs = safeJoin(root, row.path);
258
+ const text = existsSync(abs) ? readFileSync(abs, 'utf-8') : null;
259
+ if (text === null || sha256(text) !== row.sha256) {
260
+ // @sync-invariant none:input-shape the plan no longer matches the tree; refused before any write
261
+ throw new DocsModernizeStaleError(`${row.path} changed since the plan was made; re-run cleo docs modernize --dry-run`);
262
+ }
263
+ texts.set(row.path, text);
264
+ }
265
+ const started = options.now?.() ?? new Date();
266
+ const stamp = started.toISOString().replace(/[-:.]/g, '').replace('T', '-').slice(0, 15);
267
+ const runId = `dm-${stamp}-${sha256(plan.generatedAt + root).slice(0, 6)}`;
268
+ const journal = new StepJournal(journalPath(projectRoot, runId));
269
+ const blobs = [];
270
+ const moves = [];
271
+ const deleted = [];
272
+ const receipt = (edgeCount) => ({
273
+ runId,
274
+ root,
275
+ appliedAt: started.toISOString(),
276
+ planSha256: planHash(plan),
277
+ ops: journal.list(),
278
+ edgeCount,
279
+ rolledBackAt: null,
280
+ blobs: [...blobs],
281
+ moves: [...moves],
282
+ deleted: [...deleted],
283
+ });
284
+ const save = async (edgeCount = 0) => {
285
+ await writeFileAtomic({
286
+ path: modernizeReceiptPath(projectRoot, runId),
287
+ content: `${JSON.stringify(receipt(edgeCount), null, 2)}\n`,
288
+ });
289
+ };
290
+ // Pass 0 — every scanned file's bytes, hash-verified in the blob store.
291
+ const cleoDir = getCleoDirAbsolute(projectRoot);
292
+ for (const row of plan.rows) {
293
+ const outcome = await backfillBlobStoreBytes(cleoDir, row.sha256, safeJoin(root, row.path));
294
+ if (outcome !== 'written' && outcome !== 'present') {
295
+ // @sync-invariant none:local-only the blob store copy is a local prerequisite; nothing synced was written
296
+ throw new DocsModernizeBlobError(`${row.path}: bytes could not be confirmed in the blob store (${outcome}); nothing was removed`);
297
+ }
298
+ blobs.push({ path: row.path, sha256: row.sha256 });
299
+ }
300
+ await save();
301
+ const store = createAttachmentStore();
302
+ const { rebuildDocsWikilinks, syncDocBodyWikilinks } = await import('../wikilinks.js');
303
+ let edgeCount = 0;
304
+ try {
305
+ if (!tree) {
306
+ // Store-only: every file becomes a task-owned, slugless doc; the folder stays.
307
+ for (const row of plan.rows) {
308
+ const ownerId = ownerOf(row);
309
+ const stored = await store.getMetadata(row.sha256, projectRoot);
310
+ const refs = stored ? await store.listRefs(stored.id, projectRoot) : [];
311
+ if (refs.some((r) => r.ownerType === IMPORT_OWNER_TYPE && r.ownerId === ownerId))
312
+ continue;
313
+ const i = journal.begin({ op: 'ingest', path: row.path, ownerId, afterSha256: row.sha256 });
314
+ if (stored) {
315
+ // Already a doc: link it to the task, leave its kind and slug alone.
316
+ await store.ref(stored.id, IMPORT_OWNER_TYPE, ownerId, MODERNIZE_ACTOR, projectRoot);
317
+ journal.done(i, { attachmentId: stored.id });
318
+ }
319
+ else {
320
+ const text = texts.get(row.path) ?? '';
321
+ const attachmentId = await storeText(text, row.path, undefined, row.kind, ownerId, projectRoot);
322
+ journal.done(i, { attachmentId });
323
+ }
324
+ }
325
+ }
326
+ else {
327
+ // Pass 1 — bodies.
328
+ const survivors = plan.rows.filter((r) => r.disposition === 'keep' || r.disposition === 'rename');
329
+ const slugByPath = new Map(plan.rows.map((r) => [r.path, r.slug]));
330
+ // Old names → final slug for existing [[wikilinks]]: the path without its
331
+ // extension always; the bare file name only when no other file shares it.
332
+ const slugRename = new Map();
333
+ const baseCount = new Map();
334
+ for (const row of plan.rows) {
335
+ const base = normalizeSlug((row.path.split('/').pop() ?? '').replace(/\.[^.]+$/, ''));
336
+ baseCount.set(base, (baseCount.get(base) ?? 0) + 1);
337
+ }
338
+ for (const row of plan.rows) {
339
+ const noExt = row.path.replace(/\.[^.]+$/, '');
340
+ if (noExt !== row.slug)
341
+ slugRename.set(noExt, row.slug);
342
+ const base = normalizeSlug((row.path.split('/').pop() ?? '').replace(/\.[^.]+$/, ''));
343
+ if (base && base !== row.slug && baseCount.get(base) === 1)
344
+ slugRename.set(base, row.slug);
345
+ }
346
+ const survivorBySlug = new Map(survivors.map((r) => [r.slug, r]));
347
+ const finalPathOf = finalPaths(plan.rows);
348
+ // The resolvable slug each merged or deleted file is stored and cited
349
+ // under: its existing doc's slug, or a fresh legacy slug (never a
350
+ // folder-prefixed path id, T14185).
351
+ const usedSlugs = new Set(plan.rows.map((r) => r.slug));
352
+ const foldSlug = new Map();
353
+ for (const row of plan.rows) {
354
+ if (row.disposition !== 'merge-into' && row.disposition !== 'delete')
355
+ continue;
356
+ const stored = await store.getMetadata(row.sha256, projectRoot);
357
+ if (stored) {
358
+ const slug = (await store.getExtras(stored.id, projectRoot))?.slug;
359
+ foldSlug.set(row.path, slug ?? row.target ?? row.slug);
360
+ }
361
+ else {
362
+ foldSlug.set(row.path, await legacySlug(row, projectRoot, usedSlugs));
363
+ }
364
+ }
365
+ const cite = (slug) => (options.wikilinks ? `[[${slug}]]` : `\`${slug}\``);
366
+ // A keep-marked file gets only the plan's `link-fix` hrefs on disk
367
+ // (T14185); its folds live in the store doc only.
368
+ const keepPaths = new Set(survivors.filter((r) => r.disposition === 'keep').map((r) => r.path));
369
+ const storeBodies = new Map();
370
+ const bodies = new Map();
371
+ for (const row of survivors) {
372
+ const original = texts.get(row.path) ?? '';
373
+ let body = original;
374
+ for (const dup of plan.rows) {
375
+ if (dup.disposition === 'merge-into' && dup.target === row.slug) {
376
+ body = mergeDocBodies(body, texts.get(dup.path) ?? '', cite(foldSlug.get(dup.path) ?? row.slug));
377
+ }
378
+ }
379
+ if (options.wikilinks)
380
+ body = rewriteDocLinks(body, row.path, slugByPath);
381
+ body = repointMarkdownLinks(body, row.path, row.target ?? row.path, finalPathOf);
382
+ body = renameWikilinks(body, slugRename);
383
+ storeBodies.set(row.path, body);
384
+ bodies.set(row.path, keepPaths.has(row.path)
385
+ ? applyLinkFixes(original, (plan.linkFixes ?? []).filter((f) => f.path === row.path))
386
+ : body);
387
+ }
388
+ // Pass 2 — store.
389
+ for (const row of survivors) {
390
+ const body = storeBodies.get(row.path) ?? '';
391
+ const existing = await store.findBySlug(row.slug, projectRoot);
392
+ if (existing && existing.metadata.sha256 === sha256(body))
393
+ continue;
394
+ if (existing) {
395
+ // Update before create, as a merge: the stored doc keeps all of its content.
396
+ const before = await store.get(existing.metadata.sha256, projectRoot);
397
+ if (!before) {
398
+ // @sync-invariant none:local-only the stored doc's bytes are unreadable, so the step could not be undone; refused before it acts
399
+ throw new DocsModernizeBlobError(`stored doc ${row.slug} has no readable bytes to back up; refusing to update it (roll back with --rollback ${runId})`);
400
+ }
401
+ const storedText = before.bytes.toString('utf-8');
402
+ const merged = mergeDocBodies(storedText, body, cite(row.slug));
403
+ storeBodies.set(row.path, merged);
404
+ if (!keepPaths.has(row.path))
405
+ bodies.set(row.path, merged);
406
+ if (merged === storedText)
407
+ continue;
408
+ if ((await store.getMetadata(sha256(merged), projectRoot)) !== null)
409
+ continue;
410
+ const beforeSha = await backup(projectRoot, runId, storedText);
411
+ const i = journal.begin({
412
+ op: 'merge',
413
+ slug: row.slug,
414
+ path: row.path,
415
+ beforeSha256: beforeSha,
416
+ afterSha256: sha256(merged),
417
+ prior: {
418
+ previousAttachmentId: existing.metadata.id,
419
+ newAttachmentId: null,
420
+ linkTargets: await linkTargets(row.slug, projectRoot),
421
+ },
422
+ });
423
+ await save();
424
+ const { updateDocBySlug } = await import('../docs-update.js');
425
+ const updated = await updateDocBySlug(projectRoot, {
426
+ slug: row.slug,
427
+ content: merged,
428
+ message: `cleo docs modernize: ${row.path}`,
429
+ attachedBy: MODERNIZE_ACTOR,
430
+ });
431
+ if (!updated.ok) {
432
+ // @sync-invariant none:input-shape the docs update path refused the merged body; the journal holds every earlier step for rollback
433
+ throw new DocsModernizeReceiptError(`update of ${row.slug} failed (${updated.error.code}): ${updated.error.message}; roll back with --rollback ${runId}`);
434
+ }
435
+ journal.done(i, {
436
+ attachmentId: updated.result.attachmentId,
437
+ prior: {
438
+ previousAttachmentId: updated.result.previousAttachmentId,
439
+ newAttachmentId: updated.result.attachmentId,
440
+ linkTargets: journal.list()[i]?.prior?.linkTargets ?? [],
441
+ },
442
+ });
443
+ continue;
444
+ }
445
+ // The same bytes are already a doc under another name: re-putting them
446
+ // would re-slug that row, which a rollback could not undo. Leave it.
447
+ if ((await store.getMetadata(sha256(body), projectRoot)) !== null)
448
+ continue;
449
+ const ownerId = ownerOf(row);
450
+ const i = journal.begin({
451
+ op: 'ingest',
452
+ slug: row.slug,
453
+ path: row.path,
454
+ ownerId,
455
+ afterSha256: sha256(body),
456
+ });
457
+ const attachmentId = await storeText(body, row.path, row.slug, row.kind, ownerId, projectRoot);
458
+ journal.done(i, { attachmentId });
459
+ }
460
+ const { supersedeDoc } = await import('../supersede.js');
461
+ for (const row of plan.rows) {
462
+ if (row.disposition !== 'merge-into' && row.disposition !== 'delete')
463
+ continue;
464
+ const survivor = row.target ? survivorBySlug.get(row.target) : undefined;
465
+ // Where the content went, even when no store step follows.
466
+ const f = journal.begin({
467
+ op: 'fold',
468
+ path: row.path,
469
+ target: row.target,
470
+ beforeSha256: row.sha256,
471
+ afterSha256: survivor ? sha256(storeBodies.get(survivor.path) ?? '') : null,
472
+ });
473
+ journal.done(f);
474
+ const stored = await store.getMetadata(row.sha256, projectRoot);
475
+ let oldSlug;
476
+ if (stored) {
477
+ // Already a doc: supersede it under its own slug, unless it is the survivor itself.
478
+ oldSlug = (await store.getExtras(stored.id, projectRoot))?.slug ?? null;
479
+ if (!oldSlug || oldSlug === row.target)
480
+ continue;
481
+ }
482
+ else {
483
+ // Stored even without a survivor in the store, so the slug its fold
484
+ // heading cites always resolves (T14185).
485
+ oldSlug = foldSlug.get(row.path) ?? (await legacySlug(row, projectRoot, usedSlugs));
486
+ const ownerId = ownerOf(row);
487
+ const i = journal.begin({
488
+ op: 'ingest',
489
+ slug: oldSlug,
490
+ path: row.path,
491
+ ownerId,
492
+ afterSha256: row.sha256,
493
+ });
494
+ const attachmentId = await storeText(texts.get(row.path) ?? '', row.path, oldSlug, row.kind, ownerId, projectRoot);
495
+ journal.done(i, { attachmentId });
496
+ }
497
+ if (!row.target || !(await store.findBySlug(row.target, projectRoot)))
498
+ continue;
499
+ const prior = await readSupersessionPrior(projectRoot, oldSlug, row.target);
500
+ const s = journal.begin({
501
+ op: 'supersede',
502
+ slug: oldSlug,
503
+ target: row.target,
504
+ ...(prior ? { prior } : {}),
505
+ });
506
+ await supersedeDoc(projectRoot, {
507
+ oldSlug,
508
+ newSlug: row.target,
509
+ reason: `cleo docs modernize ${runId}: ${row.reasons.at(-1) ?? row.disposition}`,
510
+ });
511
+ journal.done(s);
512
+ }
513
+ await save();
514
+ // Pass 3 — files.
515
+ for (const row of survivors) {
516
+ const body = bodies.get(row.path) ?? '';
517
+ const target = row.target ?? row.path;
518
+ const abs = safeJoin(root, target);
519
+ const before = existsSync(abs) ? readFileSync(abs, 'utf-8') : null;
520
+ if (before !== body) {
521
+ const beforeSha = before === null ? null : await backup(projectRoot, runId, before);
522
+ await backup(projectRoot, runId, body);
523
+ const i = journal.begin({
524
+ op: 'write-file',
525
+ path: target,
526
+ beforeSha256: beforeSha,
527
+ afterSha256: sha256(body),
528
+ });
529
+ await writeFileAtomic({ path: abs, content: body });
530
+ journal.done(i);
531
+ }
532
+ if (target !== row.path)
533
+ moves.push({ from: row.path, to: target });
534
+ }
535
+ const kept = new Set(survivors.map((r) => r.target ?? r.path));
536
+ for (const row of plan.rows) {
537
+ if (kept.has(row.path))
538
+ continue;
539
+ const abs = safeJoin(root, row.path);
540
+ if (!existsSync(abs))
541
+ continue;
542
+ const beforeSha = await backup(projectRoot, runId, texts.get(row.path) ?? '');
543
+ const i = journal.begin({ op: 'remove-file', path: row.path, beforeSha256: beforeSha });
544
+ rmSync(abs);
545
+ journal.done(i);
546
+ if (row.disposition === 'merge-into' || row.disposition === 'delete')
547
+ deleted.push(row.path);
548
+ pruneEmptyDirs(root, dirname(abs));
549
+ }
550
+ await save();
551
+ // Pass 4 — graph: wikilinks and relative markdown links both become
552
+ // `links-to` edges, so the graph holds in either link mode (T14185).
553
+ const slugByFinalPath = new Map(survivors.map((r) => [r.target ?? r.path, r.slug]));
554
+ for (const row of survivors) {
555
+ const body = storeBodies.get(row.path) ?? '';
556
+ const targets = linkedSlugs(body, row.target ?? row.path, slugByFinalPath, extractWikilinkTargets(body)).filter((slug) => slug !== row.slug);
557
+ const priorTargets = await linkTargets(row.slug, projectRoot);
558
+ const i = journal.begin({
559
+ op: 'link',
560
+ slug: row.slug,
561
+ prior: { linkTargets: priorTargets },
562
+ });
563
+ await syncDocBodyWikilinks(row.slug, targets, { projectRoot });
564
+ journal.done(i);
565
+ }
566
+ }
567
+ }
568
+ finally {
569
+ ({ edgeCount } = await rebuildDocsWikilinks({ projectRoot }));
570
+ await save(edgeCount);
571
+ }
572
+ return receipt(edgeCount);
573
+ }
574
+ /** Fold a step journal into the latest state of each step. */
575
+ function readJournal(projectRoot, runId) {
576
+ const path = journalPath(projectRoot, runId);
577
+ if (!existsSync(path))
578
+ return null;
579
+ const ops = [];
580
+ const lines = readFileSync(path, 'utf-8')
581
+ .split('\n')
582
+ .filter((l) => l.trim() !== '');
583
+ for (const [n, line] of lines.entries()) {
584
+ let json;
585
+ try {
586
+ json = JSON.parse(line);
587
+ }
588
+ catch {
589
+ // A crash mid-append tears only the last line: that step never acted
590
+ // past its `pending` write, so it is skipped. A torn line anywhere
591
+ // else means the journal itself is damaged.
592
+ if (n === lines.length - 1)
593
+ continue;
594
+ // @sync-invariant none:local-only a corrupt journal line stops the rollback before any step
595
+ throw new DocsModernizeReceiptError(`journal ${runId} line ${n + 1} is not JSON`);
596
+ }
597
+ const parsed = docsModernizeJournalLineSchema.safeParse(json);
598
+ if (!parsed.success) {
599
+ // @sync-invariant none:local-only a corrupt journal line stops the rollback before any step
600
+ throw new DocsModernizeReceiptError(`journal ${runId} has a malformed line: ${parsed.error.message}`);
601
+ }
602
+ const { i, ...op } = parsed.data;
603
+ ops[i] = op;
604
+ }
605
+ return ops.filter((op) => op !== undefined);
606
+ }
607
+ /**
608
+ * Read a run's receipt, with its steps taken from the write-ahead journal
609
+ * (which records steps the receipt file may not have caught up with).
610
+ *
611
+ * @param projectRoot - Project root.
612
+ * @param runId - Run id.
613
+ * @param checkoutRoot - The invoking checkout the run's root may sit in (T14188).
614
+ * @returns The receipt.
615
+ * @throws DocsModernizeReceiptError when it does not exist.
616
+ */
617
+ export function readDocsModernizeReceipt(projectRoot, runId, checkoutRoot = projectRoot) {
618
+ const path = modernizeReceiptPath(projectRoot, runId);
619
+ if (!/^dm-[\w-]+$/.test(runId) || !existsSync(path)) {
620
+ // @sync-invariant none:input-shape an unknown run id is refused before any write
621
+ throw new DocsModernizeReceiptError(`no modernize receipt ${runId} under .cleo/modernize/`);
622
+ }
623
+ const receipt = parseModernizeReceipt(readFileSync(path, 'utf-8'), projectRoot, checkoutRoot);
624
+ const ops = readJournal(projectRoot, runId);
625
+ return ops ? { ...receipt, ops } : receipt;
626
+ }
627
+ /**
628
+ * Reverse an apply run from its journal, newest step first. Each reversed
629
+ * step is journaled, so a rollback that stops part-way resumes on retry.
630
+ *
631
+ * @param projectRoot - Project root.
632
+ * @param runId - Run id from the apply receipt.
633
+ * @param now - Clock override (tests).
634
+ * @param checkoutRoot - The invoking checkout the run's root may sit in (T14188).
635
+ * @returns The receipt, now stamped `rolledBackAt`.
636
+ * @throws DocsModernizeReceiptError when the receipt is missing, already
637
+ * rolled back, or a file was edited after the apply.
638
+ */
639
+ export async function rollbackDocsModernize(projectRoot, runId, now = () => new Date(), checkoutRoot = projectRoot) {
640
+ const receipt = readDocsModernizeReceipt(projectRoot, runId, checkoutRoot);
641
+ if (receipt.rolledBackAt) {
642
+ // @sync-invariant none:input-shape a run is rolled back once; the second request is refused before any write
643
+ throw new DocsModernizeReceiptError(`${runId} was already rolled back at ${receipt.rolledBackAt}`);
644
+ }
645
+ const store = createAttachmentStore();
646
+ const readBackup = (sha) => {
647
+ const path = backupPath(projectRoot, runId, sha);
648
+ if (!existsSync(path)) {
649
+ // @sync-invariant none:local-only a missing backup stops the rollback before it overwrites anything
650
+ throw new DocsModernizeReceiptError(`backup ${sha} of ${runId} is missing`);
651
+ }
652
+ return readFileSync(path, 'utf-8');
653
+ };
654
+ const { syncDocBodyWikilinks, rebuildDocsWikilinks } = await import('../wikilinks.js');
655
+ const journal = journalPath(projectRoot, runId);
656
+ const ops = receipt.ops;
657
+ let edgeCount = receipt.edgeCount;
658
+ try {
659
+ for (let i = ops.length - 1; i >= 0; i -= 1) {
660
+ const op = ops[i];
661
+ if (!op || op.state === 'reversed')
662
+ continue;
663
+ const prior = op.prior ?? {};
664
+ switch (op.op) {
665
+ case 'remove-file':
666
+ case 'write-file': {
667
+ if (!op.path)
668
+ break;
669
+ const abs = safeJoin(receipt.root, op.path);
670
+ const current = existsSync(abs) ? sha256(readFileSync(abs, 'utf-8')) : null;
671
+ const expected = op.op === 'write-file' ? op.afterSha256 : null;
672
+ const restored = op.beforeSha256;
673
+ if (current !== expected && current !== restored && op.state === 'done') {
674
+ // @sync-invariant none:local-only a file edited after the apply is never overwritten; rollback stops for the owner
675
+ throw new DocsModernizeReceiptError(`${op.path} was changed after the modernize run; resolve it by hand, then re-run --rollback ${runId}`);
676
+ }
677
+ if (restored) {
678
+ mkdirSync(dirname(abs), { recursive: true });
679
+ await writeFileAtomic({ path: abs, content: readBackup(restored) });
680
+ }
681
+ else if (existsSync(abs)) {
682
+ rmSync(abs);
683
+ pruneEmptyDirs(receipt.root, dirname(abs));
684
+ }
685
+ break;
686
+ }
687
+ case 'link':
688
+ if (op.slug)
689
+ await syncDocBodyWikilinks(op.slug, prior.linkTargets ?? [], { projectRoot });
690
+ break;
691
+ case 'supersede':
692
+ if (op.slug && op.target && op.prior) {
693
+ await restoreSupersession(projectRoot, op.slug, op.target, op.prior);
694
+ }
695
+ break;
696
+ case 'merge': {
697
+ if (!op.slug || !prior.previousAttachmentId)
698
+ break;
699
+ // A merge journaled `pending` may still have committed: the slug
700
+ // then sits on a row other than the one it started on.
701
+ const live = await store.findBySlug(op.slug, projectRoot);
702
+ if (!live || live.metadata.id === prior.previousAttachmentId)
703
+ break; // never committed
704
+ // Revert only the row this run wrote: the one journaled as `done`,
705
+ // or (for a `pending` merge that committed) the row holding exactly
706
+ // the merged bytes. Anything else is a later writer's edit.
707
+ const ours = prior.newAttachmentId
708
+ ? live.metadata.id === prior.newAttachmentId
709
+ : live.metadata.sha256 === op.afterSha256;
710
+ if (!ours) {
711
+ // @sync-invariant none:local-only a doc another writer updated after the run is never reverted over their edit
712
+ throw new DocsModernizeReceiptError(`${op.slug} was updated after the modernize run; revert it by hand, then re-run --rollback ${runId}`);
713
+ }
714
+ await revertDocUpdate(projectRoot, op.slug, {
715
+ ...prior,
716
+ newAttachmentId: live.metadata.id,
717
+ });
718
+ break;
719
+ }
720
+ case 'ingest': {
721
+ const owner = op.ownerId ?? IMPORT_PROJECT_OWNER_ID;
722
+ const id = op.attachmentId ??
723
+ (op.afterSha256
724
+ ? (await store.getMetadata(op.afterSha256, projectRoot))?.id
725
+ : undefined);
726
+ if (!id)
727
+ break;
728
+ const refs = await store.listRefs(id, projectRoot);
729
+ if (refs.some((r) => r.ownerType === IMPORT_OWNER_TYPE && r.ownerId === owner)) {
730
+ await store.deref(id, IMPORT_OWNER_TYPE, owner, projectRoot);
731
+ }
732
+ break;
733
+ }
734
+ case 'fold':
735
+ break;
736
+ default: {
737
+ // Store-mode steps (T14118).
738
+ const { reverseStoreOp } = await import('./store.js');
739
+ await reverseStoreOp(projectRoot, op);
740
+ }
741
+ }
742
+ appendDurable(journal, `${JSON.stringify({ i, ...op, state: 'reversed' })}\n`);
743
+ }
744
+ }
745
+ finally {
746
+ ({ edgeCount } = await rebuildDocsWikilinks({ projectRoot }));
747
+ }
748
+ const done = {
749
+ ...receipt,
750
+ ops: readJournal(projectRoot, runId) ?? receipt.ops,
751
+ edgeCount,
752
+ rolledBackAt: now().toISOString(),
753
+ };
754
+ await writeFileAtomic({
755
+ path: modernizeReceiptPath(projectRoot, runId),
756
+ content: `${JSON.stringify(done, null, 2)}\n`,
757
+ });
758
+ return done;
759
+ }
760
+ //# sourceMappingURL=apply.js.map