@tequila99/githuman 0.11.0 → 0.12.0

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 (330) hide show
  1. package/README.md +62 -2
  2. package/dist/cli/commands/mcp.js +112 -0
  3. package/dist/cli/commands/pi-acp.js +21 -0
  4. package/dist/cli/config.js +7 -1
  5. package/dist/cli/index.js +19 -8
  6. package/dist/cli/server-runtime.js +1 -0
  7. package/dist/server/adapters/agents/artifact-permission.js +88 -0
  8. package/dist/server/adapters/pi-acp/agent.js +98 -0
  9. package/dist/server/adapters/pi-acp/compatibility.js +57 -0
  10. package/dist/server/adapters/pi-acp/control.js +76 -0
  11. package/dist/server/adapters/pi-acp/errors.js +17 -0
  12. package/dist/server/adapters/pi-acp/extension.js +255 -0
  13. package/dist/server/adapters/pi-acp/launch.js +16 -0
  14. package/dist/server/adapters/pi-acp/mcp.js +153 -0
  15. package/dist/server/adapters/pi-acp/process.js +199 -0
  16. package/dist/server/adapters/pi-acp/session.js +409 -0
  17. package/dist/server/adapters/pi-acp/vendor/LICENSE +21 -0
  18. package/dist/server/adapters/pi-acp/vendor/README.md +29 -0
  19. package/dist/server/adapters/pi-acp/vendor/prompt.js +56 -0
  20. package/dist/server/app.js +94 -5
  21. package/dist/server/config/agents.js +4 -6
  22. package/dist/server/db/artifact-graph-migrations.js +56 -0
  23. package/dist/server/db/artifact-migrations.js +70 -0
  24. package/dist/server/db/artifact-review-migrations.js +24 -0
  25. package/dist/server/db/artifact-search-migrations.js +31 -0
  26. package/dist/server/db/artifact-validation-migrations.js +70 -0
  27. package/dist/server/db/index.js +3 -0
  28. package/dist/server/db/migrations.js +10 -0
  29. package/dist/server/db/sqlite-error.js +15 -0
  30. package/dist/server/db/transaction.js +40 -0
  31. package/dist/server/errors/artifacts.js +28 -0
  32. package/dist/server/errors/database.js +2 -0
  33. package/dist/server/hooks/access/require-access.js +8 -0
  34. package/dist/server/repositories/artifact-catalog.repo.js +283 -0
  35. package/dist/server/repositories/artifact-files.repo.js +181 -0
  36. package/dist/server/repositories/artifact-graph.repo.js +50 -0
  37. package/dist/server/repositories/artifact-search.repo.js +192 -0
  38. package/dist/server/repositories/artifact-transition.repo.js +85 -0
  39. package/dist/server/repositories/artifact-validation.repo.js +146 -0
  40. package/dist/server/repositories/notes-catalog.repo.js +2 -10
  41. package/dist/server/repositories/task.repo.js +114 -0
  42. package/dist/server/routes/agent.js +8 -1
  43. package/dist/server/routes/artifact-actions.js +129 -0
  44. package/dist/server/routes/artifact-agent.js +183 -0
  45. package/dist/server/routes/artifact-definitions.js +163 -0
  46. package/dist/server/routes/artifact-transitions.js +80 -0
  47. package/dist/server/routes/artifacts.js +111 -0
  48. package/dist/server/routes/events.js +5 -1
  49. package/dist/server/routes/project-files.js +90 -0
  50. package/dist/server/services/agent/agent-session.js +180 -10
  51. package/dist/server/services/agent/context-builder.js +30 -2
  52. package/dist/server/services/agent/session-registry.js +106 -2
  53. package/dist/server/services/agent/turn-task-context.js +1 -0
  54. package/dist/server/services/artifact-authorization.service.js +174 -0
  55. package/dist/server/services/artifact-definition.service.js +96 -0
  56. package/dist/server/services/artifact-export.service.js +44 -0
  57. package/dist/server/services/artifact-graph-validation.js +497 -0
  58. package/dist/server/services/artifact-graph.service.js +81 -0
  59. package/dist/server/services/artifact-lifecycle.service.js +343 -0
  60. package/dist/server/services/artifact-maintenance.service.js +55 -0
  61. package/dist/server/services/artifact-search.service.js +331 -0
  62. package/dist/server/services/artifact-storage.service.js +646 -0
  63. package/dist/server/services/artifact-transition.service.js +607 -0
  64. package/dist/server/services/artifact-type.service.js +74 -0
  65. package/dist/server/services/artifact-validation-jobs.service.js +518 -0
  66. package/dist/server/services/device-connections.js +9 -1
  67. package/dist/server/services/project-files.service.js +144 -0
  68. package/dist/server/services/review.service.js +6 -5
  69. package/dist/server/services/task.service.js +225 -0
  70. package/dist/server/utils/artifact-actor.js +13 -0
  71. package/dist/server/utils/artifact-page.js +42 -0
  72. package/dist/server/utils/take-within-bytes.js +16 -0
  73. package/dist/server/utils/text.js +7 -0
  74. package/dist/shared/agents/schemas.js +27 -0
  75. package/dist/shared/app/storage-constants.js +2 -0
  76. package/dist/shared/artifact-tools/descriptions.js +11 -0
  77. package/dist/shared/artifact-tools/instructions.js +14 -0
  78. package/dist/shared/artifact-tools/schemas.js +119 -0
  79. package/dist/shared/artifact-transitions/constants.js +21 -0
  80. package/dist/shared/artifact-transitions/schemas.js +111 -0
  81. package/dist/shared/artifact-transitions/transitions.js +61 -0
  82. package/dist/shared/artifact-transitions/types.js +1 -0
  83. package/dist/shared/artifacts/constants.js +34 -0
  84. package/dist/shared/artifacts/mcp-constants.js +24 -0
  85. package/dist/shared/artifacts/normalize.js +9 -0
  86. package/dist/shared/artifacts/schemas.js +820 -0
  87. package/dist/shared/artifacts/search.js +101 -0
  88. package/dist/shared/artifacts/types.js +1 -0
  89. package/dist/shared/events/schemas.js +1 -1
  90. package/dist/shared/events/types.js +1 -0
  91. package/dist/shared/git/constants.js +2 -0
  92. package/dist/shared/project-files/constants.js +18 -0
  93. package/dist/shared/project-files/schemas.js +35 -0
  94. package/dist/shared/project-files/types.js +1 -0
  95. package/dist/shared/tasks/schemas.js +261 -0
  96. package/dist/shared/tasks/types.js +1 -0
  97. package/dist/shared/utils/artifact-body.js +10 -0
  98. package/dist/shared/utils/canonical-json.js +22 -0
  99. package/dist/shared/utils/is-safe-prefixed-filename.js +10 -0
  100. package/dist/shared/utils/is-uuid.js +4 -2
  101. package/dist/shared/utils/is-valid-artifact-type-id.js +9 -0
  102. package/dist/shared/utils/is-valid-filename-prefix.js +13 -0
  103. package/dist/shared/utils/markdown-path.js +4 -0
  104. package/dist/shared/utils/pdf-path.js +4 -0
  105. package/dist/web/assets/AccessPage-Dvj0aCkm.js +1 -0
  106. package/dist/web/assets/AddButton-CMC9qf51.js +4 -0
  107. package/dist/web/assets/AddToAgentButton-BitTdCEg.js +1 -0
  108. package/dist/web/assets/AppPage-Cozv2vqG.js +1 -0
  109. package/dist/web/assets/ArtifactDefinitionPicker-BQOoU60k.js +3 -0
  110. package/dist/web/assets/ArtifactDefinitionPicker-D3qdrxlC.css +1 -0
  111. package/dist/web/assets/ArtifactsPage-BJR-XYYs.js +2 -0
  112. package/dist/web/assets/ArtifactsPage-Cu1xdYNf.css +1 -0
  113. package/dist/web/assets/CancelButton-mkxRHwoZ.js +1 -0
  114. package/dist/web/assets/ClosePopup-BtDeDZsk.js +1 -0
  115. package/dist/web/assets/CreateButton-zCmb7snC.js +1 -0
  116. package/dist/web/assets/CreateReviewFabButton-BCg1TvFh.js +1 -0
  117. package/dist/web/assets/DialogHeader-CPL9OtDV.css +1 -0
  118. package/dist/web/assets/DialogHeader-CQvAZVx7.js +1 -0
  119. package/dist/web/assets/{ErrorNotFound-DtnO85PU.js → ErrorNotFound-fXS367lZ.js} +1 -1
  120. package/dist/web/assets/{IndexPage-D5PvyGrX.css → IndexPage-5V5GLtgi.css} +1 -1
  121. package/dist/web/assets/IndexPage-DMTZyWQA.js +1 -0
  122. package/dist/web/assets/MainLayout-DWJ_-EMV.js +7 -0
  123. package/dist/web/assets/MainLayout-Dy74m_q1.css +1 -0
  124. package/dist/web/assets/NoteVisualEditor-5rHBQ3j0.js +1054 -0
  125. package/dist/web/assets/{NoteVisualEditor-BYikrY7Q.css → NoteVisualEditor-DE4ACTRJ.css} +1 -1
  126. package/dist/web/assets/NotesPage-C_eoCUl7.css +1 -0
  127. package/dist/web/assets/NotesPage-CjaxjLBW.js +4 -0
  128. package/dist/web/assets/{PdfPreview-kL_nk9my.js → PdfPreview-2NQ5gkjh.js} +2 -2
  129. package/dist/web/assets/ProjectFilePage-B-WUUxnq.js +1 -0
  130. package/dist/web/assets/QBtn-V3VXtJXi.js +1 -0
  131. package/dist/web/assets/QFab-CxpedhPI.js +1 -0
  132. package/dist/web/assets/QForm-ClpSneeD.js +1 -0
  133. package/dist/web/assets/QInput-BnqLJGXl.js +1 -0
  134. package/dist/web/assets/QScrollArea-js5dkUz1.js +1 -0
  135. package/dist/web/assets/QSpace-C7iAnGlU.js +1 -0
  136. package/dist/web/assets/QTabs-DIy5gWMb.js +1 -0
  137. package/dist/web/assets/QVirtualScroll-DkD0TczV.js +1 -0
  138. package/dist/web/assets/ReviewDetailPage-BgxcsTqY.css +1 -0
  139. package/dist/web/assets/ReviewDetailPage-Bmc8DmSu.js +1 -0
  140. package/dist/web/assets/ReviewsPage-DR1IWCmu.js +1 -0
  141. package/dist/web/assets/SearchInput-BRyN93Z1.js +1 -0
  142. package/dist/web/assets/StatusSwitcher-CpkH8BuC.js +1 -0
  143. package/dist/web/assets/{StatusSwitcher-D6i8_OfR.css → StatusSwitcher-DaE1ufAA.css} +1 -1
  144. package/dist/web/assets/TagFilterField-Bl3FBkB5.js +1 -0
  145. package/dist/web/assets/TagFilterField-DjUH0m3_.css +1 -0
  146. package/dist/web/assets/{TerminalView-617DZuPt.css → TerminalView-BCeke4xd.css} +1 -1
  147. package/dist/web/assets/{TerminalView-BsO_SZut.js → TerminalView-tEyLM5qZ.js} +12 -12
  148. package/dist/web/assets/{abnfDiagram-O67JEVCF-qUSrlp_c.js → abnfDiagram-O67JEVCF-DN9xiV9p.js} +1 -1
  149. package/dist/web/assets/{access-store-Q8nkv4Nb.js → access-store-CwkOl71l.js} +1 -1
  150. package/dist/web/assets/{agent-store-D2jgeBgi.js → agent-store-Grbx4jkz.js} +5 -5
  151. package/dist/web/assets/architecture-7GRP2DOG-DmaYCP8T.js +1 -0
  152. package/dist/web/assets/{architectureDiagram-NJMV4G6O-D7RC6OS2.js → architectureDiagram-NJMV4G6O-BVAJOucG.js} +1 -1
  153. package/dist/web/assets/{blockDiagram-BEXU5L5S-CmNtTpfK.js → blockDiagram-BEXU5L5S-TJAfSX44.js} +1 -1
  154. package/dist/web/assets/{c4Diagram-YGBWAQC7-CKCcu1qx.js → c4Diagram-YGBWAQC7-5UHyT_tW.js} +1 -1
  155. package/dist/web/assets/channel-B4_5H-fb.js +1 -0
  156. package/dist/web/assets/{chunk-3FUC2YCW-Drz6oeKx.js → chunk-3FUC2YCW-CelgLyJi.js} +1 -1
  157. package/dist/web/assets/{chunk-5DYCD2WN-RItzEgV_.js → chunk-5DYCD2WN-bhEAVIbQ.js} +1 -1
  158. package/dist/web/assets/{chunk-742MDFTN-D6hKdOv7.js → chunk-742MDFTN-ByLz4EcD.js} +1 -1
  159. package/dist/web/assets/{chunk-7INBJB4K-Dk7-Eior.js → chunk-7INBJB4K-CK2i0csa.js} +1 -1
  160. package/dist/web/assets/{chunk-7M6MHVWA-Cw8E0Nkn.js → chunk-7M6MHVWA-CoCiujjC.js} +1 -1
  161. package/dist/web/assets/{chunk-7PRAP22T-t-mTeNXF.js → chunk-7PRAP22T-CY4NCrCY.js} +1 -1
  162. package/dist/web/assets/{chunk-GTNCS2PH-C7j2atV8.js → chunk-GTNCS2PH-DMB7bQGB.js} +1 -1
  163. package/dist/web/assets/{chunk-GWA4HPMP-CH9T9LZO.js → chunk-GWA4HPMP-F6qclPTR.js} +1 -1
  164. package/dist/web/assets/{chunk-MBY4JIJT-yXOO37xD.js → chunk-MBY4JIJT-CSiiBtlX.js} +1 -1
  165. package/dist/web/assets/{chunk-NETBCI7D-C-s2UtAR.js → chunk-NETBCI7D-CBh7GChL.js} +1 -1
  166. package/dist/web/assets/{chunk-O7XYJQB3-5Me_aV8o.js → chunk-O7XYJQB3-B2aqz0sN.js} +1 -1
  167. package/dist/web/assets/{chunk-UA2S7LBM-BQaibeff.js → chunk-UA2S7LBM-B_-P_eR0.js} +1 -1
  168. package/dist/web/assets/{chunk-WEXAMYUT-UBcOE4Lf.js → chunk-WEXAMYUT-19nMFxzK.js} +1 -1
  169. package/dist/web/assets/{chunk-Z7XXMR3K-BsA8o3Pt.js → chunk-Z7XXMR3K-Co7gzRY1.js} +1 -1
  170. package/dist/web/assets/{chunk-ZIGJFQKS--KwSI1U0.js → chunk-ZIGJFQKS-Eoi4K5bK.js} +1 -1
  171. package/dist/web/assets/{classDiagram-v2-NBCMYWYE-pMF8oCmq.js → classDiagram-v2-NBCMYWYE-CfFGzL-A.js} +1 -1
  172. package/dist/web/assets/copy-code-DaqEfdXL.js +1 -0
  173. package/dist/web/assets/create-DedXY-KP.js +1 -0
  174. package/dist/web/assets/{cynefin-OW5HDTMX-DFgtXolF.js → cynefin-OW5HDTMX-DdZXl3KU.js} +1 -1
  175. package/dist/web/assets/{cynefinDiagram-VND7K2PF-CPY0JRyq.js → cynefinDiagram-VND7K2PF-WU21nYH-.js} +1 -1
  176. package/dist/web/assets/{dagre-6A5THRUB-KdjJKAB7.js → dagre-6A5THRUB-48z3F8Pl.js} +1 -1
  177. package/dist/web/assets/date-DZIuZp-w.js +1 -0
  178. package/dist/web/assets/devices-store-Bfk2wdrQ.js +1 -0
  179. package/dist/web/assets/{diagram-22UHCM2B-BDcOcvF_.js → diagram-22UHCM2B-B0AZgJW9.js} +1 -1
  180. package/dist/web/assets/{diagram-3UASUU5V-6j2nlZL3.js → diagram-3UASUU5V-CzEzrxuO.js} +1 -1
  181. package/dist/web/assets/{diagram-ATOU4E4O-BN8cdxED.js → diagram-ATOU4E4O-w6h1bD3w.js} +1 -1
  182. package/dist/web/assets/{diagram-CDSNMT55-cWPiXHkr.js → diagram-CDSNMT55-BdTHQd_o.js} +1 -1
  183. package/dist/web/assets/{diagram-MLGK6HIB-DcgkH_Vl.js → diagram-MLGK6HIB-pB4cj6c2.js} +1 -1
  184. package/dist/web/assets/{diagram-MPIPVDR6-D_e86L99.js → diagram-MPIPVDR6-C7C06qyV.js} +1 -1
  185. package/dist/web/assets/diff-store-DBDL5Hw5.js +93 -0
  186. package/dist/web/assets/{dist-Bdsq-Ze1.js → dist-B7Cu0FBW.js} +1 -1
  187. package/dist/web/assets/{dist-BVVhgjWT.js → dist-B8i8y3GY.js} +1 -1
  188. package/dist/web/assets/{dist-CjkUzU4s.js → dist-BNgPhnKo.js} +1 -1
  189. package/dist/web/assets/{dist-Bz40Nd9I.js → dist-BWoPV5P3.js} +1 -1
  190. package/dist/web/assets/{dist-BP40mvwJ.js → dist-CJ7isxli.js} +1 -1
  191. package/dist/web/assets/{dist-C9enVshI.js → dist-CTJJYvYG.js} +1 -1
  192. package/dist/web/assets/{dist-BwpyANeW.js → dist-CZz0KXAA.js} +1 -1
  193. package/dist/web/assets/{dist-CU43ytsh.js → dist-DNpwgDba.js} +1 -1
  194. package/dist/web/assets/{dist-BMFtkY2y.js → dist-DWCdhk-O.js} +1 -1
  195. package/dist/web/assets/{dist-MUQaJRtC.js → dist-DqSUdjG9.js} +1 -1
  196. package/dist/web/assets/{dist-CLCP7f3x.js → dist-Dy9K9LOR.js} +1 -1
  197. package/dist/web/assets/{ebnfDiagram-ZINNZB2B-D8xHOFOi.js → ebnfDiagram-ZINNZB2B-75vt7539.js} +1 -1
  198. package/dist/web/assets/{elk-276RUBZZ-JiDwNyLy.js → elk-276RUBZZ-DXDjJVTy.js} +1 -1
  199. package/dist/web/assets/{erDiagram-OPXOYQCR-D3J6u81a.js → erDiagram-OPXOYQCR-tNNInTmk.js} +1 -1
  200. package/dist/web/assets/eventmodeling-NTZA5JFV-QMRehQtM.js +1 -0
  201. package/dist/web/assets/flowDiagram-KWPJA3E3-DJAgsbXC.js +1 -0
  202. package/dist/web/assets/{ganttDiagram-FUAMR5RP-nvAVhbgI.js → ganttDiagram-FUAMR5RP-h3E8lnCd.js} +1 -1
  203. package/dist/web/assets/{gitGraph-4MIJSDKK-Bv_wkB2T.js → gitGraph-4MIJSDKK-BUwY-iv2.js} +1 -1
  204. package/dist/web/assets/{gitGraphDiagram-X574FWY7-DxxwadhH.js → gitGraphDiagram-X574FWY7-C0AgQH5k.js} +1 -1
  205. package/dist/web/assets/i18n-CfTd46OX.js +1 -0
  206. package/dist/web/assets/icons-BoaZfGBj.js +1 -0
  207. package/dist/web/assets/index-BQU3KK1E.css +1 -0
  208. package/dist/web/assets/index-BTpokWxr.js +2 -0
  209. package/dist/web/assets/{info-A6RAGUB7-Dw18yeIE.js → info-A6RAGUB7-DVMZ4hdK.js} +1 -1
  210. package/dist/web/assets/{infoDiagram-VRGFBTTK-C3BrgjYg.js → infoDiagram-VRGFBTTK-kLaGXGQ2.js} +1 -1
  211. package/dist/web/assets/init-app-Bxc_l2yT.js +1 -0
  212. package/dist/web/assets/{is-D0waCELe.js → is-B1kBQ5Va.js} +1 -1
  213. package/dist/web/assets/{ishikawaDiagram-OU5B5YK6-DU2p9szX.js → ishikawaDiagram-OU5B5YK6-Oj7bZlyo.js} +1 -1
  214. package/dist/web/assets/{journeyDiagram-ZHPQQLJL-Bs2zQnew.js → journeyDiagram-ZHPQQLJL-BG7V2Srs.js} +1 -1
  215. package/dist/web/assets/{kanban-definition-PNTS6WVX-pqXd5uNi.js → kanban-definition-PNTS6WVX-CIJiDE6d.js} +1 -1
  216. package/dist/web/assets/{line-CN4Dxpei.js → line-D2vEKYRZ.js} +1 -1
  217. package/dist/web/assets/markdown-Bm2wfvok.js +16 -0
  218. package/dist/web/assets/mdi-Byw1NZrZ.js +1 -0
  219. package/dist/web/assets/{mermaid-parser.core-DZqDOGRM.js → mermaid-parser.core-BcBEWjW6.js} +3 -3
  220. package/dist/web/assets/{mermaid.core-CUndCvkz.js → mermaid.core-DobO0deo.js} +4 -4
  221. package/dist/web/assets/{mindmap-definition-NLK3R4M7-DyK7YqB7.js → mindmap-definition-NLK3R4M7-y7Qz5gBF.js} +1 -1
  222. package/dist/web/assets/{packet-AYTQ26CC-DvlW_0BP.js → packet-AYTQ26CC-oRQgKr9d.js} +1 -1
  223. package/dist/web/assets/{pegDiagram-GJSIUBJH-k2e_-FWQ.js → pegDiagram-GJSIUBJH-RIU8XOHq.js} +1 -1
  224. package/dist/web/assets/{pie-WAS4IAKB-B99zaYjO.js → pie-WAS4IAKB-qmOlsLkx.js} +1 -1
  225. package/dist/web/assets/{pieDiagram-5QR66LMP-BPbS05lK.js → pieDiagram-5QR66LMP-BM932TDA.js} +1 -1
  226. package/dist/web/assets/pinia-BfqEuvsi.js +1 -0
  227. package/dist/web/assets/pinia-Cmp1GI8k.js +1 -0
  228. package/dist/web/assets/preview-store-D7qF6SgX.js +2 -0
  229. package/dist/web/assets/project-files-store-OhGs_H7U.js +1 -0
  230. package/dist/web/assets/{quadrantDiagram-O4NWA36T-DqNNdsd2.js → quadrantDiagram-O4NWA36T-qQZYwTy7.js} +1 -1
  231. package/dist/web/assets/{radar-RG4KPBEZ-BIAAr9LO.js → radar-RG4KPBEZ-Kdglk6eY.js} +1 -1
  232. package/dist/web/assets/{railroad-74A4TZTK-DYxr-1-f.js → railroad-74A4TZTK-kVhMYx4I.js} +1 -1
  233. package/dist/web/assets/railroad-abnf-HS5TGJTU-MiKgjOXr.js +1 -0
  234. package/dist/web/assets/railroad-ebnf-LZEXJU2U-BFL9h7Zx.js +1 -0
  235. package/dist/web/assets/railroad-peg-WCYAUIDC-ChGGfIn7.js +1 -0
  236. package/dist/web/assets/{railroadDiagram-XR7U4H2S-TNg-LqRR.js → railroadDiagram-XR7U4H2S-K9SrOGcT.js} +1 -1
  237. package/dist/web/assets/render-Brao9Nj2.js +1 -0
  238. package/dist/web/assets/repository-store-DZgQ5H7J.js +1 -0
  239. package/dist/web/assets/{requirementDiagram-PLB6GJNP-hKAyJR6W.js → requirementDiagram-PLB6GJNP-CtA9ZKEx.js} +1 -1
  240. package/dist/web/assets/runtime-core.esm-bundler-DDnHUoIy.js +1 -0
  241. package/dist/web/assets/{safe-storage-B1Aq5CGi.js → safe-storage-CvVvrm4v.js} +1 -1
  242. package/dist/web/assets/{sankeyDiagram-IPEJSGJF-_MSL-tES.js → sankeyDiagram-IPEJSGJF-BH_t4exx.js} +1 -1
  243. package/dist/web/assets/{sequenceDiagram-PO4LG4MO-Byq-joBG.js → sequenceDiagram-PO4LG4MO-hPNPqpDa.js} +1 -1
  244. package/dist/web/assets/single-flight-CyDZB3rb.js +1 -0
  245. package/dist/web/assets/{stateDiagram-v2-GCMORJYK-BR2jAgaa.js → stateDiagram-v2-GCMORJYK-BVmjZDRD.js} +1 -1
  246. package/dist/web/assets/{swimlanes-2SLR337P-Nt8ojGKK.js → swimlanes-2SLR337P-Tgk8-6jj.js} +1 -1
  247. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-W1zDrK61.js +8 -0
  248. package/dist/web/assets/{terminal-store-D0abibMa.js → terminal-store-CKgJjtC6.js} +1 -1
  249. package/dist/web/assets/theme-slSxLNv6.js +1 -0
  250. package/dist/web/assets/{timeline-definition-EJHVYXUP-BtihLvxk.js → timeline-definition-EJHVYXUP-lAIHEGe8.js} +1 -1
  251. package/dist/web/assets/{treeView-Q6P3EWNA-D5HzXeKd.js → treeView-Q6P3EWNA-CdvzKtWf.js} +1 -1
  252. package/dist/web/assets/{treemap-WGGIJYW6-Dovjuutg.js → treemap-WGGIJYW6-BxDSJfrJ.js} +1 -1
  253. package/dist/web/assets/uid-B1JNhznI.js +1 -0
  254. package/dist/web/assets/use-add-agent-context-BSHGL5iV.js +1 -0
  255. package/dist/web/assets/use-app-theme-fmVV2QM8.js +1 -0
  256. package/dist/web/assets/use-countdown-D6_qSOZJ.js +1 -0
  257. package/dist/web/assets/use-notify-error-Dx2D9IEP.js +1 -0
  258. package/dist/web/assets/use-panel-width-BYOPuHat.js +1 -0
  259. package/dist/web/assets/use-router-link-D7geCGQI.js +1 -0
  260. package/dist/web/assets/use-row-height-BK8kNihW.js +1 -0
  261. package/dist/web/assets/use-server-events-CvcbsbIf.js +1 -0
  262. package/dist/web/assets/use-virtual-scroll-DixIsJk4.js +1 -0
  263. package/dist/web/assets/use-workspace-navigation-AaRMWmjQ.js +1 -0
  264. package/dist/web/assets/{usecaseDiagram-POWQR4AR-DSwRE8zj.js → usecaseDiagram-POWQR4AR-BH3KvMpz.js} +1 -1
  265. package/dist/web/assets/{vennDiagram-UO4OBE2U-C-B8yP1K.js → vennDiagram-UO4OBE2U-Dfi-raKp.js} +1 -1
  266. package/dist/web/assets/viewport-store-NqQS7itT.js +1 -0
  267. package/dist/web/assets/{vue-i18n.runtime-CEtihHHL.js → vue-i18n.runtime-JOo8b7-t.js} +2 -2
  268. package/dist/web/assets/{wardley-WFR3VGLG-wWgaZuh6.js → wardley-WFR3VGLG-DmbtSM24.js} +1 -1
  269. package/dist/web/assets/{wardleyDiagram-VNRHLVJA-DbhenrHd.js → wardleyDiagram-VNRHLVJA-DIzhGvI7.js} +1 -1
  270. package/dist/web/assets/weighted-lru-BFtZKA_y.js +1 -0
  271. package/dist/web/assets/workspace-navigation-xfxULfM7.js +1 -0
  272. package/dist/web/assets/{xychartDiagram-PMCCYNJV-CxFDmAEt.js → xychartDiagram-PMCCYNJV-XgqSHH_N.js} +1 -1
  273. package/dist/web/index.html +10 -8
  274. package/package.json +5 -3
  275. package/dist/web/assets/AccessPage-_OKn9U0P.js +0 -1
  276. package/dist/web/assets/AppPage-oQ9Rz0be.js +0 -1
  277. package/dist/web/assets/CreateButton-D1EtoicG.js +0 -1
  278. package/dist/web/assets/CreateReviewFabButton-BxvT3i9e.js +0 -1
  279. package/dist/web/assets/IndexPage-gdD7BHxA.js +0 -1
  280. package/dist/web/assets/MainLayout-47PTLKZf.js +0 -6
  281. package/dist/web/assets/MainLayout-ikSrFqBh.css +0 -1
  282. package/dist/web/assets/NoteVisualEditor-BQ6mVfj0.js +0 -1054
  283. package/dist/web/assets/NotesPage-DNR9iW_G.js +0 -4
  284. package/dist/web/assets/NotesPage-_8naQciW.css +0 -1
  285. package/dist/web/assets/PanelResize-CAQFVc8b.js +0 -1
  286. package/dist/web/assets/QBtn-CB-yw_iP.js +0 -1
  287. package/dist/web/assets/QExpansionItem-Dr5oXO0m.js +0 -4
  288. package/dist/web/assets/QFab-DPz8RiP0.js +0 -1
  289. package/dist/web/assets/QForm-CASiKhGm.js +0 -1
  290. package/dist/web/assets/QInput-k34nCPWg.js +0 -1
  291. package/dist/web/assets/QSelect-CaKn9Tkg.js +0 -1
  292. package/dist/web/assets/QSpace-Di_bZQY6.js +0 -1
  293. package/dist/web/assets/QVirtualScroll-oGN_Tl5J.js +0 -1
  294. package/dist/web/assets/ReviewDetailPage-BKgIprY0.js +0 -1
  295. package/dist/web/assets/ReviewDetailPage-CkCilMNW.css +0 -1
  296. package/dist/web/assets/ReviewsPage-6PJ6wofN.js +0 -1
  297. package/dist/web/assets/StatusSwitcher-s9w6iKpR.js +0 -1
  298. package/dist/web/assets/architecture-7GRP2DOG-D6PShS0_.js +0 -1
  299. package/dist/web/assets/channel-D6lb5DbY.js +0 -1
  300. package/dist/web/assets/create-B9Tn5Xvi.js +0 -1
  301. package/dist/web/assets/devices-store-CrZ4MxyY.js +0 -1
  302. package/dist/web/assets/diff-store-ByxMCKoa.js +0 -93
  303. package/dist/web/assets/eventmodeling-NTZA5JFV-QCwZLvmm.js +0 -1
  304. package/dist/web/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNa-Dr0goTwe.woff +0 -0
  305. package/dist/web/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ-D-x-0Q06.woff2 +0 -0
  306. package/dist/web/assets/flowDiagram-KWPJA3E3-CgF90hDi.js +0 -1
  307. package/dist/web/assets/i18n-DDCqtTTW.js +0 -1
  308. package/dist/web/assets/index-BNHt6zmP.js +0 -2
  309. package/dist/web/assets/index-BwbM_RKi.css +0 -1
  310. package/dist/web/assets/init-app-BouXTk8p.js +0 -1
  311. package/dist/web/assets/markdown-jR0fhe4d.js +0 -15
  312. package/dist/web/assets/pinia-BETEFLDG.js +0 -1
  313. package/dist/web/assets/pinia-DO0AdKGg.js +0 -1
  314. package/dist/web/assets/railroad-abnf-HS5TGJTU-GESjhc3t.js +0 -1
  315. package/dist/web/assets/railroad-ebnf-LZEXJU2U-Cybu0A7J.js +0 -1
  316. package/dist/web/assets/railroad-peg-WCYAUIDC-DCHxrtbI.js +0 -1
  317. package/dist/web/assets/runtime-core.esm-bundler-CAvPiMpd.js +0 -1
  318. package/dist/web/assets/single-flight-DLCbzLI3.js +0 -1
  319. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-Ds1hVYqh.js +0 -8
  320. package/dist/web/assets/theme-QEcOl0lf.js +0 -1
  321. package/dist/web/assets/use-add-agent-context-BaPUIO93.js +0 -1
  322. package/dist/web/assets/use-app-theme-B1cdbnHn.js +0 -1
  323. package/dist/web/assets/use-countdown-CTag4T13.js +0 -1
  324. package/dist/web/assets/use-panel-width-CB-0S3zX.js +0 -1
  325. package/dist/web/assets/use-router-link-Dr9wnnyZ.js +0 -1
  326. package/dist/web/assets/use-row-height-DQHVMtUe.js +0 -1
  327. package/dist/web/assets/use-server-events-BrBCbeHu.js +0 -1
  328. package/dist/web/assets/use-virtual-scroll-DG0UKjVk.js +0 -1
  329. package/dist/web/assets/weighted-lru-BSxRnMHR.js +0 -1
  330. /package/dist/web/assets/{PanelResize-XjrbOj2S.css → AddToAgentButton-XjrbOj2S.css} +0 -0
@@ -0,0 +1,497 @@
1
+ import { canonicalJson } from "../../shared/utils/canonical-json.js";
2
+ import { sha256Hex } from "../utils/hash.js";
3
+ import { allowedTransition } from "../../shared/artifact-transitions/transitions.js";
4
+ // Status that a lifecycle command gives to each of its subjects.
5
+ const COMMAND_STATUS = {
6
+ return_to_draft: 'draft',
7
+ reject: 'rejected',
8
+ accept: 'accepted',
9
+ archive: 'archived',
10
+ delete: 'deleted',
11
+ restore: 'draft'
12
+ };
13
+ // Each kind is a separate graph that must stay acyclic.
14
+ function edgeTargets(kind, artifact) {
15
+ if (kind === 'lineage')
16
+ return artifact.revisionOf;
17
+ return artifact.relations
18
+ .filter(relation => kind === 'hierarchy'
19
+ ? relation.kind === 'parent'
20
+ : relation.kind === 'replaces' || relation.kind === 'supplements')
21
+ .map(relation => relation.targetId);
22
+ }
23
+ // Check one written artifact against a valid store. Every new cycle contains the
24
+ // artifact, because only its own edges are new, so a walk from it finds them all.
25
+ export function artifactIssues(artifact, lookup, storeId) {
26
+ const errors = [];
27
+ const issue = (code, artifactIds, message) => errors.push({ code, artifactIds, message });
28
+ const find = (id) => (id === artifact.id ? artifact : lookup(id));
29
+ if (artifact.storeId !== storeId) {
30
+ issue('foreign_store', [artifact.id], 'Artifacts must belong to the same store.');
31
+ }
32
+ const keys = new Set();
33
+ for (const relation of artifact.relations) {
34
+ if (relation.targetId === artifact.id) {
35
+ issue('self_link', [artifact.id], 'An artifact cannot link to itself.');
36
+ }
37
+ if (!find(relation.targetId)) {
38
+ issue('missing_target', [artifact.id, relation.targetId], 'Relationship target is absent from this store.');
39
+ }
40
+ const key = `${relation.kind}:${relation.targetId}`;
41
+ if (keys.has(key)) {
42
+ issue('duplicate_relation', [artifact.id, relation.targetId], 'Duplicate relationship.');
43
+ }
44
+ keys.add(key);
45
+ }
46
+ if (artifact.revisionOf.length > 1) {
47
+ issue('invalid_lineage', [artifact.id], 'A revision has at most one predecessor.');
48
+ }
49
+ for (const parent of artifact.revisionOf) {
50
+ if (parent === artifact.id || !find(parent)) {
51
+ issue('invalid_lineage', [artifact.id, parent], 'Lineage predecessor must exist in the same store.');
52
+ }
53
+ if (find(parent)?.status === 'accepted' &&
54
+ !artifact.relations.some(r => r.targetId === parent &&
55
+ (r.kind === 'replaces' || r.kind === 'supplements'))) {
56
+ issue('missing_revision_priority', [artifact.id, parent], 'A revision of an accepted artifact must replace or supplement it.');
57
+ }
58
+ }
59
+ for (const kind of ['hierarchy', 'priority', 'lineage']) {
60
+ const visited = new Set();
61
+ const stack = [...edgeTargets(kind, artifact)];
62
+ while (stack.length) {
63
+ const id = stack.pop();
64
+ if (id === artifact.id) {
65
+ issue(`${kind}_cycle`, [artifact.id], `${kind} graph contains a cycle.`);
66
+ break;
67
+ }
68
+ if (visited.has(id))
69
+ continue;
70
+ visited.add(id);
71
+ const node = find(id);
72
+ if (node)
73
+ stack.push(...edgeTargets(kind, node));
74
+ }
75
+ }
76
+ return errors;
77
+ }
78
+ export function graphIssues(artifacts, storeId) {
79
+ const nodes = new Map(artifacts.map(artifact => [artifact.id, artifact]));
80
+ const errors = [];
81
+ const issue = (code, artifactIds, message) => errors.push({ code, artifactIds, message });
82
+ for (const artifact of artifacts) {
83
+ if (artifact.storeId !== storeId) {
84
+ issue('foreign_store', [artifact.id], 'Artifacts must belong to the same store.');
85
+ }
86
+ const keys = new Set();
87
+ for (const relation of artifact.relations) {
88
+ if (relation.targetId === artifact.id) {
89
+ issue('self_link', [artifact.id], 'An artifact cannot link to itself.');
90
+ }
91
+ if (!nodes.has(relation.targetId)) {
92
+ issue('missing_target', [artifact.id, relation.targetId], 'Relationship target is absent from this store.');
93
+ }
94
+ const key = `${relation.kind}:${relation.targetId}`;
95
+ if (keys.has(key)) {
96
+ issue('duplicate_relation', [artifact.id, relation.targetId], 'Duplicate relationship.');
97
+ }
98
+ keys.add(key);
99
+ }
100
+ if (artifact.revisionOf.length > 1) {
101
+ issue('invalid_lineage', [artifact.id], 'A revision has at most one predecessor.');
102
+ }
103
+ for (const parent of artifact.revisionOf) {
104
+ if (parent === artifact.id || !nodes.has(parent)) {
105
+ issue('invalid_lineage', [artifact.id, parent], 'Lineage predecessor must exist in the same store.');
106
+ }
107
+ if (nodes.get(parent)?.status === 'accepted' &&
108
+ !artifact.relations.some(r => r.targetId === parent &&
109
+ (r.kind === 'replaces' || r.kind === 'supplements'))) {
110
+ issue('missing_revision_priority', [artifact.id, parent], 'A revision of an accepted artifact must replace or supplement it.');
111
+ }
112
+ }
113
+ }
114
+ for (const kind of ['hierarchy', 'priority', 'lineage']) {
115
+ const color = new Map();
116
+ const next = (artifact) => edgeTargets(kind, artifact);
117
+ // Iterative DFS avoids exhausting the call stack on long revision chains.
118
+ for (const node of artifacts) {
119
+ if (color.has(node.id))
120
+ continue;
121
+ const stack = [{ id: node.id, exit: false }];
122
+ while (stack.length) {
123
+ const frame = stack.pop();
124
+ if (frame.exit) {
125
+ color.set(frame.id, 2);
126
+ continue;
127
+ }
128
+ if (color.get(frame.id) === 2)
129
+ continue;
130
+ if (color.get(frame.id) === 1) {
131
+ issue(`${kind}_cycle`, [frame.id], `${kind} graph contains a cycle.`);
132
+ continue;
133
+ }
134
+ color.set(frame.id, 1);
135
+ stack.push({ id: frame.id, exit: true });
136
+ for (const target of next(nodes.get(frame.id))) {
137
+ if (!nodes.has(target))
138
+ continue;
139
+ if (color.get(target) === 1) {
140
+ issue(`${kind}_cycle`, [frame.id, target], `${kind} graph contains a cycle.`);
141
+ }
142
+ else if (!color.has(target)) {
143
+ stack.push({ id: target, exit: false });
144
+ }
145
+ }
146
+ }
147
+ }
148
+ }
149
+ return errors;
150
+ }
151
+ function relations(items) {
152
+ return [...items]
153
+ .map(relation => ({ kind: relation.kind, targetId: relation.targetId }))
154
+ .sort((a, b) => `${a.kind}:${a.targetId}`.localeCompare(`${b.kind}:${b.targetId}`));
155
+ }
156
+ export function canonicalChangeSet(input) {
157
+ return {
158
+ ...input,
159
+ subjectIds: [...input.subjectIds].sort(),
160
+ expectedVersions: [...input.expectedVersions].sort((a, b) => a.id.localeCompare(b.id)),
161
+ relationChanges: input.relationChanges
162
+ .map(change => ({ ...change, relations: relations(change.relations) }))
163
+ .sort((a, b) => a.artifactId.localeCompare(b.artifactId)),
164
+ targetStatuses: [...input.targetStatuses].sort((a, b) => a.artifactId.localeCompare(b.artifactId)),
165
+ supplementDispositions: [...input.supplementDispositions].sort((a, b) => a.artifactId.localeCompare(b.artifactId))
166
+ };
167
+ }
168
+ export function previewArtifactChangeSet(current, input, previouslySuperseded = new Set()) {
169
+ const canonical = canonicalChangeSet(input);
170
+ const nodes = new Map(current.map(artifact => [artifact.id, artifact]));
171
+ const post = new Map(current.map(artifact => [artifact.id, structuredClone(artifact)]));
172
+ const errors = [];
173
+ const error = (code, artifactIds, message) => errors.push({ code, artifactIds, message });
174
+ if (canonical.command === 'submit_review') {
175
+ error('unsupported_command', canonical.subjectIds, 'Artifact review was removed. Create a supported lifecycle proposal.');
176
+ }
177
+ const affected = new Set(canonical.subjectIds);
178
+ const retired = new Set();
179
+ for (const collection of [
180
+ canonical.expectedVersions.map(v => v.id),
181
+ canonical.relationChanges.map(v => v.artifactId),
182
+ canonical.targetStatuses.map(v => v.artifactId),
183
+ canonical.supplementDispositions.map(v => v.artifactId)
184
+ ]) {
185
+ if (new Set(collection).size !== collection.length) {
186
+ error('duplicate_change', collection, 'A change set contains duplicate entries.');
187
+ }
188
+ }
189
+ for (const id of canonical.subjectIds) {
190
+ if (!nodes.has(id)) {
191
+ error('missing_subject', [id], 'Change set subject is absent from this store.');
192
+ }
193
+ }
194
+ for (const change of canonical.relationChanges) {
195
+ const node = post.get(change.artifactId);
196
+ if (!node) {
197
+ error('missing_target', [change.artifactId], 'Changed artifact is absent from this store.');
198
+ continue;
199
+ }
200
+ const before = nodes.get(change.artifactId);
201
+ if (before?.status === 'accepted') {
202
+ // Only the acceptance of a new document may replace an active one.
203
+ const had = new Set(before.relations
204
+ .filter(edge => edge.kind === 'replaces')
205
+ .map(edge => edge.targetId));
206
+ if (change.relations.some(edge => edge.kind === 'replaces' && !had.has(edge.targetId))) {
207
+ error('replaces_on_active_artifact', [change.artifactId], 'An accepted artifact cannot gain a replaces link.');
208
+ }
209
+ }
210
+ node.relations = structuredClone(change.relations);
211
+ affected.add(node.id);
212
+ }
213
+ if (canonical.command === 'update_relations' &&
214
+ !canonical.relationChanges.length) {
215
+ error('empty_change_set', canonical.subjectIds, 'An update_relations change set needs relation changes.');
216
+ }
217
+ const dispositionIds = new Set(canonical.supplementDispositions.map(item => item.artifactId));
218
+ for (const change of canonical.targetStatuses) {
219
+ const subject = canonical.subjectIds.includes(change.artifactId);
220
+ const allowed = subject
221
+ ? change.status === COMMAND_STATUS[canonical.command]
222
+ : canonical.command === 'accept' && change.status === 'superseded';
223
+ if (!allowed) {
224
+ error('status_not_allowed_for_command', [change.artifactId], `Command ${canonical.command} cannot set status ${change.status}.`);
225
+ }
226
+ if (dispositionIds.has(change.artifactId)) {
227
+ error('status_duplicates_disposition', [change.artifactId], 'A supplement status comes from its disposition only.');
228
+ }
229
+ }
230
+ for (const change of canonical.targetStatuses) {
231
+ const node = post.get(change.artifactId);
232
+ if (!node) {
233
+ error('missing_target', [change.artifactId], 'Changed artifact is absent from this store.');
234
+ continue;
235
+ }
236
+ if (!allowedTransition(node.status, change.status) &&
237
+ !(canonical.command === 'accept' && change.status === 'superseded')) {
238
+ error('invalid_transition', [node.id], `Transition ${node.status} to ${change.status} is not allowed.`);
239
+ }
240
+ if (canonical.subjectIds.includes(node.id) &&
241
+ ((canonical.command === 'restore' &&
242
+ !['archived', 'deleted'].includes(node.status)) ||
243
+ (canonical.command === 'return_to_draft' &&
244
+ !['in_review', 'rejected'].includes(node.status)))) {
245
+ error('invalid_transition', [node.id], 'The command does not apply to the current status.');
246
+ }
247
+ node.status = change.status;
248
+ affected.add(node.id);
249
+ }
250
+ for (const id of canonical.subjectIds) {
251
+ const node = post.get(id);
252
+ if (!node)
253
+ continue;
254
+ const expectedStatus = COMMAND_STATUS[canonical.command];
255
+ if (expectedStatus && node.status !== expectedStatus) {
256
+ error('missing_subject_status', [id], 'The command must include the subject target status.');
257
+ }
258
+ }
259
+ // Replacements and lineage retire predecessors only in the proposed poststate.
260
+ for (const node of post.values()) {
261
+ if (node.status !== 'accepted' ||
262
+ nodes.get(node.id)?.status === 'accepted') {
263
+ continue;
264
+ }
265
+ for (const relation of node.relations.filter(edge => edge.kind === 'replaces')) {
266
+ const target = post.get(relation.targetId);
267
+ if (target) {
268
+ if (nodes.get(target.id)?.status !== 'accepted' &&
269
+ (!['draft', 'in_review'].includes(nodes.get(target.id).status) ||
270
+ !isAncestor(target.id, node, nodes))) {
271
+ error('invalid_replacement', [node.id, target.id], 'Replacement target is not active or in review.');
272
+ }
273
+ target.status = 'superseded';
274
+ retired.add(target.id);
275
+ affected.add(target.id);
276
+ }
277
+ }
278
+ let parent = node.revisionOf[0];
279
+ const visited = new Set();
280
+ while (parent && !visited.has(parent)) {
281
+ visited.add(parent);
282
+ const ancestor = post.get(parent);
283
+ if (!ancestor)
284
+ break;
285
+ if (nodes.get(parent)?.status === 'draft' ||
286
+ nodes.get(parent)?.status === 'in_review') {
287
+ ancestor.status = 'superseded';
288
+ retired.add(parent);
289
+ affected.add(parent);
290
+ }
291
+ parent = ancestor.revisionOf[0];
292
+ }
293
+ }
294
+ const required = new Set();
295
+ // A replacement of a base also affects supplements of its supplements.
296
+ const inactive = new Set([...post.values()]
297
+ .filter(node => nodes.get(node.id)?.status === 'accepted' &&
298
+ node.status !== 'accepted')
299
+ .map(node => node.id));
300
+ let changed = true;
301
+ while (changed) {
302
+ changed = false;
303
+ for (const node of current) {
304
+ if (node.status === 'accepted' &&
305
+ !required.has(node.id) &&
306
+ node.relations.some(r => r.kind === 'supplements' && inactive.has(r.targetId))) {
307
+ required.add(node.id);
308
+ inactive.add(node.id);
309
+ affected.add(node.id);
310
+ changed = true;
311
+ }
312
+ }
313
+ }
314
+ const includedIn = [];
315
+ for (const id of required) {
316
+ const disposition = canonical.supplementDispositions.find(item => item.artifactId === id);
317
+ if (!disposition) {
318
+ error('missing_supplement_disposition', [id], 'Every active supplement needs an explicit disposition.');
319
+ continue;
320
+ }
321
+ const node = post.get(id);
322
+ if (disposition.disposition === 'carry_forward') {
323
+ const base = disposition.newBaseId
324
+ ? post.get(disposition.newBaseId)
325
+ : undefined;
326
+ if (!base ||
327
+ base.id === id ||
328
+ base.status !== 'accepted' ||
329
+ disposition.targetStatus) {
330
+ error('invalid_disposition', [id], 'Carry-forward requires a new accepted base.');
331
+ }
332
+ else {
333
+ node.status = 'accepted';
334
+ node.relations = node.relations.filter(relation => relation.kind !== 'supplements' || !inactive.has(relation.targetId));
335
+ if (!node.relations.some(relation => relation.kind === 'supplements' && relation.targetId === base.id)) {
336
+ node.relations.push({ kind: 'supplements', targetId: base.id });
337
+ }
338
+ affected.add(base.id);
339
+ }
340
+ }
341
+ else if (disposition.disposition === 'obsolete') {
342
+ if (!disposition.targetStatus || disposition.newBaseId) {
343
+ error('invalid_disposition', [id], 'Obsolete requires archived or deleted as target status.');
344
+ }
345
+ else {
346
+ node.status = disposition.targetStatus;
347
+ }
348
+ }
349
+ else {
350
+ if (disposition.newBaseId ||
351
+ disposition.targetStatus ||
352
+ canonical.command !== 'accept') {
353
+ error('invalid_disposition', [id], 'Incorporated is allowed only as part of acceptance.');
354
+ }
355
+ node.status = 'archived';
356
+ for (const targetId of canonical.subjectIds) {
357
+ includedIn.push({ artifactId: id, targetId });
358
+ }
359
+ }
360
+ }
361
+ for (const target of canonical.targetStatuses) {
362
+ if (target.status === 'superseded' && !retired.has(target.artifactId)) {
363
+ error('invalid_supersession', [target.artifactId], 'Supersession must result from an accepted replacement or lineage.');
364
+ }
365
+ if (post.get(target.artifactId)?.status !== target.status) {
366
+ error('conflicting_target_status', [target.artifactId], 'Explicit and derived target statuses disagree.');
367
+ }
368
+ }
369
+ for (const disposition of canonical.supplementDispositions) {
370
+ if (!required.has(disposition.artifactId)) {
371
+ error('unexpected_disposition', [disposition.artifactId], 'The supplement is not affected by this change set.');
372
+ }
373
+ }
374
+ for (const node of post.values()) {
375
+ if (node.status === 'accepted') {
376
+ for (const relation of node.relations) {
377
+ if (relation.kind === 'replaces' &&
378
+ !['superseded', 'archived', 'deleted'].includes(post.get(relation.targetId)?.status ?? '')) {
379
+ error('active_replacement_target', [node.id, relation.targetId], 'An accepted replacement requires an inactive predecessor.');
380
+ }
381
+ if (relation.kind === 'supplements' &&
382
+ post.get(relation.targetId)?.status !== 'accepted') {
383
+ error('inactive_supplement_base', [node.id, relation.targetId], 'An active supplement needs an accepted base.');
384
+ }
385
+ }
386
+ }
387
+ }
388
+ const replacements = new Map();
389
+ for (const node of post.values()) {
390
+ if (node.status !== 'accepted')
391
+ continue;
392
+ for (const relation of node.relations) {
393
+ if (relation.kind !== 'replaces')
394
+ continue;
395
+ const previous = replacements.get(relation.targetId);
396
+ if (previous && previous !== node.id) {
397
+ error('competing_replacements', [previous, node.id, relation.targetId], 'An artifact cannot have two active replacements.');
398
+ }
399
+ replacements.set(relation.targetId, node.id);
400
+ }
401
+ }
402
+ errors.push(...graphIssues([...post.values()], canonical.storeId));
403
+ // Compute the component in the union of the old and proposed graphs.
404
+ // A references target enters the set for existence checks only. It does not expand the set.
405
+ const referenceTargets = new Set();
406
+ changed = true;
407
+ while (changed) {
408
+ changed = false;
409
+ const add = (id, viaReference = false) => {
410
+ if (!nodes.has(id))
411
+ return;
412
+ if (!affected.has(id)) {
413
+ affected.add(id);
414
+ if (viaReference)
415
+ referenceTargets.add(id);
416
+ changed = true;
417
+ }
418
+ else if (!viaReference && referenceTargets.delete(id)) {
419
+ changed = true;
420
+ }
421
+ };
422
+ const expands = (id) => affected.has(id) && !referenceTargets.has(id);
423
+ const tasks = new Set([...affected]
424
+ .filter(id => !referenceTargets.has(id))
425
+ .flatMap(id => [
426
+ ...(nodes.get(id)?.taskIds ?? []),
427
+ ...(post.get(id)?.taskIds ?? [])
428
+ ]));
429
+ for (const node of [...current, ...post.values()]) {
430
+ for (const target of [
431
+ ...node.revisionOf,
432
+ ...node.relations
433
+ .filter(r => r.kind !== 'references')
434
+ .map(r => r.targetId)
435
+ ]) {
436
+ if (expands(node.id))
437
+ add(target);
438
+ if (expands(target))
439
+ add(node.id);
440
+ }
441
+ if (expands(node.id)) {
442
+ for (const relation of node.relations.filter(r => r.kind === 'references')) {
443
+ add(relation.targetId, true);
444
+ }
445
+ }
446
+ if (node.status === 'accepted' &&
447
+ (node.taskIds.length === 0 || node.taskIds.some(id => tasks.has(id)))) {
448
+ add(node.id);
449
+ }
450
+ }
451
+ }
452
+ for (const expected of canonical.expectedVersions) {
453
+ if (nodes.get(expected.id)?.metadataVersion !== expected.version) {
454
+ error('stale_version', [expected.id], 'Expected artifact version no longer matches.');
455
+ }
456
+ }
457
+ for (const id of affected) {
458
+ if (nodes.has(id) &&
459
+ !canonical.expectedVersions.some(expected => expected.id === id)) {
460
+ error('missing_expected_version', [id], 'The affected artifact needs an expected version.');
461
+ }
462
+ }
463
+ // Superseded documents can never be restored by editing a proposed status.
464
+ for (const node of post.values()) {
465
+ if ((nodes.get(node.id)?.status === 'superseded' ||
466
+ previouslySuperseded.has(node.id)) &&
467
+ ['draft', 'in_review', 'accepted'].includes(node.status)) {
468
+ error('invalid_restore', [node.id], 'A superseded artifact requires a new revision.');
469
+ }
470
+ }
471
+ return {
472
+ canonical,
473
+ changeDigest: sha256Hex(canonicalJson(canonical)),
474
+ affectedIds: [...affected].sort(),
475
+ requiredSupplementDispositions: [...required].sort(),
476
+ expectedVersions: [...affected]
477
+ .filter(id => nodes.has(id))
478
+ .sort()
479
+ .map(id => ({ id, version: nodes.get(id).metadataVersion })),
480
+ errors,
481
+ postState: [...post.values()]
482
+ .filter(node => affected.has(node.id))
483
+ .sort((a, b) => a.id.localeCompare(b.id)),
484
+ includedIn
485
+ };
486
+ }
487
+ function isAncestor(id, artifact, nodes) {
488
+ const visited = new Set();
489
+ let parent = artifact.revisionOf[0];
490
+ while (parent && !visited.has(parent)) {
491
+ if (parent === id)
492
+ return true;
493
+ visited.add(parent);
494
+ parent = nodes.get(parent)?.revisionOf[0] ?? '';
495
+ }
496
+ return false;
497
+ }
@@ -0,0 +1,81 @@
1
+ import { DEFAULT_ARTIFACT_PAGE_SIZE } from "../../shared/artifacts/constants.js";
2
+ import { Value } from '@sinclair/typebox/value';
3
+ import { ArtifactChangeSetSchema, GraphPageQuery } from "../../shared/artifacts/schemas.js";
4
+ import { ArtifactInputError } from "../errors/artifacts.js";
5
+ import { artifactPage } from "../utils/artifact-page.js";
6
+ import { previewArtifactChangeSet } from "./artifact-graph-validation.js";
7
+ export class ArtifactGraphService {
8
+ storage;
9
+ constructor(storage) {
10
+ this.storage = storage;
11
+ }
12
+ relations(id, options = {}) {
13
+ if (!Value.Check(GraphPageQuery, options)) {
14
+ throw new ArtifactInputError('Invalid graph page options.');
15
+ }
16
+ return this.storage.catalog.readSnapshot(() => this.relationsSnapshot(id, options));
17
+ }
18
+ relationsSnapshot(id, options) {
19
+ this.storage.get(id);
20
+ const direction = options.direction ?? 'outgoing';
21
+ return artifactPage(this.storage.graphRepository.edges(id, direction, options.kind), {
22
+ storeId: this.storage.storeId,
23
+ epoch: this.storage.catalog.epoch(),
24
+ query: `graph:v2:${id}:${direction}:${options.kind ?? ''}`,
25
+ limit: options.limit,
26
+ cursor: options.cursor
27
+ });
28
+ }
29
+ readDocumentContext(id) {
30
+ return this.storage.catalog.readSnapshot(() => ({
31
+ ...this.storage.readVerifiedFile(id),
32
+ storeId: this.storage.storeId,
33
+ storeEpoch: this.storage.catalog.epoch(),
34
+ outgoing: this.relationsSnapshot(id, {
35
+ direction: 'outgoing',
36
+ limit: DEFAULT_ARTIFACT_PAGE_SIZE
37
+ }),
38
+ incoming: this.relationsSnapshot(id, {
39
+ direction: 'incoming',
40
+ limit: DEFAULT_ARTIFACT_PAGE_SIZE
41
+ })
42
+ }));
43
+ }
44
+ ancestry(id, limit = 50, cursor) {
45
+ return this.storage.catalog.readSnapshot(() => {
46
+ this.storage.get(id);
47
+ const page = artifactPage(this.storage.graphRepository.ancestors(id), {
48
+ storeId: this.storage.storeId,
49
+ epoch: this.storage.catalog.epoch(),
50
+ query: `ancestry:${id}`,
51
+ limit,
52
+ cursor
53
+ });
54
+ return {
55
+ storeId: page.storeId,
56
+ storeEpoch: page.storeEpoch,
57
+ artifactId: id,
58
+ ancestorIds: page.items,
59
+ nextCursor: page.nextCursor
60
+ };
61
+ });
62
+ }
63
+ previewChangeSet(input) {
64
+ if (!Value.Check(ArtifactChangeSetSchema, input) || !input.reason.trim()) {
65
+ throw new ArtifactInputError('Invalid change set.');
66
+ }
67
+ if (input.storeId !== this.storage.storeId) {
68
+ throw new ArtifactInputError('Change set belongs to another store.');
69
+ }
70
+ return this.storage.catalog.readSnapshot(() => {
71
+ const result = previewArtifactChangeSet(this.storage.catalog.all(), input, this.storage.catalog.supersededArtifactIds());
72
+ for (const id of result.affectedIds) {
73
+ const artifact = this.storage.catalog.get(id);
74
+ if (artifact) {
75
+ this.storage.files.verify(`${this.storage.files.prefix}${id}.md`, artifact.sha256, artifact.bytes);
76
+ }
77
+ }
78
+ return result;
79
+ });
80
+ }
81
+ }