@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
@@ -2,7 +2,8 @@
2
2
  * `cleo docs doctor` — store health diagnostics + safe repairs — T13447.
3
3
  *
4
4
  * Audits the CleoDocs store (`cleo.db` `attachments`/`docs_wikilinks` tables
5
- * plus the on-disk legacy surfaces) for eight known drift classes and, when
5
+ * plus the on-disk legacy surfaces and the `docs_publications` ledger) for nine
6
+ * known drift classes and, when
6
7
  * invoked with `apply: true`, performs the repairs that are safe to run
7
8
  * in-place:
8
9
  *
@@ -63,6 +64,25 @@
63
64
  * doc whose remaining positions are
64
65
  * unrecoverable stays listed at `info` level —
65
66
  * visibility only, no invented repair (T13843).
67
+ * - `publication-ledger-drift` — `docs_publications` rows whose mirror is not
68
+ * in sync (T14117): the file is gone (`deleted`;
69
+ * outside the project root this is a dead
70
+ * worktree) or differs from the store head
71
+ * (`modified`). Repair (`prune-publication`) deletes
72
+ * a `deleted` row only when the mirror is provably
73
+ * gone and its blob is still held by an attachments
74
+ * row or by a live ledger row. The ledger syncs
75
+ * across devices, so an absolute path or one outside
76
+ * both this root and this device's worktrees root is
77
+ * `report-only`, and a relative path still tracked by
78
+ * HEAD or the default branch (another branch checked
79
+ * out) is kept. A path in a removed worktree of this
80
+ * device is gone. Each pruned row is appended in
81
+ * full to {@link DOCS_DOCTOR_LEDGER_PRUNE_AUDIT_FILE}
82
+ * first, so it can be re-inserted. `modified` rows
83
+ * and unprotected `deleted` rows get a plan-only
84
+ * entry (republish, or ingest the edit) — the
85
+ * doctor never picks which side of a drift wins.
66
86
  *
67
87
  * SAFETY: `apply: true` (or `repair: true`) REFUSES to run without a backup receipt
68
88
  * ({@link DocsDoctorOptions.backupReceiptPath}) whose mtime is at most
@@ -79,12 +99,13 @@
79
99
  * @see wikilinks.ts — derived edge table + rebuild (T11826)
80
100
  */
81
101
  import { createHash } from 'node:crypto';
82
- import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
102
+ import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, statSync, } from 'node:fs';
83
103
  import { readFile } from 'node:fs/promises';
84
104
  import { tmpdir } from 'node:os';
85
- import { isAbsolute, join, resolve, sep } from 'node:path';
105
+ import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
86
106
  import { getCleoWorktreesRoot } from '@cleocode/paths';
87
107
  import { eq } from 'drizzle-orm';
108
+ import { gitDefaultBranchRefs, gitRefHasPath, gitToplevel } from '../git/work-tree.js';
88
109
  import { resolveCleoDir } from '../paths.js';
89
110
  import { createAttachmentStore } from '../store/attachment-store.js';
90
111
  import { backfillBlobStoreBytes, blobContentExists, resolveLegacyByteSource, } from '../store/blob-keep.js';
@@ -94,6 +115,8 @@ import { docsAttachments } from '../store/tasks-schema.js';
94
115
  import { applyConduitHistoryBackfill, planConduitHistoryBackfill, } from './conduit-history-backfill.js';
95
116
  import { deriveDocLinks, isScannableTextMime, linksJsonOrNull, } from './derive-links.js';
96
117
  import { DOCS_VERSIONING_AUDIT_FILE } from './docs-update.js';
118
+ import { assertCheckoutOfProject } from './modernize/guard.js';
119
+ import { deleteDocsPublication, readDocsPublications } from './publications-store.js';
97
120
  import { deriveWikilinkEdges, rebuildDocsWikilinks } from './wikilinks.js';
98
121
  /**
99
122
  * Maximum age of the backup receipt accepted before an `apply` run.
@@ -117,6 +140,33 @@ export const DOCS_DOCTOR_DEFAULT_OLDER_THAN_DAYS = 30;
117
140
  * @task T13447
118
141
  */
119
142
  export const DOCS_DOCTOR_MAX_DETAIL_ITEMS = 50;
143
+ /**
144
+ * Project-relative audit log the ledger prune appends each deleted
145
+ * `docs_publications` row to (one JSON line per row, written before the
146
+ * delete), so a pruned row can be restored with `upsertDocsPublication`.
147
+ *
148
+ * @task T14117
149
+ */
150
+ export const DOCS_DOCTOR_LEDGER_PRUNE_AUDIT_FILE = '.cleo/audit/docs-publications-prune.jsonl';
151
+ /**
152
+ * The diagnostic classes that carry a repair, accepted by
153
+ * {@link DocsDoctorOptions.only} to scope a run to some of them (T14174).
154
+ *
155
+ * @task T14174
156
+ */
157
+ export const DOCS_DOCTOR_REPAIR_CLASSES = [
158
+ 'dangling-pointers',
159
+ 'doc-version-skew',
160
+ 'empty-provenance',
161
+ 'wikilinks-drift',
162
+ 'legacy-blob-coverage',
163
+ 'conduit-history-gaps',
164
+ 'publication-ledger-drift',
165
+ ];
166
+ /** Whether a diagnostic kind carries a repair (T14174). @internal */
167
+ function isRepairClass(kind) {
168
+ return DOCS_DOCTOR_REPAIR_CLASSES.includes(kind);
169
+ }
120
170
  /**
121
171
  * Parse a row's `attachment_json` defensively. Malformed JSON yields null —
122
172
  * such rows are skipped by kind-scoped checks rather than failing the run.
@@ -326,6 +376,157 @@ async function fetchRowContent(row, payload, projectRoot) {
326
376
  return null;
327
377
  }
328
378
  }
379
+ /**
380
+ * Decide whether a missing mirror is provably gone, not merely absent from
381
+ * this device or this checkout (T14117 review). `docs_publications` syncs
382
+ * across devices, so a prune here deletes the row everywhere:
383
+ *
384
+ * - an absolute path, or one outside both this project root and this
385
+ * device's CLEO worktrees root, belongs to another device or checkout and
386
+ * is never proven gone;
387
+ * - a path inside this device's worktrees root whose worktree directory no
388
+ * longer exists is gone;
389
+ * - otherwise the path must be absent from `HEAD` and from the default
390
+ * branch (local and `origin/`) of the checkout that holds it, so a file
391
+ * missing only because another branch is checked out is kept. When git
392
+ * cannot answer, the row is not proven gone.
393
+ *
394
+ * @internal
395
+ */
396
+ function provePublicationGone(projectRoot, publishedPath, absPath) {
397
+ if (isAbsolute(publishedPath)) {
398
+ return {
399
+ gone: false,
400
+ reason: 'its path is absolute (published from another device or checkout)',
401
+ };
402
+ }
403
+ const relToRoot = relative(projectRoot, absPath);
404
+ const insideRoot = !relToRoot.startsWith('..') && !isAbsolute(relToRoot);
405
+ // `base` is the existing directory the path sits under; git paths are taken
406
+ // relative to it through its realpath, since gitToplevel() canonicalises
407
+ // (macOS /var → /private/var would otherwise yield a bogus `../..` path).
408
+ let base = projectRoot;
409
+ if (!insideRoot) {
410
+ const worktreesRoot = getCleoWorktreesRoot();
411
+ const relToWorktrees = relative(worktreesRoot, absPath);
412
+ if (relToWorktrees.startsWith('..') || isAbsolute(relToWorktrees)) {
413
+ return {
414
+ gone: false,
415
+ reason: "it is outside this project root and this device's worktrees root (another device's path)",
416
+ };
417
+ }
418
+ const [projectHash, worktreeName] = relToWorktrees.split(sep);
419
+ if (projectHash === undefined || worktreeName === undefined) {
420
+ return { gone: false, reason: 'it does not name a worktree' };
421
+ }
422
+ const worktreeDir = join(worktreesRoot, projectHash, worktreeName);
423
+ if (!existsSync(worktreeDir)) {
424
+ return { gone: true, reason: `its worktree ${worktreeDir} no longer exists` };
425
+ }
426
+ base = worktreeDir;
427
+ }
428
+ const toplevel = gitToplevel(base);
429
+ if (toplevel === null)
430
+ return { gone: false, reason: 'git cannot read the checkout that holds it' };
431
+ const relPath = join(relative(toplevel, realpathSync(base)), relative(base, absPath))
432
+ .split(sep)
433
+ .join('/');
434
+ const defaults = gitDefaultBranchRefs(toplevel);
435
+ for (const ref of ['HEAD', ...defaults]) {
436
+ const has = gitRefHasPath(toplevel, ref, relPath);
437
+ if (has === true)
438
+ return { gone: false, reason: `${ref} still tracks it` };
439
+ if (has === null && ref === 'HEAD')
440
+ return { gone: false, reason: 'git cannot read HEAD' };
441
+ }
442
+ return {
443
+ gone: true,
444
+ reason: `absent from ${['HEAD', ...defaults].join(', ')}`,
445
+ };
446
+ }
447
+ /**
448
+ * Classify every non-in-sync `docs_publications` row and plan its repair
449
+ * (T14117). Reuses {@link statusDocs} for the drift classification so the
450
+ * doctor and `cleo docs status` can never disagree.
451
+ *
452
+ * A `deleted` row is prunable only when its blob stays protected without it:
453
+ * some attachments row holds the sha, or another ledger row naming the same
454
+ * sha is not itself `deleted`.
455
+ *
456
+ * @internal
457
+ */
458
+ async function scanPublicationLedger(projectRoot, rows, checkoutRoot) {
459
+ const ledger = await readDocsPublications(projectRoot);
460
+ if (ledger.length === 0)
461
+ return [];
462
+ const { statusDocs } = await import('./docs-ops.js');
463
+ const status = await statusDocs({ projectRoot, checkoutRoot });
464
+ const key = (r) => `${r.ownerId}\u0000${r.blobName}\u0000${r.publishedPath}`;
465
+ const recordByKey = new Map(ledger.map((r) => [key(r), r]));
466
+ const attachmentShas = new Set(rows.map((r) => r.sha256));
467
+ const liveLedgerShas = new Map();
468
+ for (const item of status.items) {
469
+ if (item.drift === 'deleted')
470
+ continue;
471
+ const record = recordByKey.get(key(item));
472
+ if (record)
473
+ liveLedgerShas.set(record.lastBlobSha, (liveLedgerShas.get(record.lastBlobSha) ?? 0) + 1);
474
+ }
475
+ const findings = [];
476
+ for (const item of status.items) {
477
+ if (item.drift !== 'deleted' && item.drift !== 'modified')
478
+ continue;
479
+ const record = recordByKey.get(key(item));
480
+ if (!record)
481
+ continue;
482
+ const absPath = isAbsolute(record.publishedPath)
483
+ ? record.publishedPath
484
+ : resolve(checkoutRoot, record.publishedPath);
485
+ const rel = relative(checkoutRoot, absPath);
486
+ const outsideRoot = rel.startsWith('..') || isAbsolute(rel);
487
+ if (item.drift === 'modified') {
488
+ const storeMoved = item.fileSha === record.lastBlobSha;
489
+ findings.push({
490
+ record,
491
+ drift: 'modified',
492
+ outsideRoot,
493
+ action: storeMoved ? 'republish' : 'reconcile-edit',
494
+ prunable: false,
495
+ detail: storeMoved
496
+ ? `mirror ${record.publishedPath} holds the last published blob; the store head moved on — run \`cleo docs publish\``
497
+ : `mirror ${record.publishedPath} was edited outside the store — ingest it with \`cleo docs update\` or republish to overwrite`,
498
+ });
499
+ continue;
500
+ }
501
+ const shas = new Set([record.lastBlobSha, item.blobSha]);
502
+ const protectedElsewhere = [...shas].every((sha) => attachmentShas.has(sha) || (liveLedgerShas.get(sha) ?? 0) > 0);
503
+ const proof = provePublicationGone(checkoutRoot, record.publishedPath, absPath);
504
+ const blob = record.lastBlobSha.slice(0, 12);
505
+ let action;
506
+ let detail;
507
+ if (proof.gone === false) {
508
+ action = 'report-only';
509
+ detail = `mirror ${record.publishedPath} is missing here but ${proof.reason} — not pruned (the ledger syncs across devices)`;
510
+ }
511
+ else if (!protectedElsewhere) {
512
+ action = 'republish-or-prune';
513
+ detail = `mirror ${record.publishedPath} is gone (${proof.reason}) and this row is the only reference to blob ${blob}… — republish it, or archive the doc before pruning`;
514
+ }
515
+ else {
516
+ action = 'prune-publication';
517
+ detail = `mirror ${record.publishedPath} is gone (${proof.reason}); blob ${blob}… stays referenced elsewhere → delete the ledger row`;
518
+ }
519
+ findings.push({
520
+ record,
521
+ drift: 'deleted',
522
+ outsideRoot,
523
+ action,
524
+ prunable: action === 'prune-publication',
525
+ detail,
526
+ });
527
+ }
528
+ return findings;
529
+ }
329
530
  /**
330
531
  * Scan the store and compute diagnostics + repair inputs in one pass.
331
532
  *
@@ -335,7 +536,7 @@ async function fetchRowContent(row, payload, projectRoot) {
335
536
  *
336
537
  * @internal
337
538
  */
338
- async function scanStore(projectRoot, db, olderThanDays) {
539
+ async function scanStore(projectRoot, db, olderThanDays, checkoutRoot) {
339
540
  const rows = await db.select().from(docsAttachments).all();
340
541
  const payloadById = new Map();
341
542
  for (const row of rows)
@@ -540,6 +741,15 @@ async function scanStore(projectRoot, db, olderThanDays) {
540
741
  // planner reads the audit file, the registry and (hash-verified) bytes,
541
742
  // and writes nothing.
542
743
  const conduitBackfill = await planConduitHistoryBackfill(projectRoot);
744
+ const ledger = await scanPublicationLedger(projectRoot, rows, checkoutRoot);
745
+ const ledgerItems = ledger.slice(0, DOCS_DOCTOR_MAX_DETAIL_ITEMS).map((f) => ({
746
+ ownerId: f.record.ownerId,
747
+ blobName: f.record.blobName,
748
+ publishedPath: f.record.publishedPath,
749
+ drift: f.drift,
750
+ outsideRoot: f.outsideRoot,
751
+ proposed: f.action,
752
+ }));
543
753
  // Actionable = anything a doctor run should surface: rows to backfill,
544
754
  // unrecoverable gaps (real, unhealed drift — reported even when the doc
545
755
  // is otherwise complete), or a refused conflict (review-2253). Severity:
@@ -616,6 +826,13 @@ async function scanStore(projectRoot, db, olderThanDays) {
616
826
  items: conduitItems,
617
827
  truncated: conduitActionable.length > conduitItems.length,
618
828
  },
829
+ {
830
+ kind: 'publication-ledger-drift',
831
+ severity: ledger.length > 0 ? 'warn' : 'info',
832
+ count: ledger.length,
833
+ items: ledgerItems,
834
+ truncated: ledger.length > ledgerItems.length,
835
+ },
619
836
  ];
620
837
  return {
621
838
  diagnostics,
@@ -626,6 +843,7 @@ async function scanStore(projectRoot, db, olderThanDays) {
626
843
  missingEdges,
627
844
  needsWikilinksRebuild: missingEdges.length > 0 || backfillChangesLinks,
628
845
  conduitBackfill,
846
+ ledger,
629
847
  };
630
848
  }
631
849
  /**
@@ -668,18 +886,63 @@ export async function runDocsDoctor(projectRoot, opts = {}) {
668
886
  },
669
887
  };
670
888
  }
889
+ // T14174 — scope to some repair classes.
890
+ let only = null;
891
+ if (opts.only !== undefined) {
892
+ const valid = new Set(DOCS_DOCTOR_REPAIR_CLASSES);
893
+ const unknown = opts.only.filter((c) => !valid.has(c));
894
+ if (unknown.length > 0 || opts.only.length === 0) {
895
+ return {
896
+ ok: false,
897
+ error: {
898
+ // @sync-invariant none:input-shape an argument refusal before any repair write; nothing was written
899
+ code: 'E_DOCS_DOCTOR_UNKNOWN_CLASS',
900
+ message: `docs doctor: --only ${unknown.length > 0 ? `names unknown class(es) ${unknown.join(', ')}` : 'names no class'}; ` +
901
+ `valid classes: ${DOCS_DOCTOR_REPAIR_CLASSES.join(', ')}`,
902
+ },
903
+ };
904
+ }
905
+ if (repair) {
906
+ return {
907
+ ok: false,
908
+ error: {
909
+ // @sync-invariant none:input-shape a flag-combination refusal before any repair write; nothing was written
910
+ code: 'E_DOCS_DOCTOR_FLAG_CONFLICT',
911
+ message: 'docs doctor: --only and --repair cannot be combined — --repair is already scoped to ' +
912
+ 'the legacy-blob backfill; use `--apply --only legacy-blob-coverage` instead.',
913
+ },
914
+ };
915
+ }
916
+ only = [...new Set(opts.only)].filter((c) => valid.has(c));
917
+ }
918
+ const inScope = (kind) => only === null || only.includes(kind);
919
+ const rebuildWikilinks = inScope('wikilinks-drift') || inScope('empty-provenance');
671
920
  if (mutate) {
672
921
  const refusal = checkBackupReceipt(opts.backupReceiptPath);
673
922
  if (refusal)
674
923
  return { ok: false, error: refusal };
675
924
  }
676
925
  const db = await getDb(projectRoot);
677
- const pre = await scanStore(projectRoot, db, olderThanDays);
926
+ const checkoutRoot = opts.checkoutRoot ?? projectRoot;
927
+ try {
928
+ assertCheckoutOfProject(projectRoot, checkoutRoot);
929
+ }
930
+ catch (err) {
931
+ return {
932
+ ok: false,
933
+ error: {
934
+ // @sync-invariant none:input-shape a checkout of another repository is refused before any read or repair write
935
+ code: 'E_DOCS_DOCTOR_FOREIGN_CHECKOUT',
936
+ message: err instanceof Error ? err.message : String(err),
937
+ },
938
+ };
939
+ }
940
+ const pre = await scanStore(projectRoot, db, olderThanDays, checkoutRoot);
678
941
  const repairs = [];
679
942
  // T13653: in repair mode ONLY the blob backfill runs — apply mode plans it
680
943
  // alongside the other repairs.
681
944
  if (!repair) {
682
- for (const { row, path } of pre.dangling.archivable) {
945
+ for (const { row, path } of inScope('dangling-pointers') ? pre.dangling.archivable : []) {
683
946
  repairs.push({
684
947
  kind: 'dangling-pointers',
685
948
  action: 'archive-dangling',
@@ -689,7 +952,7 @@ export async function runDocsDoctor(projectRoot, opts = {}) {
689
952
  detail: `marked-dangling: local-file target missing (${path}) → lifecycle_status = 'archived'`,
690
953
  });
691
954
  }
692
- for (const { row, path } of pre.dangling.clearable) {
955
+ for (const { row, path } of inScope('dangling-pointers') ? pre.dangling.clearable : []) {
693
956
  repairs.push({
694
957
  kind: 'dangling-pointers',
695
958
  action: 'clear-stale-path',
@@ -700,7 +963,7 @@ export async function runDocsDoctor(projectRoot, opts = {}) {
700
963
  `(sha256 ${row.sha256.slice(0, 12)}…)`,
701
964
  });
702
965
  }
703
- for (const { row, expected } of pre.skewed) {
966
+ for (const { row, expected } of inScope('doc-version-skew') ? pre.skewed : []) {
704
967
  repairs.push({
705
968
  kind: 'doc-version-skew',
706
969
  action: 'set-doc-version',
@@ -710,7 +973,7 @@ export async function runDocsDoctor(projectRoot, opts = {}) {
710
973
  detail: `audit-note: docVersion ${row.docVersion} → ${expected} (audit log revisions + 1)`,
711
974
  });
712
975
  }
713
- for (const { row, derived } of pre.backfillable) {
976
+ for (const { row, derived } of inScope('empty-provenance') ? pre.backfillable : []) {
714
977
  const derivedCount = derived.topics.length + derived.relatedTasks.length;
715
978
  repairs.push({
716
979
  kind: 'empty-provenance',
@@ -723,7 +986,7 @@ export async function runDocsDoctor(projectRoot, opts = {}) {
723
986
  : 'no derivable topics or task mentions — columns stay NULL',
724
987
  });
725
988
  }
726
- if (pre.needsWikilinksRebuild) {
989
+ if (pre.needsWikilinksRebuild && rebuildWikilinks) {
727
990
  repairs.push({
728
991
  kind: 'wikilinks-drift',
729
992
  action: 'rebuild-wikilinks',
@@ -737,7 +1000,7 @@ export async function runDocsDoctor(projectRoot, opts = {}) {
737
1000
  // dry-run/apply mode; executed only under --apply (the --repair mode
738
1001
  // stays the T13653 blob-backfill-only fast path). A complete doc with
739
1002
  // unrecoverable gaps still gets an entry so the gap stays explained.
740
- for (const plan of pre.conduitBackfill) {
1003
+ for (const plan of inScope('conduit-history-gaps') ? pre.conduitBackfill : []) {
741
1004
  if (plan.complete && plan.gaps.length === 0)
742
1005
  continue;
743
1006
  if (plan.conflict) {
@@ -775,8 +1038,19 @@ export async function runDocsDoctor(projectRoot, opts = {}) {
775
1038
  `(chain ${plan.expectedVersions}, present ${plan.presentVersions}${gapNote}${auditNote})`,
776
1039
  });
777
1040
  }
1041
+ // T14117 — publication-ledger drift: prune protected deleted rows; every
1042
+ // other finding is a plan-only proposal.
1043
+ for (const finding of inScope('publication-ledger-drift') ? pre.ledger : []) {
1044
+ repairs.push({
1045
+ kind: 'publication-ledger-drift',
1046
+ action: finding.action,
1047
+ applied: apply && finding.prunable,
1048
+ detail: finding.detail,
1049
+ });
1050
+ }
778
1051
  }
779
- for (const candidate of pre.blobBackfillable) {
1052
+ const blobBackfillable = inScope('legacy-blob-coverage') ? pre.blobBackfillable : [];
1053
+ for (const candidate of blobBackfillable) {
780
1054
  repairs.push({
781
1055
  kind: 'legacy-blob-coverage',
782
1056
  action: 'backfill-blob-bytes',
@@ -798,7 +1072,7 @@ export async function runDocsDoctor(projectRoot, opts = {}) {
798
1072
  .map((r, i) => ({ r, i }))
799
1073
  .filter(({ r }) => r.kind === 'legacy-blob-coverage' && r.action === 'backfill-blob-bytes')
800
1074
  .map(({ i }) => i);
801
- for (const [n, candidate] of pre.blobBackfillable.entries()) {
1075
+ for (const [n, candidate] of blobBackfillable.entries()) {
802
1076
  if (candidate.sourcePath === null)
803
1077
  continue;
804
1078
  const outcome = await backfillBlobStoreBytes(cleoDir, candidate.sha256, candidate.sourcePath);
@@ -822,7 +1096,7 @@ export async function runDocsDoctor(projectRoot, opts = {}) {
822
1096
  // backfill above so the bytes the new rows address are mirrored first;
823
1097
  // annotate each repair entry with its executed outcome (zip by slug).
824
1098
  const conduitBySlug = new Map();
825
- for (const plan of pre.conduitBackfill) {
1099
+ for (const plan of inScope('conduit-history-gaps') ? pre.conduitBackfill : []) {
826
1100
  if (plan.toBackfill.length === 0 || plan.conflict)
827
1101
  continue;
828
1102
  const result = await applyConduitHistoryBackfill(projectRoot, plan);
@@ -839,14 +1113,14 @@ export async function runDocsDoctor(projectRoot, opts = {}) {
839
1113
  detail: `${repair.detail} → executed: ${outcome.inserted} row(s) inserted, ${outcome.gaps} gap(s)`,
840
1114
  };
841
1115
  }
842
- for (const { row, expected } of pre.skewed) {
1116
+ for (const { row, expected } of inScope('doc-version-skew') ? pre.skewed : []) {
843
1117
  await db
844
1118
  .update(docsAttachments)
845
1119
  .set({ docVersion: expected })
846
1120
  .where(eq(docsAttachments.id, row.id))
847
1121
  .run();
848
1122
  }
849
- for (const { row, derived } of pre.backfillable) {
1123
+ for (const { row, derived } of inScope('empty-provenance') ? pre.backfillable : []) {
850
1124
  if (derived.topics.length + derived.relatedTasks.length === 0)
851
1125
  continue;
852
1126
  await db
@@ -858,14 +1132,14 @@ export async function runDocsDoctor(projectRoot, opts = {}) {
858
1132
  .where(eq(docsAttachments.id, row.id))
859
1133
  .run();
860
1134
  }
861
- for (const { row } of pre.dangling.archivable) {
1135
+ for (const { row } of inScope('dangling-pointers') ? pre.dangling.archivable : []) {
862
1136
  await db
863
1137
  .update(docsAttachments)
864
1138
  .set({ lifecycleStatus: 'archived' })
865
1139
  .where(eq(docsAttachments.id, row.id))
866
1140
  .run();
867
1141
  }
868
- for (const { row, payload } of pre.dangling.clearable) {
1142
+ for (const { row, payload } of inScope('dangling-pointers') ? pre.dangling.clearable : []) {
869
1143
  const cleaned = { ...payload };
870
1144
  delete cleaned['path'];
871
1145
  await db
@@ -874,32 +1148,57 @@ export async function runDocsDoctor(projectRoot, opts = {}) {
874
1148
  .where(eq(docsAttachments.id, row.id))
875
1149
  .run();
876
1150
  }
877
- if (pre.needsWikilinksRebuild) {
1151
+ if (pre.needsWikilinksRebuild && rebuildWikilinks) {
878
1152
  await rebuildDocsWikilinks({ projectRoot });
879
1153
  }
1154
+ const prunable = inScope('publication-ledger-drift')
1155
+ ? pre.ledger.filter((f) => f.prunable)
1156
+ : [];
1157
+ if (prunable.length > 0) {
1158
+ const auditPath = join(projectRoot, DOCS_DOCTOR_LEDGER_PRUNE_AUDIT_FILE);
1159
+ mkdirSync(dirname(auditPath), { recursive: true });
1160
+ const prunedAt = new Date().toISOString();
1161
+ for (const finding of prunable) {
1162
+ // Audit first: the full row is recoverable even if the delete is interrupted.
1163
+ appendFileSync(auditPath, `${JSON.stringify({ op: 'docs.doctor.prune-publication', prunedAt, drift: finding.drift, outsideRoot: finding.outsideRoot, backupReceiptPath: opts.backupReceiptPath ?? null, record: finding.record })}\n`, 'utf-8');
1164
+ await deleteDocsPublication(projectRoot, finding.record);
1165
+ }
1166
+ }
880
1167
  }
881
1168
  }
882
1169
  // T13560: on a mutating run, re-scan so the envelope's diagnostics (and the
883
1170
  // summary computed from them) describe POST-repair state, consistent with
884
1171
  // a follow-up dry run. Dry runs keep the pre-repair scan.
885
- const post = mutate ? await scanStore(projectRoot, db, olderThanDays) : pre;
1172
+ const post = mutate ? await scanStore(projectRoot, db, olderThanDays, checkoutRoot) : pre;
886
1173
  const findings = post.diagnostics.reduce((sum, d) => sum + d.count, 0);
887
1174
  // T13727 — surface the conduit backfill counts in the one-line summary so
888
1175
  // operators see backfilled/skipped/gap per run without reading the report.
889
1176
  const conduitActionable = pre.conduitBackfill.filter((p) => !p.complete || p.gaps.length > 0 || p.conflict);
890
1177
  const conduitGapTotal = conduitActionable.reduce((s, p) => s + p.gaps.length, 0);
891
1178
  const conduitConflictTotal = conduitActionable.filter((p) => p.conflict).length;
892
- const conduitBackfillNote = repair || conduitActionable.length === 0
1179
+ const conduitBackfillNote = repair || !inScope('conduit-history-gaps') || conduitActionable.length === 0
893
1180
  ? ''
894
1181
  : apply
895
1182
  ? `; conduit history: ${conduitActionable.reduce((s, p) => s + p.toBackfill.length, 0)} version(s) backfilled across ${conduitActionable.length} doc(s) (${conduitGapTotal} gap(s) unrecoverable, ${conduitConflictTotal} conflict(s))`
896
1183
  : `; conduit history: ${conduitActionable.length} doc(s) need attention (${conduitActionable.reduce((s, p) => s + p.toBackfill.length, 0)} version(s) to backfill, ${conduitGapTotal} gap(s) unrecoverable, ${conduitConflictTotal} conflict(s))`;
1184
+ const repairCounts = {};
1185
+ for (const r of repairs) {
1186
+ const kind = r.kind;
1187
+ if (!isRepairClass(kind))
1188
+ continue;
1189
+ const entry = repairCounts[kind] ?? { planned: 0, applied: 0 };
1190
+ entry.planned += 1;
1191
+ if (r.applied)
1192
+ entry.applied += 1;
1193
+ repairCounts[kind] = entry;
1194
+ }
1195
+ const scopeNote = only === null ? '' : ` (only: ${only.join(', ')})`;
897
1196
  const summary = repair
898
1197
  ? `docs doctor repaired ${repairs.filter((r) => r.applied).length} blob backfill(s); ` +
899
1198
  `${post.diagnostics.find((d) => d.kind === 'legacy-blob-coverage')?.count ?? 0} sha(s) still missing post-repair`
900
1199
  : apply
901
- ? `docs doctor applied ${repairs.filter((r) => r.applied).length} repair(s); ${findings} finding(s) remain post-repair${conduitBackfillNote}`
902
- : `docs doctor found ${findings} finding(s); ${repairs.length} repair(s) planned (dry-run — pass --apply to execute)${conduitBackfillNote}`;
1200
+ ? `docs doctor applied ${repairs.filter((r) => r.applied).length} repair(s)${scopeNote}; ${findings} finding(s) remain post-repair${conduitBackfillNote}`
1201
+ : `docs doctor found ${findings} finding(s); ${repairs.length} repair(s) planned${scopeNote} (dry-run — pass --apply to execute)${conduitBackfillNote}`;
903
1202
  return {
904
1203
  ok: true,
905
1204
  report: {
@@ -908,6 +1207,8 @@ export async function runDocsDoctor(projectRoot, opts = {}) {
908
1207
  backupReceiptPath: mutate ? (opts.backupReceiptPath ?? null) : null,
909
1208
  diagnostics: post.diagnostics,
910
1209
  repairs,
1210
+ repairCounts,
1211
+ only,
911
1212
  summary,
912
1213
  },
913
1214
  };