@cleocode/cleo 2026.10.27 → 2026.11.1-canary.2

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 (182) hide show
  1. package/dist/.source-stamp.json +4 -0
  2. package/dist/cli/{adapter-ZIB3OAPD.js → adapter-DG2SEXUO.js} +3 -3
  3. package/dist/cli/{add-TBJUMJW7.js → add-AMFBHFDV.js} +3 -3
  4. package/dist/cli/{add-batch-GGKVPTS5.js → add-batch-VOB72ZFB.js} +3 -3
  5. package/dist/cli/{admin-NGPG2LSO.js → admin-ENYHUNBS.js} +3 -3
  6. package/dist/cli/{admin-PEYBYMZN.js → admin-KEAEREQO.js} +15 -5
  7. package/dist/cli/{adr-2XVNDYZ7.js → adr-CBMIVBG2.js} +3 -3
  8. package/dist/cli/{analyze-A44RDCSX.js → analyze-MM2IHHTY.js} +3 -3
  9. package/dist/cli/{archive-QQB6M2RF.js → archive-6BHTE7TX.js} +3 -3
  10. package/dist/cli/{archive-stats-K4YKKYQV.js → archive-stats-ZOFY2YNK.js} +3 -3
  11. package/dist/cli/{attention-KAHDF6KD.js → attention-RE77HDUM.js} +3 -3
  12. package/dist/cli/{backup-IYFULQTM.js → backup-RIV7X622.js} +6 -5
  13. package/dist/cli/{blockers-KYNJJKZQ.js → blockers-WAI3SFV4.js} +3 -3
  14. package/dist/cli/{briefing-J6PDDZ2C.js → briefing-KZOICDXX.js} +3 -3
  15. package/dist/cli/{cancel-A4OCUVDI.js → cancel-4T3T6JT4.js} +3 -3
  16. package/dist/cli/{chunk-SXXOC6KZ.js → canon-docs-ZXWC4AFI.js} +51 -4
  17. package/dist/cli/{check-ZT6O5JDS.js → check-X5OV6U7U.js} +8 -12
  18. package/dist/cli/{check-HHT4J5ZM.js → check-XK6RXMFT.js} +3 -3
  19. package/dist/cli/{chunk-TUUKU4H7.js → chunk-5AL6B3J2.js} +7 -3
  20. package/dist/cli/{owner-override-cap-53HYZHIL.js → chunk-6C5X2J6S.js} +3 -2
  21. package/dist/cli/{chunk-PGR4RWQL.js → chunk-7YXILGEA.js} +2 -2
  22. package/dist/cli/{chunk-JRDAITPC.js → chunk-HFB4FPLA.js} +12 -12
  23. package/dist/cli/{chunk-OIS3BDSL.js → chunk-N3CEB7IU.js} +2 -2
  24. package/dist/cli/chunk-RYGY5LG4.js +89 -0
  25. package/dist/cli/{claim-MFFSWD64.js → claim-K5UMNF55.js} +3 -3
  26. package/dist/cli/{cli-5PJ2B6ZH.js → cli-EPAZQQOF.js} +3 -3
  27. package/dist/cli/{complete-2IN4YEWL.js → complete-OFIIUOEY.js} +4 -4
  28. package/dist/cli/{complexity-JVEIZE5M.js → complexity-H26U2QOX.js} +3 -3
  29. package/dist/cli/{compliance-MC3X2M34.js → compliance-VIA5PZ5R.js} +3 -3
  30. package/dist/cli/{conduit-XYYPJYUF.js → conduit-TKGCVRHB.js} +3 -3
  31. package/dist/cli/{config-Z5BXGRZB.js → config-EJ6VVCKX.js} +5 -5
  32. package/dist/cli/{consensus-T6UARQTD.js → consensus-ZSGZX7L7.js} +3 -3
  33. package/dist/cli/{context-M4F3YXZE.js → context-IBMFZA3M.js} +3 -3
  34. package/dist/cli/{contribution-76FJYJKR.js → contribution-VIZLVDNF.js} +3 -3
  35. package/dist/cli/{current-EFRCVSWL.js → current-OOPMJS6P.js} +3 -3
  36. package/dist/cli/{daemon-UAAMDN6C.js → daemon-B3YLFQHS.js} +3 -3
  37. package/dist/cli/{dash-ZEBYLYPV.js → dash-NPMLI7UK.js} +3 -3
  38. package/dist/cli/{decompose-XTK2DXSM.js → decompose-E245QWNE.js} +3 -3
  39. package/dist/cli/{decomposition-ZDACSPOU.js → decomposition-WIQ6INOS.js} +3 -3
  40. package/dist/cli/{delete-6YZADILS.js → delete-B2RR556E.js} +3 -3
  41. package/dist/cli/{deps-WTASB2GB.js → deps-FFNX2XN3.js} +3 -3
  42. package/dist/cli/{detect-IITE5EBW.js → detect-KDRXLJWQ.js} +3 -3
  43. package/dist/cli/{diagnostics-MFUOSLSQ.js → diagnostics-7CC55S3O.js} +3 -3
  44. package/dist/cli/{docs-AIZLN3ZE.js → docs-56FESINI.js} +134 -92
  45. package/dist/cli/{docs-NF5COLOB.js → docs-HOAUXIRZ.js} +133 -14
  46. package/dist/cli/{doctor-4DWCNDOP.js → doctor-4GX4L5UU.js} +9 -4
  47. package/dist/cli/{doctor-tool-locks-VXKJZKAY.js → doctor-tool-locks-3ZT2LXRY.js} +2 -2
  48. package/dist/cli/{canon-docs-2GTOLF7Y.js → doctor-type-matrix-DD233VK2.js} +8 -8
  49. package/dist/cli/{done-A7ITQSJT.js → done-QS7QSMQN.js} +73 -5
  50. package/dist/cli/{export-OTDQUAQQ.js → export-GKDTJMJQ.js} +3 -3
  51. package/dist/cli/{export-tasks-BKVXMFLD.js → export-tasks-DY2KH6PM.js} +3 -3
  52. package/dist/cli/{find-BWKYKZEN.js → find-LZS55M3I.js} +3 -3
  53. package/dist/cli/{focus-772SYK7Z.js → focus-EWBOWGC6.js} +3 -3
  54. package/dist/cli/{goal-TG74GKBK.js → goal-IIV76STG.js} +2 -2
  55. package/dist/cli/{grade-BZYBWDA7.js → grade-U7WCOFGN.js} +3 -3
  56. package/dist/cli/{graph-LB2TBRH6.js → graph-QWKRRCDD.js} +3 -3
  57. package/dist/cli/{history-XIYAXOUF.js → history-IEPFGAUQ.js} +3 -3
  58. package/dist/cli/{import-C2QISQC7.js → import-7O3BUOY4.js} +3 -3
  59. package/dist/cli/{import-tasks-P6FYXAAS.js → import-tasks-4KTKM7D5.js} +3 -3
  60. package/dist/cli/index.js +140 -103
  61. package/dist/cli/{inject-EDZVSPTW.js → inject-DADRRGXR.js} +3 -3
  62. package/dist/cli/{intelligence-OE7SIVR5.js → intelligence-QDTM2EX2.js} +3 -3
  63. package/dist/cli/{issue-S67EZXN7.js → issue-J7P2DNXC.js} +3 -3
  64. package/dist/cli/{labels-M7GIBYAQ.js → labels-MMXU55EP.js} +3 -3
  65. package/dist/cli/{lifecycle-NO5QDJ2J.js → lifecycle-FYA3POCM.js} +3 -3
  66. package/dist/cli/{list-VH6DPZ4V.js → list-KOHCHASA.js} +3 -3
  67. package/dist/cli/{llm-P35U52I5.js → llm-KOYHCBQH.js} +3 -3
  68. package/dist/cli/{log-XIPOX6UM.js → log-YP4H4AC6.js} +3 -3
  69. package/dist/cli/{manifest-A5F2YD35.js → manifest-M65CBVMQ.js} +3 -3
  70. package/dist/cli/{map-AOMU72RY.js → map-P6XIYLGM.js} +3 -3
  71. package/dist/cli/{memory-GK5WDYWT.js → memory-BJU2327X.js} +2 -2
  72. package/dist/cli/{memory-4WNZWJG7.js → memory-JQP3IPVA.js} +3 -3
  73. package/dist/cli/{migrate-claude-mem-OTSYHYBZ.js → migrate-claude-mem-7CTLN7GB.js} +3 -3
  74. package/dist/cli/{next-YUZ4X7NB.js → next-6NFF7HWH.js} +3 -3
  75. package/dist/cli/{nexus-RYH4IAG3.js → nexus-2S4ZWFAK.js} +2 -2
  76. package/dist/cli/{nexus-YHPP3RZU.js → nexus-UJPUET3K.js} +23 -15
  77. package/dist/cli/{ops-XPJK3WBN.js → ops-KE27PJVV.js} +3 -3
  78. package/dist/cli/{orchestrate-AYBDAVOQ.js → orchestrate-PQTIILHP.js} +2 -2
  79. package/dist/cli/{orchestrate-TWV43SAR.js → orchestrate-VK6MOSKX.js} +3 -3
  80. package/dist/cli/owner-override-cap-3B7UXQXA.js +29 -0
  81. package/dist/cli/{phase-CFH2NZU5.js → phase-GEX6VL47.js} +3 -3
  82. package/dist/cli/{pipeline-LW5HWLBI.js → pipeline-DUN63YOQ.js} +2 -2
  83. package/dist/cli/{pivot-LEBKDG22.js → pivot-DPRA43KL.js} +3 -3
  84. package/dist/cli/{plan-6VK5I4BT.js → plan-RGACSVCS.js} +3 -3
  85. package/dist/cli/{playbook-4GKDYZYW.js → playbook-ER2NXMCT.js} +3 -3
  86. package/dist/cli/{promote-4V4ZLAL3.js → promote-BIZDTTBH.js} +3 -3
  87. package/dist/cli/{provenance-2SFRY42O.js → provenance-FIINGMVN.js} +3 -3
  88. package/dist/cli/{provider-G6MBFVHR.js → provider-PJ7BG4LO.js} +3 -3
  89. package/dist/cli/{reason-2AYZ6SGZ.js → reason-4SH3OF2O.js} +3 -3
  90. package/dist/cli/{reconcile-7CBD7AHH.js → reconcile-ZSFETNAY.js} +3 -3
  91. package/dist/cli/{relates-FP43JAOK.js → relates-FXHIOAAM.js} +3 -3
  92. package/dist/cli/{release-NW7XHRVB.js → release-AMRAT2PE.js} +128 -3
  93. package/dist/cli/{release-OWPFPUQG.js → release-VUCVEZXA.js} +78 -3
  94. package/dist/cli/release-authorize-IV4MF6OV.js +84 -0
  95. package/dist/cli/{reorder-M457CQRR.js → reorder-MKO63DJV.js} +3 -3
  96. package/dist/cli/{reparent-53T6IUUS.js → reparent-LWWKDESX.js} +3 -3
  97. package/dist/cli/{req-ORMMU5PV.js → req-3V4FLQGJ.js} +3 -3
  98. package/dist/cli/{research-LFGCGIEP.js → research-ACQIAOKO.js} +3 -3
  99. package/dist/cli/{restore-ZYRIZQUJ.js → restore-DQDE4Q5N.js} +3 -3
  100. package/dist/cli/review-2VXZEKW6.js +131 -0
  101. package/dist/cli/{roadmap-EK6LXBQ4.js → roadmap-RJP3DGOE.js} +3 -3
  102. package/dist/cli/{run-PZJCIRFD.js → run-SUK5J7SL.js} +42 -2
  103. package/dist/cli/{safestop-NO57J3RK.js → safestop-A55EIVTT.js} +3 -3
  104. package/dist/cli/{saga-NIJMIJE5.js → saga-D5ZHKWK3.js} +3 -3
  105. package/dist/cli/{selfimprove-6HSEV3RG.js → selfimprove-3RFOSFEY.js} +3 -3
  106. package/dist/cli/{selfimprove-IYKG5PF4.js → selfimprove-V3J63HFW.js} +2 -2
  107. package/dist/cli/{sequence-T42KPEEQ.js → sequence-67LSEZXS.js} +3 -3
  108. package/dist/cli/{service-CBEZWGHC.js → service-3HF5T3FK.js} +3 -3
  109. package/dist/cli/{session-TFVW42XK.js → session-NZI7KLAG.js} +2 -2
  110. package/dist/cli/{session-XDADM7O4.js → session-O7WQZ3FC.js} +3 -3
  111. package/dist/cli/{show-I2TEU6MP.js → show-ZGPWZ5K7.js} +3 -3
  112. package/dist/cli/{skills-ZBYRPFRL.js → skills-QEDUL5OK.js} +3 -3
  113. package/dist/cli/{snapshot-GMN7LZ4A.js → snapshot-2L56HYDV.js} +3 -3
  114. package/dist/cli/{start-FQWDRGJE.js → start-7LUSXQIY.js} +3 -3
  115. package/dist/cli/{stats-D2AU77GH.js → stats-QMT2U6UV.js} +3 -3
  116. package/dist/cli/{sticky-FKZFQYNV.js → sticky-P7ZQNJOY.js} +3 -3
  117. package/dist/cli/{stop-ZCL7RZPH.js → stop-7PQWX343.js} +3 -3
  118. package/dist/cli/{sync-WM4JLGXF.js → sync-BRLFSZLJ.js} +3 -3
  119. package/dist/cli/{tasks-54WNYQZS.js → tasks-AMX5BNT6.js} +3 -3
  120. package/dist/cli/{tasks-RU6AAJKD.js → tasks-RTGNQOSH.js} +2 -2
  121. package/dist/cli/{testing-S7FCPHW5.js → testing-WKWDA2VL.js} +3 -3
  122. package/dist/cli/{token-BDRGEO3E.js → token-ECWD4DBT.js} +3 -3
  123. package/dist/cli/{update-BD77ORMO.js → update-AJYEZD5C.js} +3 -3
  124. package/dist/cli/{verify-VOJRQQE2.js → verify-XLJ7RTFB.js} +4 -4
  125. package/dist/cli/{worktree-2HHJT5BS.js → worktree-AFGKXSPR.js} +3 -3
  126. package/package.json +16 -13
  127. package/studio-dist/client/_app/immutable/chunks/{CwQ8nAoh.js → Btz0tQqj.js} +1 -1
  128. package/studio-dist/client/_app/immutable/chunks/CPIhpiGj.js +1 -0
  129. package/studio-dist/client/_app/immutable/chunks/{Js_lZuOv.js → D6qKHbA2.js} +1 -1
  130. package/studio-dist/client/_app/immutable/chunks/{Dv66614U.js → RdpDuuJu.js} +1 -1
  131. package/studio-dist/client/_app/immutable/entry/{app.C2oQiIiz.js → app.DT5LX-9S.js} +2 -2
  132. package/studio-dist/client/_app/immutable/entry/start.Xnwi6D3w.js +1 -0
  133. package/studio-dist/client/_app/immutable/nodes/{0.DaE6eVaG.js → 0.Cu0XAzuX.js} +1 -1
  134. package/studio-dist/client/_app/immutable/nodes/{1.D0Mb3i_7.js → 1.Bo7-qoq9.js} +1 -1
  135. package/studio-dist/client/_app/immutable/nodes/{15.ChRO-4dz.js → 15.CeLncxci.js} +1 -1
  136. package/studio-dist/client/_app/immutable/nodes/{16.fltF2wse.js → 16.ECAaVAgD.js} +1 -1
  137. package/studio-dist/client/_app/immutable/nodes/{17.DnYjMhyj.js → 17.DmaoWxRR.js} +1 -1
  138. package/studio-dist/client/_app/immutable/nodes/{18.DYVIAC9a.js → 18.DxeHl5QX.js} +1 -1
  139. package/studio-dist/client/_app/immutable/nodes/{20.CVBcbjBW.js → 20.COjwnX-m.js} +1 -1
  140. package/studio-dist/client/_app/immutable/nodes/{24.Bfswm8Y5.js → 24.CkYJs7KO.js} +1 -1
  141. package/studio-dist/client/_app/immutable/nodes/25.tPoDyB6K.js +39 -0
  142. package/studio-dist/client/_app/immutable/nodes/{26.D3RoTHoD.js → 26.CYF2cRZS.js} +1 -1
  143. package/studio-dist/client/_app/immutable/nodes/{3.ezhkaJvt.js → 3.085mGQcP.js} +1 -1
  144. package/studio-dist/client/_app/version.json +1 -1
  145. package/studio-dist/server/chunks/{0-BKQLYEeE.js → 0-CMBufzu-.js} +3 -3
  146. package/studio-dist/server/chunks/1-CtcXfgeh.js +9 -0
  147. package/studio-dist/server/chunks/{15-DuSvkmiZ.js → 15-3YhTbrvk.js} +3 -3
  148. package/studio-dist/server/chunks/{16-DJVePzvS.js → 16-CSecxIh7.js} +3 -3
  149. package/studio-dist/server/chunks/{17-D8K9-EPJ.js → 17-CeIfhqmH.js} +3 -3
  150. package/studio-dist/server/chunks/{18-D5FUPGnv.js → 18-DQjD_dVK.js} +3 -3
  151. package/studio-dist/server/chunks/{20-CmHgNZFV.js → 20-BcSZ8IZV.js} +3 -3
  152. package/studio-dist/server/chunks/{24-CfuHWHgd.js → 24-BdooKWYf.js} +3 -3
  153. package/studio-dist/server/chunks/{25-bm3BDSum.js → 25-C7Mrt_ZY.js} +201 -6
  154. package/studio-dist/server/chunks/{26-DRlhAbrf.js → 26-BZvP7L0W.js} +3 -3
  155. package/studio-dist/server/chunks/{3-BrE1-gTA.js → 3-OZioTEgm.js} +3 -3
  156. package/studio-dist/server/chunks/{_layout.svelte-BeqNMCvk.js → _layout.svelte-DD9NUkp3.js} +4 -4
  157. package/studio-dist/server/chunks/{_page.svelte-CJVF_ySt.js → _page.svelte-BaRJl2wA.js} +3 -3
  158. package/studio-dist/server/chunks/{_page.svelte-DM-MP-Vl.js → _page.svelte-C132GXE8.js} +3 -3
  159. package/studio-dist/server/chunks/{_page.svelte-Ds0GMs_q.js → _page.svelte-C1OCI1HJ.js} +4 -4
  160. package/studio-dist/server/chunks/{_page.svelte-DnmZYECG.js → _page.svelte-CkCxe4CP.js} +3 -3
  161. package/studio-dist/server/chunks/{_page.svelte-V7BTZinR.js → _page.svelte-D74SMLO2.js} +6 -6
  162. package/studio-dist/server/chunks/{_page.svelte-B7xxx6BP.js → _page.svelte-DokPLKF3.js} +3 -3
  163. package/studio-dist/server/chunks/{_page.svelte-DIx7Hcw-.js → _page.svelte-X5UYq87e.js} +3 -3
  164. package/studio-dist/server/chunks/{_page.svelte-CkXCx1iz.js → _page.svelte-kmz8T3zl.js} +3 -3
  165. package/studio-dist/server/chunks/{_page.svelte-Bchzzf-O.js → _page.svelte-tr7JTALw.js} +3 -3
  166. package/studio-dist/server/chunks/{_server.ts-Ck-oreiA.js → _server.ts-BBKnsrJQ.js} +3 -3
  167. package/studio-dist/server/chunks/{_server.ts-Cd_KHxRr.js → _server.ts-BPhofOwJ.js} +2 -2
  168. package/studio-dist/server/chunks/{_server.ts-b2YIpIsi.js → _server.ts-CYudVzjN.js} +3 -3
  169. package/studio-dist/server/chunks/{_server.ts-DdCviCsN.js → _server.ts-CuZllzdA.js} +3 -3
  170. package/studio-dist/server/chunks/{_server.ts-YtGAClvW.js → _server.ts-DdRUhbRy.js} +3 -3
  171. package/studio-dist/server/chunks/{client-DR3j81J8.js → client-Bzj70t1H.js} +2 -2
  172. package/studio-dist/server/chunks/{error.svelte-NY-X_Qdt.js → error.svelte-CtgEIltc.js} +3 -3
  173. package/studio-dist/server/chunks/{http-Dz72j56i.js → http-DhvqcYGu.js} +2 -2
  174. package/studio-dist/server/chunks/{internal-y7btLoII.js → internal-jhq9gXru.js} +2 -2
  175. package/studio-dist/server/chunks/{operations-registry-DKEHmggR.js → operations-registry-DggYPK_D.js} +177 -1
  176. package/studio-dist/server/chunks/{stores-Cm-Qj76R.js → stores-CdLD7i4a.js} +2 -2
  177. package/studio-dist/server/index.js +1 -1
  178. package/studio-dist/server/manifest.js +17 -17
  179. package/studio-dist/client/_app/immutable/chunks/BXnpxR_0.js +0 -1
  180. package/studio-dist/client/_app/immutable/entry/start.BolRa540.js +0 -1
  181. package/studio-dist/client/_app/immutable/nodes/25.B_7VqHJ6.js +0 -39
  182. package/studio-dist/server/chunks/1-ClOr3JN3.js +0 -9
@@ -25,14 +25,11 @@ import {
25
25
  dispatchFromCli,
26
26
  dispatchRaw,
27
27
  handleRawError
28
- } from "./chunk-OIS3BDSL.js";
28
+ } from "./chunk-N3CEB7IU.js";
29
29
  import "./chunk-6N6WGLXE.js";
30
30
  import {
31
31
  resolve
32
32
  } from "./chunk-4VASFLUW.js";
33
- import {
34
- loadCanonRegistry
35
- } from "./chunk-SXXOC6KZ.js";
36
33
  import {
37
34
  UnknownFlagError,
38
35
  assertKnownFlags
@@ -49,11 +46,11 @@ import {
49
46
  } from "./chunk-ESI6Q4OS.js";
50
47
  import "./chunk-6JSJNYH2.js";
51
48
  import "./chunk-D7BAQNV2.js";
52
- import "./chunk-JRDAITPC.js";
49
+ import "./chunk-HFB4FPLA.js";
53
50
 
54
51
  // packages/cleo/src/cli/commands/docs.ts
55
52
  import { existsSync } from "node:fs";
56
- import { appendFile, mkdir as mkdir3, readdir, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
53
+ import { mkdir as mkdir3, readdir, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
57
54
  import { dirname as dirname3, isAbsolute, join as join4, resolve as resolve3 } from "node:path";
58
55
  import {
59
56
  DocKindConfigError,
@@ -67,11 +64,6 @@ import {
67
64
  resolveWorktreeRouting
68
65
  } from "@cleocode/core/core-paths";
69
66
  import { createDocsReadModel } from "@cleocode/core/docs/docs-read-model";
70
- import {
71
- checkSlugSimilarity,
72
- DEFAULT_SIMILARITY_MODE,
73
- DEFAULT_SIMILARITY_THRESHOLD
74
- } from "@cleocode/core/docs/similarity-check";
75
67
  import { CleoError as CleoError2 } from "@cleocode/core/errors";
76
68
  import { pushWarning } from "@cleocode/core/output";
77
69
  import { getProjectRoot as getProjectRoot3 } from "@cleocode/core/project-scope";
@@ -291,6 +283,113 @@ var migrateCommand = defineCommand({
291
283
  }
292
284
  });
293
285
 
286
+ // packages/cleo/src/cli/commands/docs/modernize.ts
287
+ var modernizeCommand = defineCommand({
288
+ meta: {
289
+ name: "modernize",
290
+ description: "Turn a messy docs folder into clean, linked CleoDocs (T14113). Dry run by default: per file a kind, a stable slug (no task, session or date tokens) and keep/rename/merge-into/delete with reasons; ambiguous classify and merge calls go to System One (cleo decide) with a rule fallback. --apply stores docs, merges duplicates, records supersession, moves files and writes wikilink edges, with a receipt under .cleo/modernize/; --rollback <runId> reverses a run."
291
+ },
292
+ args: {
293
+ path: {
294
+ type: "positional",
295
+ description: "Folder to modernize (required unless --rollback or --plan)",
296
+ required: false
297
+ },
298
+ apply: {
299
+ type: "boolean",
300
+ description: "Execute the plan (default: dry run, nothing written)"
301
+ },
302
+ plan: {
303
+ type: "string",
304
+ description: "Apply this saved plan JSON verbatim (with --apply)"
305
+ },
306
+ out: {
307
+ type: "string",
308
+ description: "Save the plan JSON to this path for review"
309
+ },
310
+ rollback: {
311
+ type: "string",
312
+ description: "Roll back the apply run with this id (from the receipt)"
313
+ },
314
+ exclude: {
315
+ type: "string",
316
+ description: "Extra folder names to skip, comma-separated"
317
+ },
318
+ overrides: {
319
+ type: "string",
320
+ description: "Reviewer decisions JSON keyed by path; they win over rules and System One"
321
+ },
322
+ kind: {
323
+ type: "string",
324
+ description: "Use this kind for every file instead of classifying (bulk ingest)"
325
+ },
326
+ "allow-dirty": {
327
+ type: "boolean",
328
+ description: "Apply even when the root is not a clean git checkout (default: refuse)"
329
+ },
330
+ store: {
331
+ type: "boolean",
332
+ description: "Modernize the docs already in the store: re-kind, re-slug (old slugs stay as aliases), renumber colliding ADRs"
333
+ },
334
+ "store-only": {
335
+ type: "boolean",
336
+ description: "Ingest every file into the store (task-owned) and leave the folder untouched"
337
+ }
338
+ },
339
+ async run({ args }) {
340
+ await dispatchFromCli(
341
+ "mutate",
342
+ "docs",
343
+ "modernize",
344
+ {
345
+ ...typeof args.path === "string" ? { path: args.path } : {},
346
+ apply: args.apply === true,
347
+ ...typeof args.plan === "string" ? { planFile: args.plan } : {},
348
+ ...typeof args.out === "string" ? { out: args.out } : {},
349
+ ...typeof args.rollback === "string" ? { rollback: args.rollback } : {},
350
+ ...typeof args.exclude === "string" ? { exclude: args.exclude } : {},
351
+ ...typeof args.overrides === "string" ? { overrides: args.overrides } : {},
352
+ ...typeof args.kind === "string" ? { kind: args.kind } : {},
353
+ storeOnly: args["store-only"] === true,
354
+ allowDirty: args["allow-dirty"] === true,
355
+ store: args.store === true
356
+ },
357
+ { command: "docs modernize" }
358
+ );
359
+ }
360
+ });
361
+
362
+ // packages/cleo/src/cli/commands/docs/rot.ts
363
+ var rotCommand = defineCommand({
364
+ meta: {
365
+ name: "rot",
366
+ description: "Docs-rot report from git alone (T14111): docs whose covers: code changed after them (stale), duplicate docs, and orphaned docs (dead covers globs, unreferenced docs/ files). --changed <base>..<head> also lists the docs a merged change left stale; --strict exits 2 on any finding."
367
+ },
368
+ args: {
369
+ changed: {
370
+ type: "string",
371
+ description: "Git range <base>..<head> of a merged change to flag docs for"
372
+ },
373
+ strict: {
374
+ type: "boolean",
375
+ description: "Exit 2 when the report finds stale, duplicate, orphaned or flagged docs"
376
+ }
377
+ },
378
+ async run({ args }) {
379
+ const params = typeof args.changed === "string" ? { changed: args.changed } : {};
380
+ if (args.strict !== true) {
381
+ await dispatchFromCli("query", "docs", "rot", params, { command: "docs rot" });
382
+ return;
383
+ }
384
+ const response = await dispatchRaw("query", "docs", "rot", params);
385
+ handleRawError(response, { command: "docs rot", operation: "docs.rot" });
386
+ const report = response.data;
387
+ cliOutput(report, { command: "docs rot", message: report.summary });
388
+ const findings = report.stale.length + report.duplicates.length + report.orphans.length + report.flagged.length;
389
+ if (findings > 0) process.exit(2);
390
+ }
391
+ });
392
+
294
393
  // packages/cleo/src/cli/commands/docs/set-alias.ts
295
394
  import { ExitCode as ExitCode3 } from "@cleocode/contracts/exit-codes.js";
296
395
  import { setDisplayAlias } from "@cleocode/core/docs/display-alias";
@@ -1272,7 +1371,7 @@ async function runGapCheck(_projectRoot, filterId) {
1272
1371
  var addCommand = defineCommand({
1273
1372
  meta: {
1274
1373
  name: "add",
1275
- description: 'Usage: cleo docs add <OWNER-ID> [<file>] [--content <text> | --url <url>] [--slug <kebab>] [--type <kind>]\n OWNER-ID is the FIRST positional; the file is the SECOND. Examples:\n cleo docs add T123 ./docs/rfc.md --slug my-rfc --type spec\n cleo docs add T123 --content "# Note" --slug my-note\n\nAttach a local file or remote URL to a CLEO entity (task, session, observation). Owner type is inferred from the ID prefix: T### \u2192 task, ses_* \u2192 session, O-* \u2192 observation. Use --slug to set a human-friendly alias (unique per project) (T9636).\n\nPositional arguments:\n <owner-id> Owner entity ID (T###, ses_*, O-*) \u2014 required\n [file] Local file path to attach \u2014 optional when --url/--content is set\n\nNamed arguments:\n --url <url> Remote URL to attach (instead of a local file)\n --content <text> Inline document body \u2014 author without a file (T10965); \'-\' reads stdin\n --desc <text> Free-text description of this attachment\n --labels <csv> Comma-separated labels (e.g. rfc,spec)\n --attached-by <name> Agent identity that created the attachment (default: "human")\n --slug <kebab> Human-friendly alias, unique per project (T9636)\n --title <text> Human-readable title \u2014 REQUIRED for --type adr when --slug is omitted (T10360)\n --type <kind> Taxonomy classification \u2014 run `cleo docs schema` for kinds\n --allow-similar Bypass the slug-similarity warn \u2014 every bypass is audited\n to .cleo/audit/similar-bypass.jsonl (T10361)\n --strict Enforce body-schema (requiredSections) \u2014 fail with\n E_DOC_SCHEMA_MISMATCH instead of warning (T10160)\n' + docsOutputFlagHelp + "\n\nValidation behaviors:\n \u2022 Unknown flags \u2192 E_UNKNOWN_FLAG with did-you-mean suggestions (T10359)\n \u2022 Slug collision \u2192 E_SLUG_RESERVED + 3 alternative slugs (T10386)\n \u2022 Near-duplicate slug \u2192 W_SLUG_SIMILAR warning unless --allow-similar (T10361)\n \u2022 For --type adr without --slug, slug auto-allocates as `adr-NNN-<kebab-title>` via the\n central allocator (T10360 \u2014 closes T10153). --title is required in this case."
1374
+ description: 'Usage: cleo docs add <OWNER-ID> [<file>] [--content <text> | --url <url>] [--slug <kebab>] [--type <kind>]\n OWNER-ID is the FIRST positional; the file is the SECOND. Examples:\n cleo docs add T123 ./docs/rfc.md --slug my-rfc --type spec\n cleo docs add T123 --content "# Note" --slug my-note\n\nAttach a local file or remote URL to a CLEO entity (task, session, observation). Owner type is inferred from the ID prefix: T### \u2192 task, ses_* \u2192 session, O-* \u2192 observation. Use --slug to set a human-friendly alias (unique per project) (T9636).\n\nPositional arguments:\n <owner-id> Owner entity ID (T###, ses_*, O-*) \u2014 required\n [file] Local file path to attach \u2014 optional when --url/--content is set\n\nNamed arguments:\n --url <url> Remote URL to attach (instead of a local file)\n --content <text> Inline document body \u2014 author without a file (T10965); \'-\' reads stdin\n --desc <text> Free-text description of this attachment\n --labels <csv> Comma-separated labels (e.g. rfc,spec)\n --attached-by <name> Agent identity that created the attachment (default: "human")\n --slug <kebab> Human-friendly alias, unique per project (T9636)\n --title <text> Human-readable title \u2014 REQUIRED for --type adr when --slug is omitted (T10360)\n --type <kind> Taxonomy classification \u2014 run `cleo docs schema` for kinds\n --new-because <why> Add a doc that resembles an existing one; the reason is\n recorded in .cleo/audit/docs-new-because.jsonl (T14109)\n --strict Enforce body-schema (requiredSections) \u2014 fail with\n E_DOC_SCHEMA_MISMATCH instead of warning (T10160)\n' + docsOutputFlagHelp + "\n\nValidation behaviors:\n \u2022 Unknown flags \u2192 E_UNKNOWN_FLAG with did-you-mean suggestions (T10359)\n \u2022 Slug collision \u2192 E_SLUG_RESERVED + 3 alternative slugs (T10386)\n \u2022 Likely duplicate (same content, or similar slug, title or text) \u2192 E_DOC_DUPLICATE,\n naming the existing doc, `cleo docs fetch <slug>` and `cleo docs update <slug>`,\n unless --new-because <why> (T14109)\n \u2022 For --type adr without --slug, slug auto-allocates as `adr-NNN-<kebab-title>` via the\n central allocator (T10360 \u2014 closes T10153). --title is required in this case."
1276
1375
  },
1277
1376
  args: {
1278
1377
  "owner-id": {
@@ -1317,9 +1416,13 @@ var addCommand = defineCommand({
1317
1416
  type: "string",
1318
1417
  description: "Taxonomy classification \u2014 run `cleo docs schema` to enumerate registered kinds (T9637 / T9788 / T11142)"
1319
1418
  },
1419
+ "new-because": {
1420
+ type: "string",
1421
+ description: "Why this doc is new although it resembles an existing one (T14109). A likely duplicate (same content, or a similar slug, title or text) is refused with E_DOC_DUPLICATE and routed to `cleo docs update <slug>` unless this states the reason (15+ characters); the reason is recorded in .cleo/audit/docs-new-because.jsonl."
1422
+ },
1320
1423
  "allow-similar": {
1321
1424
  type: "boolean",
1322
- description: "Bypass the T10361 slug-similarity check. Use when you really do mean to add a new doc with a near-duplicate slug (e.g. intentional fork). Every bypass is logged to .cleo/audit/similar-bypass.jsonl."
1425
+ description: "Deprecated (T14109): needs --new-because <reason>, which is what lets a likely duplicate through."
1323
1426
  },
1324
1427
  strict: {
1325
1428
  type: "boolean",
@@ -1399,83 +1502,17 @@ var addCommand = defineCommand({
1399
1502
  process.exit(ExitCode6.FILE_ERROR);
1400
1503
  }
1401
1504
  }
1402
- if (args.slug && args.type) {
1403
- const projectRoot = await getProjectRoot3();
1404
- let warnThreshold = DEFAULT_SIMILARITY_THRESHOLD;
1405
- let mode = DEFAULT_SIMILARITY_MODE;
1406
- try {
1407
- const canon = loadCanonRegistry(projectRoot);
1408
- if (canon?.similarity) {
1409
- warnThreshold = canon.similarity.warnThreshold;
1410
- mode = canon.similarity.mode;
1411
- }
1412
- } catch {
1413
- }
1414
- try {
1415
- const sim = await checkSlugSimilarity({
1416
- slug: args.slug,
1417
- type: args.type,
1418
- projectRoot,
1419
- threshold: warnThreshold
1420
- });
1421
- if (sim.mostSimilarSlug !== null) {
1422
- const scoreFixed = sim.score.toFixed(2);
1423
- const hint = `Similar to '${sim.mostSimilarSlug}' (score ${scoreFixed}) \u2014 did you mean: cleo docs update ${sim.mostSimilarSlug}? Pass --allow-similar to bypass.`;
1424
- if (mode === "block" && !allowSimilar) {
1425
- cliError(hint, ExitCode6.VALIDATION_ERROR, {
1426
- name: "E_SLUG_SIMILARITY",
1427
- fix: `Use \`cleo docs update ${sim.mostSimilarSlug}\` if updating, or pass --allow-similar to add as a new doc.`,
1428
- alternatives: [
1429
- {
1430
- action: `update '${sim.mostSimilarSlug}' instead`,
1431
- command: `cleo docs update ${sim.mostSimilarSlug}`
1432
- },
1433
- {
1434
- action: "bypass the similarity check",
1435
- command: `cleo docs add ${ownerId} ${fileArg ?? `--url ${url}`} --slug ${args.slug} --type ${args.type} --allow-similar`
1436
- }
1437
- ],
1438
- details: {
1439
- proposedSlug: args.slug,
1440
- mostSimilarSlug: sim.mostSimilarSlug,
1441
- score: sim.score,
1442
- threshold: warnThreshold,
1443
- kind: args.type
1444
- }
1445
- });
1446
- process.exit(ExitCode6.VALIDATION_ERROR);
1447
- }
1448
- humanInfo(hint);
1449
- if (allowSimilar) {
1450
- const auditLine = `${JSON.stringify({
1451
- ts: (/* @__PURE__ */ new Date()).toISOString(),
1452
- reason: "allow-similar-bypass",
1453
- proposedSlug: args.slug,
1454
- mostSimilarSlug: sim.mostSimilarSlug,
1455
- score: sim.score,
1456
- threshold: warnThreshold,
1457
- kind: args.type,
1458
- ownerId
1459
- })}
1460
- `;
1461
- try {
1462
- await mkdir3(join4(projectRoot, ".cleo", "audit"), { recursive: true });
1463
- await appendFile(
1464
- join4(projectRoot, ".cleo", "audit", "similar-bypass.jsonl"),
1465
- auditLine,
1466
- "utf-8"
1467
- );
1468
- } catch {
1469
- }
1470
- }
1471
- }
1472
- } catch (err) {
1473
- if (process.env["CLEO_DEBUG"]) {
1474
- humanInfo(
1475
- `similarity check skipped: ${err instanceof Error ? err.message : String(err)}`
1476
- );
1505
+ const newBecause = args["new-because"];
1506
+ if (allowSimilar && typeof newBecause !== "string") {
1507
+ cliError(
1508
+ "--allow-similar no longer bypasses the duplicate check on its own; say why this doc is new",
1509
+ 6,
1510
+ {
1511
+ name: "E_VALIDATION",
1512
+ fix: 'Pass --new-because "<why this is not the existing doc>" (recorded in .cleo/audit/docs-new-because.jsonl), or update the existing doc with cleo docs update <slug>.'
1477
1513
  }
1478
- }
1514
+ );
1515
+ process.exit(6);
1479
1516
  }
1480
1517
  await dispatchFromCli(
1481
1518
  "mutate",
@@ -1492,7 +1529,8 @@ var addCommand = defineCommand({
1492
1529
  ...args.slug ? { slug: args.slug } : {},
1493
1530
  ...args.title ? { title: args.title } : {},
1494
1531
  ...args.type ? { type: args.type } : {},
1495
- ...args.strict === true ? { strict: true } : {}
1532
+ ...args.strict === true ? { strict: true } : {},
1533
+ ...typeof newBecause === "string" ? { newBecause } : {}
1496
1534
  },
1497
1535
  { command: "docs add" }
1498
1536
  );
@@ -2562,7 +2600,7 @@ function parseOlderThanDays(raw) {
2562
2600
  var doctorCommand = defineCommand({
2563
2601
  meta: {
2564
2602
  name: "doctor",
2565
- description: "Docs store health diagnostics + safe repairs: dangling local-file pointers, docVersion skew vs the audit log, empty topics/related_tasks provenance, docs_wikilinks drift, legacy store surfaces, blob-store coverage, stale drafts. Dry-run by default; --apply executes repairs (a fresh backup is created automatically first); --repair runs only the hash-verified legacy-bytes \u2192 blob-store backfill (T13653)."
2603
+ description: "Docs store health diagnostics + safe repairs: dangling local-file pointers, docVersion skew vs the audit log, empty topics/related_tasks provenance, docs_wikilinks drift, legacy store surfaces, blob-store coverage, stale drafts, publication-ledger drift (rows whose mirror is gone or stale; T14117). Dry-run by default; --apply executes repairs (a fresh backup is created automatically first); --repair runs only the hash-verified legacy-bytes \u2192 blob-store backfill (T13653)."
2566
2604
  },
2567
2605
  args: {
2568
2606
  apply: {
@@ -2875,8 +2913,12 @@ var docsCommand = defineCommand({
2875
2913
  triage: triageCommand,
2876
2914
  // T13363 — docs.stats adoption metrics (read-only)
2877
2915
  stats: statsCommand,
2916
+ // T14111 — docs.rot stale/duplicate/orphaned report (read-only, git only)
2917
+ rot: rotCommand,
2878
2918
  // T13360 — docs.migrate legacy docs import (harness-bracketed, idempotent)
2879
2919
  migrate: migrateCommand,
2920
+ // T14113 — docs.modernize (dry run / apply / rollback)
2921
+ modernize: modernizeCommand,
2880
2922
  // Drift status — agent-facing, kept per cleodocs-rearch-design §6 (T13361 retired gap-check only)
2881
2923
  status: statusCommand,
2882
2924
  // T9645 reingest — retained: the docs-reingest workflow invokes `docs sync --from/--for/--name`
@@ -2897,4 +2939,4 @@ export {
2897
2939
  _llmOutputCommand,
2898
2940
  docsCommand
2899
2941
  };
2900
- //# sourceMappingURL=docs-AIZLN3ZE.js.map
2942
+ //# sourceMappingURL=docs-56FESINI.js.map
@@ -15,7 +15,7 @@
15
15
  })();
16
16
  import {
17
17
  lazyOperation
18
- } from "./chunk-JRDAITPC.js";
18
+ } from "./chunk-HFB4FPLA.js";
19
19
  import {
20
20
  defineTypedHandler,
21
21
  lafsError,
@@ -29,7 +29,7 @@ import {
29
29
  } from "./chunk-XD25OCUE.js";
30
30
 
31
31
  // packages/cleo/src/dispatch/domains/docs.ts
32
- import { randomUUID } from "node:crypto";
32
+ import { createHash, randomUUID } from "node:crypto";
33
33
  import { readFile } from "node:fs/promises";
34
34
  import { join, resolve } from "node:path";
35
35
  import { DocKindRegistry } from "@cleocode/contracts/docs-taxonomy.js";
@@ -75,6 +75,12 @@ var allocateAdrSlug = lazyOperation(
75
75
  var allocateAutoSlugForDispatch = lazyOperation(
76
76
  async () => (await import("@cleocode/core/docs/numbering")).allocateAutoSlugForDispatch
77
77
  );
78
+ var docNameViolation = lazyOperation(
79
+ async () => (await import("@cleocode/core/docs/doc-name-rule")).docNameViolation
80
+ );
81
+ var describeDocNameViolation = lazyOperation(
82
+ async () => (await import("@cleocode/core/docs/doc-name-rule")).describeDocNameViolation
83
+ );
78
84
  var exportDocument = lazyOperation(
79
85
  async () => (await import("@cleocode/core/docs/export-document")).exportDocument
80
86
  );
@@ -130,6 +136,12 @@ var triageDocs = lazyOperation(
130
136
  var computeDocsStats = lazyOperation(
131
137
  async () => (await import("@cleocode/core/docs/stats")).computeDocsStats
132
138
  );
139
+ var runDocsModernize = lazyOperation(
140
+ async () => (await import("@cleocode/core/docs/modernize/index")).runDocsModernize
141
+ );
142
+ var scanDocsRot = lazyOperation(
143
+ async () => (await import("@cleocode/core/docs/rot")).scanDocsRot
144
+ );
133
145
  var runLegacyDocMigration = lazyOperation(
134
146
  async () => (await import("@cleocode/core/docs/legacy-doc-migration")).runLegacyDocMigration
135
147
  );
@@ -284,12 +296,9 @@ var _docsTypedHandler = defineTypedHandler("docs", {
284
296
  const model = createDocsReadModel();
285
297
  const backend = await currentAttachmentBackend();
286
298
  if (isProjectScope) {
287
- const docs = await model.listProjectDocs({
288
- kind: typeFilter,
289
- limit: Number.isFinite(effectiveLimit) ? effectiveLimit : void 0
290
- });
299
+ const docs = await model.listProjectDocs({ kind: typeFilter });
291
300
  const totalCount = docs.length;
292
- const projected = docs.map((doc) => ({
301
+ const allProjected = docs.map((doc) => ({
293
302
  id: doc.id,
294
303
  // Full content hash — a SHA-256 field in a machine envelope must be the
295
304
  // real digest, never a display-truncated form (T11294: the per-doc
@@ -312,12 +321,13 @@ var _docsTypedHandler = defineTypedHandler("docs", {
312
321
  ownerId: doc.ownerId,
313
322
  ownerType: doc.ownerType
314
323
  }));
315
- projected.sort(
324
+ allProjected.sort(
316
325
  (a, b) => comparator(
317
326
  { sha256: a.sha256, slug: a.slug, createdAt: a.createdAt },
318
327
  { sha256: b.sha256, slug: b.slug, createdAt: b.createdAt }
319
328
  )
320
329
  );
330
+ const projected = Number.isFinite(effectiveLimit) ? allProjected.slice(0, effectiveLimit) : allProjected;
321
331
  const truncated = totalCount > projected.length;
322
332
  if (truncated) {
323
333
  hintParts.push(
@@ -332,6 +342,12 @@ var _docsTypedHandler = defineTypedHandler("docs", {
332
342
  ...typeFilter !== void 0 ? { type: typeFilter } : {},
333
343
  count: projected.length,
334
344
  ...truncated ? { totalCount } : {},
345
+ population: {
346
+ matched: totalCount,
347
+ returned: projected.length,
348
+ truncated,
349
+ limit: Number.isFinite(effectiveLimit) ? effectiveLimit : null
350
+ },
335
351
  limit: Number.isFinite(effectiveLimit) ? effectiveLimit : 0,
336
352
  orderBy,
337
353
  ...hintParts.length > 0 ? { hint: hintParts.join(" ") } : {},
@@ -381,6 +397,12 @@ var _docsTypedHandler = defineTypedHandler("docs", {
381
397
  ...typeFilter !== void 0 ? { type: typeFilter } : {},
382
398
  count: slicedOwner.length,
383
399
  ...truncatedOwner ? { totalCount: totalCountOwner } : {},
400
+ population: {
401
+ matched: totalCountOwner,
402
+ returned: slicedOwner.length,
403
+ truncated: truncatedOwner,
404
+ limit: Number.isFinite(effectiveLimit) ? effectiveLimit : null
405
+ },
384
406
  limit: Number.isFinite(effectiveLimit) ? effectiveLimit : 0,
385
407
  orderBy,
386
408
  ...hintParts.length > 0 ? { hint: hintParts.join(" ") } : {},
@@ -616,6 +638,28 @@ var _docsTypedHandler = defineTypedHandler("docs", {
616
638
  "migrate"
617
639
  );
618
640
  },
641
+ // ── docs.modernize (T14113 — messy docs folder → linked CleoDocs) ─────────
642
+ modernize: async (params) => {
643
+ let result;
644
+ try {
645
+ result = await runDocsModernize(getProjectRoot(), {
646
+ ...params,
647
+ exclude: params.exclude?.split(",").map((e) => e.trim()).filter(Boolean)
648
+ });
649
+ } catch (err) {
650
+ const code = err instanceof Error && "code" in err ? String(err.code) : "";
651
+ if (!code.startsWith("E_MODERNIZE")) throw err;
652
+ return lafsError(
653
+ code,
654
+ // @sync-invariant none:input-shape a stale plan, missing root or unknown receipt is refused; the receipt holds any earlier step
655
+ err instanceof Error ? err.message : code,
656
+ "modernize"
657
+ );
658
+ }
659
+ const counts = result.plan?.counts;
660
+ const summary = result.mode === "rollback" ? `rolled back ${result.receipt?.runId} (${result.receipt?.ops.length ?? 0} steps reversed)` : (result.plan?.mode === "store" ? `${result.plan.storeRows?.length ?? 0} stored docs to re-kind or re-slug` : `${result.plan?.rows.length ?? 0} files: ${counts?.keep ?? 0} keep, ${counts?.rename ?? 0} rename, ${counts?.["merge-into"] ?? 0} merge, ${counts?.delete ?? 0} delete`) + (result.receipt ? `; applied as ${result.receipt.runId} (${result.receipt.ops.length} steps, ${result.receipt.edgeCount} graph edges)` : "; dry run, nothing written");
661
+ return lafsSuccess({ ...result, summary }, "modernize");
662
+ },
619
663
  // ── docs.add ───────────────────────────────────────────────────────────────
620
664
  add: async (params) => {
621
665
  const {
@@ -629,7 +673,8 @@ var _docsTypedHandler = defineTypedHandler("docs", {
629
673
  slug: rawSlug,
630
674
  title: rawTitle,
631
675
  type: rawType,
632
- strict: strictMode
676
+ strict: strictMode,
677
+ newBecause
633
678
  } = params;
634
679
  if (!ownerId) {
635
680
  return lafsError("E_INVALID_INPUT", "ownerId is required", "add");
@@ -841,6 +886,71 @@ var _docsTypedHandler = defineTypedHandler("docs", {
841
886
  );
842
887
  }
843
888
  }
889
+ if (slug !== void 0) {
890
+ const nameCheck = await docNameViolation(slug, type);
891
+ if (nameCheck !== null) {
892
+ if (adrNumber !== void 0) releaseReservedSlug(slug);
893
+ return lafsError(
894
+ "E_DOC_NAME_RULE",
895
+ // @sync-invariant none:input-shape a slug that breaks the doc naming rule is refused before anything is written
896
+ await describeDocNameViolation(slug, nameCheck),
897
+ "add",
898
+ `cleo docs add ... --slug ${nameCheck.suggestion}`,
899
+ { suggestion: nameCheck.suggestion, reasons: nameCheck.reasons }
900
+ );
901
+ }
902
+ }
903
+ {
904
+ const reason = typeof newBecause === "string" ? newBecause.trim() : void 0;
905
+ const dup = await import("@cleocode/core/docs/duplicate-check");
906
+ if (reason !== void 0 && reason.length < dup.NEW_BECAUSE_MIN_LENGTH) {
907
+ if (adrNumber !== void 0 && slug !== void 0) releaseReservedSlug(slug);
908
+ return lafsError(
909
+ "E_INVALID_INPUT",
910
+ // @sync-invariant none:input-shape a too-short --new-because reason is refused before anything is written
911
+ `--new-because needs a reason of at least ${dup.NEW_BECAUSE_MIN_LENGTH} characters saying why this is not an existing doc`,
912
+ "add"
913
+ );
914
+ }
915
+ const bytes = typeof inlineContent === "string" ? Buffer.from(inlineContent, "utf-8") : filePath ? await readFile(resolve(String(filePath))).catch(() => null) : null;
916
+ const text = bytes?.toString("utf-8");
917
+ const title = (typeof rawTitle === "string" && rawTitle.trim() ? rawTitle : null) ?? (text ? dup.markdownTitle(text) : null);
918
+ const match = type === void 0 ? null : dup.findDuplicateDoc(
919
+ {
920
+ ...slug !== void 0 ? { slug } : {},
921
+ ...type !== void 0 ? { kind: type } : {},
922
+ ...title !== null ? { title } : {},
923
+ ...text !== void 0 ? { content: text } : {},
924
+ ...bytes ? { sha256: createHash("sha256").update(bytes).digest("hex") } : {}
925
+ },
926
+ await dup.loadDuplicateCandidates(projectRoot, { kind: type }),
927
+ await dup.readDuplicateThresholds(projectRoot)
928
+ );
929
+ if (match && reason === void 0) {
930
+ if (adrNumber !== void 0 && slug !== void 0) releaseReservedSlug(slug);
931
+ const { message, fix } = dup.describeDuplicate(match);
932
+ return lafsError(
933
+ "E_DOC_DUPLICATE",
934
+ // @sync-invariant none:input-shape a likely duplicate doc is refused before anything is written
935
+ message,
936
+ "add",
937
+ fix,
938
+ {
939
+ existing: { slug: match.slug, kind: match.kind, title: match.title },
940
+ rule: match.rule,
941
+ score: match.score
942
+ }
943
+ );
944
+ }
945
+ if (match && reason !== void 0) {
946
+ await dup.recordNewBecause(projectRoot, {
947
+ slug: slug ?? null,
948
+ kind: type ?? null,
949
+ resembles: match,
950
+ reason
951
+ });
952
+ }
953
+ }
844
954
  if (slug !== void 0 && adrNumber === void 0) {
845
955
  const reservation = await reserveSlugForDispatch(projectRoot, {
846
956
  kind: type ?? "",
@@ -1541,7 +1651,8 @@ var QUERY_OPS = /* @__PURE__ */ new Set([
1541
1651
  "audit",
1542
1652
  "doctor",
1543
1653
  "triage",
1544
- "stats"
1654
+ "stats",
1655
+ "rot"
1545
1656
  ]);
1546
1657
  var MUTATE_OPS = /* @__PURE__ */ new Set([
1547
1658
  "add",
@@ -1555,7 +1666,8 @@ var MUTATE_OPS = /* @__PURE__ */ new Set([
1555
1666
  "sync",
1556
1667
  "import",
1557
1668
  "doctor",
1558
- "migrate"
1669
+ "migrate",
1670
+ "modernize"
1559
1671
  ]);
1560
1672
  async function dispatchDocsLegacyQuery(operation, params) {
1561
1673
  const projectRoot = getProjectRoot();
@@ -1625,6 +1737,11 @@ async function dispatchDocsLegacyQuery(operation, params) {
1625
1737
  });
1626
1738
  case "stats":
1627
1739
  return computeDocsStats({ projectRoot });
1740
+ case "rot":
1741
+ return scanDocsRot({
1742
+ projectRoot,
1743
+ ...typeof params["changed"] === "string" ? { changed: params["changed"] } : {}
1744
+ });
1628
1745
  default:
1629
1746
  throw new Error(`Unsupported docs query operation: ${operation}`);
1630
1747
  }
@@ -1855,7 +1972,8 @@ var DocsHandler = class {
1855
1972
  "audit",
1856
1973
  "doctor",
1857
1974
  "triage",
1858
- "stats"
1975
+ "stats",
1976
+ "rot"
1859
1977
  ],
1860
1978
  mutate: [
1861
1979
  "add",
@@ -1869,7 +1987,8 @@ var DocsHandler = class {
1869
1987
  "sync",
1870
1988
  "import",
1871
1989
  "doctor",
1872
- "migrate"
1990
+ "migrate",
1991
+ "modernize"
1873
1992
  ]
1874
1993
  };
1875
1994
  }
@@ -1877,4 +1996,4 @@ var DocsHandler = class {
1877
1996
  export {
1878
1997
  DocsHandler
1879
1998
  };
1880
- //# sourceMappingURL=docs-NF5COLOB.js.map
1999
+ //# sourceMappingURL=docs-HOAUXIRZ.js.map
@@ -27,10 +27,13 @@ import {
27
27
  } from "./chunk-RBI6IBDF.js";
28
28
  import {
29
29
  doctorToolLocksCommand
30
- } from "./chunk-TUUKU4H7.js";
30
+ } from "./chunk-5AL6B3J2.js";
31
31
  import {
32
32
  doctorTwinCollapseCommand
33
33
  } from "./chunk-5IFMSDWF.js";
34
+ import {
35
+ doctorTypeMatrixCommand
36
+ } from "./chunk-RYGY5LG4.js";
34
37
  import {
35
38
  doctorWorktreeStoresCommand
36
39
  } from "./chunk-T2TE45UH.js";
@@ -129,7 +132,7 @@ import {
129
132
  import {
130
133
  dispatchFromCli,
131
134
  dispatchRaw
132
- } from "./chunk-OIS3BDSL.js";
135
+ } from "./chunk-N3CEB7IU.js";
133
136
  import "./chunk-6N6WGLXE.js";
134
137
  import "./chunk-4VASFLUW.js";
135
138
  import {
@@ -143,7 +146,7 @@ import {
143
146
  } from "./chunk-ESI6Q4OS.js";
144
147
  import "./chunk-6JSJNYH2.js";
145
148
  import "./chunk-D7BAQNV2.js";
146
- import "./chunk-JRDAITPC.js";
149
+ import "./chunk-HFB4FPLA.js";
147
150
 
148
151
  // packages/cleo/src/cli/commands/doctor.ts
149
152
  import { mkdirSync, writeFileSync } from "node:fs";
@@ -552,6 +555,8 @@ var doctorCommand = defineCommand({
552
555
  "ac-bindings": doctorAcBindingsCommand,
553
556
  // T12886 — stored task dependency cycles + repair plan (read-only)
554
557
  "dep-cycles": doctorDepCyclesCommand,
558
+ // T14096 — parent/child type-matrix violations: plan, --apply with a receipt, --rollback
559
+ "type-matrix": doctorTypeMatrixCommand,
555
560
  // T12158 — tables nexus resolves by bare name must actually live where it assumes
556
561
  "nexus-residency": doctorNexusResidencyCommand,
557
562
  // T12113 (gh#1222) — evidence-tool semaphore holders + orphan reaping
@@ -1280,4 +1285,4 @@ Dependency cycles: check skipped (${msg})`);
1280
1285
  export {
1281
1286
  doctorCommand
1282
1287
  };
1283
- //# sourceMappingURL=doctor-4DWCNDOP.js.map
1288
+ //# sourceMappingURL=doctor-4GX4L5UU.js.map
@@ -15,7 +15,7 @@
15
15
  })();
16
16
  import {
17
17
  doctorToolLocksCommand
18
- } from "./chunk-TUUKU4H7.js";
18
+ } from "./chunk-5AL6B3J2.js";
19
19
  import "./chunk-VIZSF3NL.js";
20
20
  import "./chunk-IVN46UST.js";
21
21
  import "./chunk-ESI6Q4OS.js";
@@ -23,4 +23,4 @@ import "./chunk-6JSJNYH2.js";
23
23
  export {
24
24
  doctorToolLocksCommand
25
25
  };
26
- //# sourceMappingURL=doctor-tool-locks-VXKJZKAY.js.map
26
+ //# sourceMappingURL=doctor-tool-locks-3ZT2LXRY.js.map
@@ -14,13 +14,13 @@
14
14
  };
15
15
  })();
16
16
  import {
17
- CanonDocsDiffError,
18
- loadCanonRegistry,
19
- runCanonDocsCheck
20
- } from "./chunk-SXXOC6KZ.js";
17
+ doctorTypeMatrixCommand
18
+ } from "./chunk-RYGY5LG4.js";
19
+ import "./chunk-VIZSF3NL.js";
20
+ import "./chunk-IVN46UST.js";
21
+ import "./chunk-ESI6Q4OS.js";
22
+ import "./chunk-6JSJNYH2.js";
21
23
  export {
22
- CanonDocsDiffError,
23
- loadCanonRegistry,
24
- runCanonDocsCheck
24
+ doctorTypeMatrixCommand
25
25
  };
26
- //# sourceMappingURL=canon-docs-2GTOLF7Y.js.map
26
+ //# sourceMappingURL=doctor-type-matrix-DD233VK2.js.map