@graphrefly/ts 0.0.1 → 0.0.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 (330) hide show
  1. package/dist/actions-types-CSC3NMDl.d.ts +657 -0
  2. package/dist/actions-types-wl9Mgkmr.d.cts +657 -0
  3. package/dist/adapters/index.cjs +5157 -2951
  4. package/dist/adapters/index.cjs.map +1 -1
  5. package/dist/adapters/index.d.cts +207 -70
  6. package/dist/adapters/index.d.ts +207 -70
  7. package/dist/adapters/index.js +3738 -1688
  8. package/dist/adapters/index.js.map +1 -1
  9. package/dist/adapters/nestjs/microservices.cjs +841 -0
  10. package/dist/adapters/nestjs/microservices.cjs.map +1 -0
  11. package/dist/adapters/nestjs/microservices.d.cts +45 -0
  12. package/dist/adapters/nestjs/microservices.d.ts +45 -0
  13. package/dist/adapters/nestjs/microservices.js +206 -0
  14. package/dist/adapters/nestjs/microservices.js.map +1 -0
  15. package/dist/adapters/nestjs/native.cjs +1391 -0
  16. package/dist/adapters/nestjs/native.cjs.map +1 -0
  17. package/dist/adapters/nestjs/native.d.cts +111 -0
  18. package/dist/adapters/nestjs/native.d.ts +111 -0
  19. package/dist/adapters/nestjs/native.js +577 -0
  20. package/dist/adapters/nestjs/native.js.map +1 -0
  21. package/dist/adapters/nestjs/websockets.cjs +924 -0
  22. package/dist/adapters/nestjs/websockets.cjs.map +1 -0
  23. package/dist/adapters/nestjs/websockets.d.cts +47 -0
  24. package/dist/adapters/nestjs/websockets.d.ts +47 -0
  25. package/dist/adapters/nestjs/websockets.js +286 -0
  26. package/dist/adapters/nestjs/websockets.js.map +1 -0
  27. package/dist/adapters/nestjs.cjs +1085 -0
  28. package/dist/adapters/nestjs.cjs.map +1 -0
  29. package/dist/adapters/nestjs.d.cts +385 -0
  30. package/dist/adapters/nestjs.d.ts +385 -0
  31. package/dist/adapters/nestjs.js +112 -0
  32. package/dist/adapters/nestjs.js.map +1 -0
  33. package/dist/adapters/observe-storage.d.cts +3 -3
  34. package/dist/adapters/observe-storage.d.ts +3 -3
  35. package/dist/adapters/observe-storage.js +4 -4
  36. package/dist/adapters/react.cjs.map +1 -1
  37. package/dist/adapters/react.d.cts +2 -2
  38. package/dist/adapters/react.d.ts +2 -2
  39. package/dist/adapters/react.js +1 -1
  40. package/dist/adapters/solid.cjs.map +1 -1
  41. package/dist/adapters/solid.d.cts +2 -2
  42. package/dist/adapters/solid.d.ts +2 -2
  43. package/dist/adapters/solid.js +1 -1
  44. package/dist/adapters/svelte.cjs.map +1 -1
  45. package/dist/adapters/svelte.d.cts +2 -2
  46. package/dist/adapters/svelte.d.ts +2 -2
  47. package/dist/adapters/svelte.js +1 -1
  48. package/dist/adapters/vue.cjs.map +1 -1
  49. package/dist/adapters/vue.d.cts +2 -2
  50. package/dist/adapters/vue.d.ts +2 -2
  51. package/dist/adapters/vue.js +1 -1
  52. package/dist/{agent-runtime-DZTJ3C-p.d.cts → agent-runtime-types-core-CaXfKNtY.d.cts} +171 -320
  53. package/dist/{agent-runtime-BesGDZcB.d.ts → agent-runtime-types-core-hU299uIC.d.ts} +171 -320
  54. package/dist/agent-runtime-types-tool-BEpSINol.d.ts +695 -0
  55. package/dist/agent-runtime-types-tool-DninPEzk.d.cts +695 -0
  56. package/dist/agentic-memory-retention-BmGX368S.d.ts +14 -0
  57. package/dist/agentic-memory-retention-Ih1qhX9Z.d.cts +14 -0
  58. package/dist/{agentic-memory-BmRCvh0d.d.ts → agentic-memory-types-CVSUS6XR.d.ts} +13 -57
  59. package/dist/{agentic-memory-DfuVZ-lY.d.cts → agentic-memory-types-FnnYlVUs.d.cts} +13 -57
  60. package/dist/{capability-admission-g0mn9hpq.d.ts → capability-admission-BW2CziT6.d.ts} +3 -3
  61. package/dist/{capability-admission-B0BzqAm5.d.cts → capability-admission-lwSecxXs.d.cts} +3 -3
  62. package/dist/chunk-2KUAG2GZ.js +9693 -0
  63. package/dist/chunk-2KUAG2GZ.js.map +1 -0
  64. package/dist/chunk-2NJP6GO4.js +1320 -0
  65. package/dist/chunk-2NJP6GO4.js.map +1 -0
  66. package/dist/{chunk-YDSIVHRR.js → chunk-4NWSNI4K.js} +1598 -1584
  67. package/dist/chunk-4NWSNI4K.js.map +1 -0
  68. package/dist/{chunk-Z2ANQ6JW.js → chunk-4Q77MMJY.js} +2 -2
  69. package/dist/chunk-6E76H4OU.js +132 -0
  70. package/dist/chunk-6E76H4OU.js.map +1 -0
  71. package/dist/chunk-7GOTIIQ7.js +819 -0
  72. package/dist/chunk-7GOTIIQ7.js.map +1 -0
  73. package/dist/{chunk-IXZBUWSP.js → chunk-7HZRJMN6.js} +2 -2
  74. package/dist/{chunk-SS2A464I.js → chunk-CJIFGJQG.js} +3 -3
  75. package/dist/{chunk-V7CIJE4B.js → chunk-CX6B6PO2.js} +92 -9
  76. package/dist/chunk-CX6B6PO2.js.map +1 -0
  77. package/dist/{chunk-FBUU43PM.js → chunk-E4QCH32M.js} +448 -446
  78. package/dist/chunk-E4QCH32M.js.map +1 -0
  79. package/dist/{chunk-HKK2JD7Z.js → chunk-ECURQ3HD.js} +12 -125
  80. package/dist/chunk-ECURQ3HD.js.map +1 -0
  81. package/dist/{chunk-3JR3EDB2.js → chunk-EOKT4QFF.js} +121 -115
  82. package/dist/chunk-EOKT4QFF.js.map +1 -0
  83. package/dist/{chunk-K3FXTC3I.js → chunk-HJACH5PU.js} +3 -3
  84. package/dist/{chunk-GTPCOEOZ.js → chunk-IDMLXLLG.js} +3 -3
  85. package/dist/{chunk-DKP56CSW.js → chunk-JKAYLDQV.js} +2 -2
  86. package/dist/{chunk-4VLAQHZI.js → chunk-K7FB3JTD.js} +17 -1
  87. package/dist/chunk-K7FB3JTD.js.map +1 -0
  88. package/dist/{chunk-H75VXTGT.js → chunk-L6LDXRWE.js} +3 -3
  89. package/dist/{chunk-H75VXTGT.js.map → chunk-L6LDXRWE.js.map} +1 -1
  90. package/dist/{chunk-YXMZ2N6J.js → chunk-M7J6Z4IJ.js} +1071 -1025
  91. package/dist/chunk-M7J6Z4IJ.js.map +1 -0
  92. package/dist/chunk-MQEQMS7B.js +1009 -0
  93. package/dist/chunk-MQEQMS7B.js.map +1 -0
  94. package/dist/chunk-MTELVIYR.js +1421 -0
  95. package/dist/chunk-MTELVIYR.js.map +1 -0
  96. package/dist/{chunk-QFY4AFY4.js → chunk-PVC7WDKI.js} +58 -52
  97. package/dist/chunk-PVC7WDKI.js.map +1 -0
  98. package/dist/{chunk-VTHLCFN4.js → chunk-RAHNUYA5.js} +2 -2
  99. package/dist/{chunk-XKXWK2KP.js → chunk-SHZDP2MQ.js} +1157 -1021
  100. package/dist/chunk-SHZDP2MQ.js.map +1 -0
  101. package/dist/{chunk-CZNV3VXM.js → chunk-SIDJO2T4.js} +11 -11
  102. package/dist/chunk-STXRMPW4.js +4131 -0
  103. package/dist/chunk-STXRMPW4.js.map +1 -0
  104. package/dist/{chunk-2747WN4Y.js → chunk-TPND5MJS.js} +3 -3
  105. package/dist/chunk-TQPCAIQI.js +612 -0
  106. package/dist/chunk-TQPCAIQI.js.map +1 -0
  107. package/dist/{chunk-43BKLFZU.js → chunk-YDWBXXQZ.js} +10 -10
  108. package/dist/{chunk-6PHB5CJS.js → chunk-ZLFRB6FI.js} +2 -2
  109. package/dist/composition/index.cjs +1228 -1076
  110. package/dist/composition/index.cjs.map +1 -1
  111. package/dist/composition/index.d.cts +3 -3
  112. package/dist/composition/index.d.ts +3 -3
  113. package/dist/composition/index.js +4 -4
  114. package/dist/core/index.cjs +1198 -1046
  115. package/dist/core/index.cjs.map +1 -1
  116. package/dist/core/index.d.cts +1 -1
  117. package/dist/core/index.d.ts +1 -1
  118. package/dist/core/index.js +2 -2
  119. package/dist/cqrs/index.d.cts +3 -3
  120. package/dist/cqrs/index.d.ts +3 -3
  121. package/dist/cqrs/messaging.d.cts +4 -4
  122. package/dist/cqrs/messaging.d.ts +4 -4
  123. package/dist/cqrs/work-queue.d.cts +5 -5
  124. package/dist/cqrs/work-queue.d.ts +5 -5
  125. package/dist/data-structures/index.cjs +1226 -1074
  126. package/dist/data-structures/index.cjs.map +1 -1
  127. package/dist/data-structures/index.d.cts +8 -7
  128. package/dist/data-structures/index.d.ts +8 -7
  129. package/dist/data-structures/index.js +6 -5
  130. package/dist/{describe-BpyJuWkw.d.ts → describe-B_A5fbXH.d.ts} +1 -1
  131. package/dist/{describe-DYMw_l1C.d.cts → describe-CQRrkv-Q.d.cts} +1 -1
  132. package/dist/executors/tool-provider-adapters.cjs +3091 -0
  133. package/dist/executors/tool-provider-adapters.cjs.map +1 -0
  134. package/dist/executors/tool-provider-adapters.d.cts +174 -0
  135. package/dist/executors/tool-provider-adapters.d.ts +174 -0
  136. package/dist/executors/tool-provider-adapters.js +1336 -0
  137. package/dist/executors/tool-provider-adapters.js.map +1 -0
  138. package/dist/executors/tool-provider-runtime.cjs +5178 -0
  139. package/dist/executors/tool-provider-runtime.cjs.map +1 -0
  140. package/dist/executors/tool-provider-runtime.d.cts +17 -0
  141. package/dist/executors/tool-provider-runtime.d.ts +17 -0
  142. package/dist/executors/tool-provider-runtime.js +15 -0
  143. package/dist/executors/tool-provider-runtime.js.map +1 -0
  144. package/dist/executors/tool-provider.cjs +6392 -0
  145. package/dist/executors/tool-provider.cjs.map +1 -0
  146. package/dist/executors/tool-provider.d.cts +60 -0
  147. package/dist/executors/tool-provider.d.ts +60 -0
  148. package/dist/executors/tool-provider.js +89 -0
  149. package/dist/executors/tool-provider.js.map +1 -0
  150. package/dist/executors/work-queue.d.cts +6 -6
  151. package/dist/executors/work-queue.d.ts +6 -6
  152. package/dist/graph/index.cjs +1499 -1341
  153. package/dist/graph/index.cjs.map +1 -1
  154. package/dist/graph/index.d.cts +9 -8
  155. package/dist/graph/index.d.ts +9 -8
  156. package/dist/graph/index.js +23 -22
  157. package/dist/{graph-DCEWh4T2.d.cts → graph-CWK3HLPB.d.cts} +133 -131
  158. package/dist/{graph-D-1-SQeC.d.ts → graph-ho5sotHP.d.ts} +133 -131
  159. package/dist/{index-Bmxeu0nK.d.ts → index-CQWcE0Dw.d.ts} +18 -4
  160. package/dist/{index-72VZC0-E.d.cts → index-DHA1i67c.d.cts} +18 -4
  161. package/dist/index.cjs +3397 -3140
  162. package/dist/index.cjs.map +1 -1
  163. package/dist/index.d.cts +12 -9
  164. package/dist/index.d.ts +12 -9
  165. package/dist/index.js +110 -109
  166. package/dist/inspection/boundary.d.cts +4 -4
  167. package/dist/inspection/boundary.d.ts +4 -4
  168. package/dist/measurements-C0mAvWU1.d.cts +164 -0
  169. package/dist/measurements-OMpCeBwh.d.ts +164 -0
  170. package/dist/messaging/index.cjs +57 -51
  171. package/dist/messaging/index.cjs.map +1 -1
  172. package/dist/messaging/index.d.cts +11 -276
  173. package/dist/messaging/index.d.ts +11 -276
  174. package/dist/messaging/index.js +1 -1
  175. package/dist/operators/index.cjs +1228 -1076
  176. package/dist/operators/index.cjs.map +1 -1
  177. package/dist/operators/index.d.cts +5 -5
  178. package/dist/operators/index.d.ts +5 -5
  179. package/dist/operators/index.js +5 -5
  180. package/dist/orchestration/index.cjs +8844 -6071
  181. package/dist/orchestration/index.cjs.map +1 -1
  182. package/dist/orchestration/index.d.cts +171 -224
  183. package/dist/orchestration/index.d.ts +171 -224
  184. package/dist/orchestration/index.js +42 -991
  185. package/dist/orchestration/index.js.map +1 -1
  186. package/dist/orchestration/messaging.d.cts +5 -8
  187. package/dist/orchestration/messaging.d.ts +5 -8
  188. package/dist/orchestration/work-queue.cjs +1243 -5
  189. package/dist/orchestration/work-queue.cjs.map +1 -1
  190. package/dist/orchestration/work-queue.d.cts +121 -10
  191. package/dist/orchestration/work-queue.d.ts +121 -10
  192. package/dist/orchestration/work-queue.js +1067 -4
  193. package/dist/orchestration/work-queue.js.map +1 -1
  194. package/dist/patterns/event-flow.cjs +6 -20
  195. package/dist/patterns/event-flow.cjs.map +1 -1
  196. package/dist/patterns/event-flow.d.cts +4 -4
  197. package/dist/patterns/event-flow.d.ts +4 -4
  198. package/dist/patterns/event-flow.js +2 -2
  199. package/dist/patterns/index.cjs +453 -465
  200. package/dist/patterns/index.cjs.map +1 -1
  201. package/dist/patterns/index.d.cts +6 -5
  202. package/dist/patterns/index.d.ts +6 -5
  203. package/dist/patterns/index.js +3 -3
  204. package/dist/process-types-C9uMYz3i.d.ts +200 -0
  205. package/dist/process-types-h3uRlzDc.d.cts +200 -0
  206. package/dist/{reactive-collection-DNV0-gFi.d.cts → reactive-collection-B9R0Bl1P.d.cts} +4 -34
  207. package/dist/{reactive-collection-BAmfrGjd.d.ts → reactive-collection-BXFrH0U2.d.ts} +4 -34
  208. package/dist/{reactive-collection-storage-BNEldDyi.d.cts → reactive-collection-storage-BLGv8Xp8.d.cts} +5 -5
  209. package/dist/{reactive-collection-storage-YZsAQsWK.d.ts → reactive-collection-storage-a4zD3665.d.ts} +5 -5
  210. package/dist/{reactive-map-DRD1LJ2H.d.cts → reactive-map-C_8sOfdS.d.cts} +3 -2
  211. package/dist/{reactive-map-EsDiCmJk.d.ts → reactive-map-jle5dnok.d.ts} +3 -2
  212. package/dist/render/index.d.cts +2 -2
  213. package/dist/render/index.d.ts +2 -2
  214. package/dist/scheduled-readiness-5rwQlyjV.d.ts +91 -0
  215. package/dist/scheduled-readiness-DpRY8jI0.d.cts +91 -0
  216. package/dist/semantic-memory-graph-C2ZUwIlq.d.ts +30 -0
  217. package/dist/semantic-memory-graph-DlZBD_Eu.d.cts +30 -0
  218. package/dist/{semantic-memory-graph-DKuNzuWw.d.cts → semantic-memory-graph-types-BvzdqmkY.d.cts} +2 -28
  219. package/dist/{semantic-memory-graph-uwbwJdhe.d.ts → semantic-memory-graph-types-Ci-OtahO.d.ts} +2 -28
  220. package/dist/solutions/index.cjs +6353 -6169
  221. package/dist/solutions/index.cjs.map +1 -1
  222. package/dist/solutions/index.d.cts +12 -10
  223. package/dist/solutions/index.d.ts +12 -10
  224. package/dist/solutions/index.js +5 -5
  225. package/dist/solutions/reactive-layout/browser/index.cjs +116 -108
  226. package/dist/solutions/reactive-layout/browser/index.cjs.map +1 -1
  227. package/dist/solutions/reactive-layout/browser/index.d.cts +4 -4
  228. package/dist/solutions/reactive-layout/browser/index.d.ts +4 -4
  229. package/dist/solutions/reactive-layout/browser/index.js +3 -3
  230. package/dist/solutions/reactive-layout/index.cjs +3578 -3350
  231. package/dist/solutions/reactive-layout/index.cjs.map +1 -1
  232. package/dist/solutions/reactive-layout/index.d.cts +32 -576
  233. package/dist/solutions/reactive-layout/index.d.ts +32 -576
  234. package/dist/solutions/reactive-layout/index.js +65 -5
  235. package/dist/solutions/reactive-layout/node-canvas/index.cjs +127 -117
  236. package/dist/solutions/reactive-layout/node-canvas/index.cjs.map +1 -1
  237. package/dist/solutions/reactive-layout/node-canvas/index.d.cts +4 -4
  238. package/dist/solutions/reactive-layout/node-canvas/index.d.ts +4 -4
  239. package/dist/solutions/reactive-layout/node-canvas/index.js +3 -3
  240. package/dist/solutions/reactive-layout/react-native/index.cjs +127 -117
  241. package/dist/solutions/reactive-layout/react-native/index.cjs.map +1 -1
  242. package/dist/solutions/reactive-layout/react-native/index.d.cts +4 -4
  243. package/dist/solutions/reactive-layout/react-native/index.d.ts +4 -4
  244. package/dist/solutions/reactive-layout/react-native/index.js +3 -3
  245. package/dist/solutions/reactive-layout/skia/index.cjs +127 -117
  246. package/dist/solutions/reactive-layout/skia/index.cjs.map +1 -1
  247. package/dist/solutions/reactive-layout/skia/index.d.cts +4 -4
  248. package/dist/solutions/reactive-layout/skia/index.d.ts +4 -4
  249. package/dist/solutions/reactive-layout/skia/index.js +3 -3
  250. package/dist/solutions/work-item/actions.cjs +1609 -1428
  251. package/dist/solutions/work-item/actions.cjs.map +1 -1
  252. package/dist/solutions/work-item/actions.d.cts +23 -184
  253. package/dist/solutions/work-item/actions.d.ts +23 -184
  254. package/dist/solutions/work-item/actions.js +1558 -934
  255. package/dist/solutions/work-item/actions.js.map +1 -1
  256. package/dist/solutions/work-item/scheduling.cjs +9181 -1838
  257. package/dist/solutions/work-item/scheduling.cjs.map +1 -1
  258. package/dist/solutions/work-item/scheduling.d.cts +1763 -328
  259. package/dist/solutions/work-item/scheduling.d.ts +1763 -328
  260. package/dist/solutions/work-item/scheduling.js +117 -3
  261. package/dist/solutions/work-item/work-queue.d.cts +7 -7
  262. package/dist/solutions/work-item/work-queue.d.ts +7 -7
  263. package/dist/sources/browser.cjs +1318 -1083
  264. package/dist/sources/browser.cjs.map +1 -1
  265. package/dist/sources/browser.d.cts +4 -4
  266. package/dist/sources/browser.d.ts +4 -4
  267. package/dist/sources/browser.js +4 -4
  268. package/dist/sources/index.cjs +1318 -1083
  269. package/dist/sources/index.cjs.map +1 -1
  270. package/dist/sources/index.d.cts +4 -4
  271. package/dist/sources/index.d.ts +4 -4
  272. package/dist/sources/index.js +4 -4
  273. package/dist/sources/node.d.cts +3 -3
  274. package/dist/sources/node.d.ts +3 -3
  275. package/dist/storage/browser.cjs +16 -0
  276. package/dist/storage/browser.cjs.map +1 -1
  277. package/dist/storage/browser.js +3 -3
  278. package/dist/storage/index.cjs +16 -0
  279. package/dist/storage/index.cjs.map +1 -1
  280. package/dist/storage/index.d.cts +5 -4
  281. package/dist/storage/index.d.ts +5 -4
  282. package/dist/storage/index.js +6 -6
  283. package/dist/storage/node.cjs +16 -0
  284. package/dist/storage/node.cjs.map +1 -1
  285. package/dist/storage/node.js +3 -3
  286. package/dist/{store-C-joM4gh.d.ts → store-BmtEs9mG.d.ts} +1 -1
  287. package/dist/{store-CNJwmT-h.d.cts → store-PAAsYvML.d.cts} +1 -1
  288. package/dist/types-BRNKoFHj.d.ts +357 -0
  289. package/dist/{types-CcPIn7sy.d.cts → types-BS6CyR-x.d.cts} +27 -108
  290. package/dist/types-BWK2ay3e.d.cts +457 -0
  291. package/dist/types-BXKSAoE1.d.cts +357 -0
  292. package/dist/types-BaPpZItj.d.ts +457 -0
  293. package/dist/types-BhveCMg5.d.cts +274 -0
  294. package/dist/{types-BsJvElYZ.d.ts → types-C9NOgNJw.d.ts} +27 -108
  295. package/dist/types-CTsQ8Ww4.d.ts +274 -0
  296. package/dist/types-DL_05n2M.d.ts +34 -0
  297. package/dist/types-XDoqxYO9.d.cts +34 -0
  298. package/dist/{work-item-runtime-C3M79vE9.d.ts → work-item-runtime-types-BqtWnSCS.d.ts} +3 -93
  299. package/dist/{work-item-runtime-qaVgsL0T.d.cts → work-item-runtime-types-CHO03AKh.d.cts} +3 -93
  300. package/dist/work-queue/index.cjs +957 -953
  301. package/dist/work-queue/index.cjs.map +1 -1
  302. package/dist/work-queue/index.d.cts +9 -357
  303. package/dist/work-queue/index.d.ts +9 -357
  304. package/dist/work-queue/index.js +957 -953
  305. package/dist/work-queue/index.js.map +1 -1
  306. package/package.json +603 -513
  307. package/dist/chunk-3JR3EDB2.js.map +0 -1
  308. package/dist/chunk-4VLAQHZI.js.map +0 -1
  309. package/dist/chunk-6XAIOL3X.js +0 -2407
  310. package/dist/chunk-6XAIOL3X.js.map +0 -1
  311. package/dist/chunk-FBUU43PM.js.map +0 -1
  312. package/dist/chunk-HKK2JD7Z.js.map +0 -1
  313. package/dist/chunk-PC3BTTBE.js +0 -3217
  314. package/dist/chunk-PC3BTTBE.js.map +0 -1
  315. package/dist/chunk-QFY4AFY4.js.map +0 -1
  316. package/dist/chunk-V7CIJE4B.js.map +0 -1
  317. package/dist/chunk-XKXWK2KP.js.map +0 -1
  318. package/dist/chunk-YDSIVHRR.js.map +0 -1
  319. package/dist/chunk-YXMZ2N6J.js.map +0 -1
  320. /package/dist/{chunk-Z2ANQ6JW.js.map → chunk-4Q77MMJY.js.map} +0 -0
  321. /package/dist/{chunk-IXZBUWSP.js.map → chunk-7HZRJMN6.js.map} +0 -0
  322. /package/dist/{chunk-SS2A464I.js.map → chunk-CJIFGJQG.js.map} +0 -0
  323. /package/dist/{chunk-K3FXTC3I.js.map → chunk-HJACH5PU.js.map} +0 -0
  324. /package/dist/{chunk-GTPCOEOZ.js.map → chunk-IDMLXLLG.js.map} +0 -0
  325. /package/dist/{chunk-DKP56CSW.js.map → chunk-JKAYLDQV.js.map} +0 -0
  326. /package/dist/{chunk-VTHLCFN4.js.map → chunk-RAHNUYA5.js.map} +0 -0
  327. /package/dist/{chunk-CZNV3VXM.js.map → chunk-SIDJO2T4.js.map} +0 -0
  328. /package/dist/{chunk-2747WN4Y.js.map → chunk-TPND5MJS.js.map} +0 -0
  329. /package/dist/{chunk-43BKLFZU.js.map → chunk-YDWBXXQZ.js.map} +0 -0
  330. /package/dist/{chunk-6PHB5CJS.js.map → chunk-ZLFRB6FI.js.map} +0 -0
@@ -44,181 +44,132 @@ function depBatch(ctx, depIndex) {
44
44
  return flattened.length === 0 ? [] : flattened;
45
45
  }
46
46
 
47
- // src/solutions/work-item/scheduling.ts
48
- function validateAcceptanceCriteria(criteria, opts = {}) {
49
- const out = [];
47
+ // src/orchestration/work-item-runtime-shared.ts
48
+ function emitWorkItemAdmissionIssue(ctx, state, key, code, message, subjectId, sourceRefs2) {
49
+ if (state.issueKeys.has(key)) return;
50
+ state.issueKeys.add(key);
51
+ state.issueSeq += 1;
52
+ state.statusSeq += 1;
53
+ state.auditSeq += 1;
54
+ const issue2 = dataIssue(code, message, { subjectId, refs: sourceRefs2 });
55
+ const status = {
56
+ kind: "work-item-status",
57
+ statusId: `${subjectId ?? "work-item"}:mapping-issue:${state.statusSeq}`,
58
+ workItemId: subjectId ?? "unknown",
59
+ state: "mapping-issue",
60
+ sourceRefs: sourceRefs2,
61
+ issues: [issue2]
62
+ };
63
+ const audit = {
64
+ id: `${subjectId ?? "work-item"}:${code}:${state.auditSeq}`,
65
+ kind: "work-item-domain-action-admission-issue",
66
+ subjectId,
67
+ issueCode: code,
68
+ message,
69
+ sourceRefs: sourceRefs2
70
+ };
71
+ ctx.down([
72
+ ["DATA", { kind: "issue", issue: issue2 }],
73
+ ["DATA", { kind: "status", status }],
74
+ ["DATA", { kind: "audit", audit }]
75
+ ]);
76
+ }
77
+ function emptyWorkItemDomainActionAdmissionState() {
78
+ return {
79
+ proposals: /* @__PURE__ */ new Map(),
80
+ policies: /* @__PURE__ */ new Map(),
81
+ decisions: /* @__PURE__ */ new Map(),
82
+ admissionsById: /* @__PURE__ */ new Map(),
83
+ admissionsByProposal: /* @__PURE__ */ new Map(),
84
+ terminalDecisionIds: /* @__PURE__ */ new Set(),
85
+ issueKeys: /* @__PURE__ */ new Set(),
86
+ statusSeq: 0,
87
+ issueSeq: 0,
88
+ auditSeq: 0
89
+ };
90
+ }
91
+ function freezeWorkItemDomainActionAdmissionViews(state) {
92
+ return {
93
+ admissionsByProposal: new Map(state.admissionsByProposal),
94
+ admissionsByWorkItem: new Map(
95
+ Array.from(state.admissionsByWorkItem, ([key, value]) => [key, Object.freeze([...value])])
96
+ ),
97
+ issues: Object.freeze([...state.issues]),
98
+ audit: Object.freeze([...state.audit])
99
+ };
100
+ }
101
+ function workItemAdmissionProposalRefs(proposal) {
102
+ return uniqueSourceRefs([
103
+ ref("work-item", proposal.workItemId),
104
+ ref("work-item-domain-action-proposal", proposal.proposalId),
105
+ ref("effect-run", proposal.effectRunId),
106
+ ref("effect-run-result", proposal.effectRunResultId),
107
+ ref("work-item-evidence", proposal.evidenceId),
108
+ ref("work-item-effect-mapping-policy", proposal.policyId),
109
+ ...proposal.sourceRefs ?? []
110
+ ]);
111
+ }
112
+ function workItemAdmissionDecisionRefs(decision, proposal, policy) {
113
+ return uniqueSourceRefs([
114
+ ...proposal === void 0 ? [] : workItemAdmissionProposalRefs(proposal),
115
+ ref("work-item-domain-action-admission-decision", decision.decisionId),
116
+ ref("work-item-domain-action-admission", decision.admissionId),
117
+ ref("work-item-domain-action-proposal", decision.proposalId),
118
+ ...decision.policyId === void 0 ? [] : [ref("work-item-domain-action-admission-policy", decision.policyId)],
119
+ ...policy === void 0 ? [] : [ref("work-item-domain-action-admission-policy", policy.policyId)],
120
+ ...decision.targetProposalId === void 0 ? [] : [ref("work-item-domain-action-proposal", decision.targetProposalId)],
121
+ ...decision.targetAdmissionId === void 0 ? [] : [ref("work-item-domain-action-admission", decision.targetAdmissionId)],
122
+ ...decision.sourceRefs ?? []
123
+ ]);
124
+ }
125
+ function uniqueSourceRefs(sourceRefs2) {
50
126
  const seen = /* @__PURE__ */ new Set();
51
- for (const criterion of criteria) {
52
- if (!isRecord(criterion)) {
53
- out.push(issue("malformed-draft", "AcceptanceCriterion must be an object", opts.workItemId));
54
- continue;
55
- }
56
- if (typeof criterion.criterionId !== "string" || criterion.criterionId === "") {
57
- out.push(
58
- issue(
59
- "missing-required-field",
60
- "AcceptanceCriterion.criterionId is required",
61
- opts.workItemId
62
- )
63
- );
64
- continue;
65
- }
66
- if (seen.has(criterion.criterionId)) {
67
- out.push(
68
- issue(
69
- "duplicate-id",
70
- `Duplicate acceptance criterion id '${criterion.criterionId}'`,
71
- opts.workItemId,
72
- { criterionId: criterion.criterionId }
73
- )
74
- );
75
- }
76
- seen.add(criterion.criterionId);
77
- if (typeof criterion.statement !== "string" || criterion.statement.trim() === "") {
78
- out.push(
79
- issue(
80
- "missing-required-field",
81
- "AcceptanceCriterion.statement is required",
82
- opts.workItemId,
83
- { criterionId: criterion.criterionId }
84
- )
85
- );
86
- }
127
+ const unique = [];
128
+ for (const sourceRef of sourceRefs2) {
129
+ const key = sourceRefKey(sourceRef);
130
+ if (seen.has(key)) continue;
131
+ seen.add(key);
132
+ unique.push(sourceRef);
87
133
  }
88
- return out;
134
+ return unique;
89
135
  }
90
- function validateVerificationPlan(plan, criteria = [], opts = {}) {
91
- const out = [];
92
- if (!isRecord(plan) || !Array.isArray(plan.steps)) {
93
- return [issue("malformed-draft", "VerificationPlan.steps must be an array", opts.workItemId)];
94
- }
95
- const criterionIds = new Set(criteria.map((criterion) => criterion.criterionId));
96
- const stepIds = /* @__PURE__ */ new Set();
97
- const modes = new Set(opts.allowedModes ?? ["auto", "manual", "hybrid"]);
98
- const effectKinds = new Set(opts.allowedEffectKinds ?? ["verification"]);
99
- for (const step of plan.steps) {
100
- if (!isRecord(step)) {
101
- out.push(issue("malformed-draft", "VerificationStep must be an object", opts.workItemId));
102
- continue;
103
- }
104
- if (typeof step.stepId !== "string" || step.stepId === "") {
105
- out.push(
106
- issue("missing-required-field", "VerificationStep.stepId is required", opts.workItemId)
107
- );
108
- continue;
109
- }
110
- if (stepIds.has(step.stepId)) {
111
- out.push(
112
- issue("duplicate-id", `Duplicate verification step id '${step.stepId}'`, opts.workItemId, {
113
- stepId: step.stepId
114
- })
115
- );
116
- }
117
- stepIds.add(step.stepId);
118
- if (typeof step.mode !== "string" || !modes.has(step.mode)) {
119
- out.push(
120
- issue(
121
- "unsupported-mode",
122
- `Unsupported verification step mode '${step.mode}'`,
123
- opts.workItemId,
124
- { stepId: step.stepId }
125
- )
126
- );
127
- }
128
- if (step.effectKind !== void 0 && (typeof step.effectKind !== "string" || !effectKinds.has(step.effectKind))) {
129
- out.push(
130
- issue(
131
- "unsupported-effect-kind",
132
- `Unsupported verification effect kind '${step.effectKind}'`,
133
- opts.workItemId,
134
- { stepId: step.stepId }
135
- )
136
- );
137
- }
138
- const verifiesCriteriaIds = Array.isArray(step.verifiesCriteriaIds) ? step.verifiesCriteriaIds : [];
139
- for (const criterionId of verifiesCriteriaIds) {
140
- if (!criterionIds.has(criterionId)) {
141
- out.push(
142
- issue("dangling-ref", `Unknown acceptance criterion '${criterionId}'`, opts.workItemId, {
143
- stepId: step.stepId,
144
- criterionId
145
- })
146
- );
147
- }
148
- }
149
- }
150
- for (const step of plan.steps) {
151
- if (!isRecord(step) || typeof step.stepId !== "string") continue;
152
- const dependsOnStepIds = Array.isArray(step.dependsOnStepIds) ? step.dependsOnStepIds : [];
153
- for (const depId of dependsOnStepIds) {
154
- if (!stepIds.has(depId)) {
155
- out.push(
156
- issue("dangling-ref", `Unknown verification dependency '${depId}'`, opts.workItemId, {
157
- stepId: step.stepId
158
- })
159
- );
136
+ function sourceRefKey(sourceRef) {
137
+ return `${sourceRef.kind}:${sourceRef.id}:${JSON.stringify(sourceRef.metadata ?? {})}`;
138
+ }
139
+ function projectRuntimeFact(graph, runtime, name, factory, pick) {
140
+ return graph.node(
141
+ [runtime],
142
+ (ctx) => {
143
+ for (const fact of depBatch(ctx, 0) ?? []) {
144
+ const typed = fact;
145
+ const value = pick(typed);
146
+ if (value !== void 0) ctx.down([["DATA", value]]);
160
147
  }
161
- }
162
- }
163
- const cycle = findCycle(
164
- plan.steps.filter(
165
- (step) => isRecord(step) && typeof step.stepId === "string" && Array.isArray(step.dependsOnStepIds)
166
- )
148
+ },
149
+ { name, factory }
167
150
  );
168
- if (cycle !== void 0) {
169
- out.push(
170
- issue(
171
- "cyclic-dependency",
172
- `Verification step cycle detected: ${cycle.join(" -> ")}`,
173
- opts.workItemId,
174
- { stepId: cycle[0] }
175
- )
176
- );
177
- }
178
- return out;
179
151
  }
180
- function findCycle(steps) {
181
- const byId = new Map(steps.map((step) => [step.stepId, step.dependsOnStepIds ?? []]));
182
- const visiting = /* @__PURE__ */ new Set();
183
- const visited = /* @__PURE__ */ new Set();
184
- const stack = [];
185
- const visit = (id) => {
186
- if (visiting.has(id)) return [...stack.slice(stack.indexOf(id)), id];
187
- if (visited.has(id)) return void 0;
188
- visiting.add(id);
189
- stack.push(id);
190
- for (const dep of byId.get(id) ?? []) {
191
- if (!byId.has(dep)) continue;
192
- const cycle = visit(dep);
193
- if (cycle !== void 0) return cycle;
194
- }
195
- stack.pop();
196
- visiting.delete(id);
197
- visited.add(id);
198
- return void 0;
199
- };
200
- for (const step of steps) {
201
- const cycle = visit(step.stepId);
202
- if (cycle !== void 0) return cycle;
152
+ function forEachPolicyDepBatch(ctx, start, count, fn) {
153
+ for (let i = 0; i < count; i += 1) {
154
+ for (const value of depBatch(ctx, start + i) ?? []) fn(value);
203
155
  }
204
- return void 0;
205
156
  }
206
- function issue(code, message, workItemId, metadata) {
157
+ function dataIssue(code, message, opts = {}) {
207
158
  return {
208
159
  kind: "issue",
209
160
  code,
210
161
  message,
211
- severity: "error",
212
- source: "work-item-scheduling",
213
- subjectId: workItemId,
214
- metadata
162
+ severity: opts.severity ?? "error",
163
+ subjectId: opts.subjectId,
164
+ refs: opts.refs?.map((r) => `${r.kind}:${r.id}`),
165
+ details: opts.details
215
166
  };
216
167
  }
217
- function isRecord(value) {
218
- return value !== null && typeof value === "object";
168
+ function ref(kind, id) {
169
+ return { kind, id };
219
170
  }
220
171
 
221
- // src/orchestration/work-item-runtime.ts
172
+ // src/orchestration/work-item-runtime-admission.ts
222
173
  function workItemDomainActionAdmissionProjector(graph, opts) {
223
174
  const name = opts.name ?? "workItemDomainActionAdmissions";
224
175
  const policyDeps = opts.admissionPolicies ?? [];
@@ -596,131 +547,86 @@ function workItemAdmissionStatusState(state) {
596
547
  if (state === "deferred") return "domain-action-deferred";
597
548
  return "domain-action-merged";
598
549
  }
599
- function emitWorkItemAdmissionIssue(ctx, state, key, code, message, subjectId, sourceRefs2) {
600
- if (state.issueKeys.has(key)) return;
601
- state.issueKeys.add(key);
602
- state.issueSeq += 1;
603
- state.statusSeq += 1;
604
- state.auditSeq += 1;
605
- const issue2 = dataIssue(code, message, { subjectId, refs: sourceRefs2 });
606
- const status = {
607
- kind: "work-item-status",
608
- statusId: `${subjectId ?? "work-item"}:mapping-issue:${state.statusSeq}`,
609
- workItemId: subjectId ?? "unknown",
610
- state: "mapping-issue",
611
- sourceRefs: sourceRefs2,
612
- issues: [issue2]
613
- };
614
- const audit = {
615
- id: `${subjectId ?? "work-item"}:${code}:${state.auditSeq}`,
616
- kind: "work-item-domain-action-admission-issue",
617
- subjectId,
618
- issueCode: code,
619
- message,
620
- sourceRefs: sourceRefs2
621
- };
622
- ctx.down([
623
- ["DATA", { kind: "issue", issue: issue2 }],
624
- ["DATA", { kind: "status", status }],
625
- ["DATA", { kind: "audit", audit }]
626
- ]);
627
- }
628
- function emptyWorkItemDomainActionAdmissionState() {
629
- return {
630
- proposals: /* @__PURE__ */ new Map(),
631
- policies: /* @__PURE__ */ new Map(),
632
- decisions: /* @__PURE__ */ new Map(),
633
- admissionsById: /* @__PURE__ */ new Map(),
634
- admissionsByProposal: /* @__PURE__ */ new Map(),
635
- terminalDecisionIds: /* @__PURE__ */ new Set(),
636
- issueKeys: /* @__PURE__ */ new Set(),
637
- statusSeq: 0,
638
- issueSeq: 0,
639
- auditSeq: 0
640
- };
641
- }
642
- function freezeWorkItemDomainActionAdmissionViews(state) {
550
+
551
+ // src/solutions/work-item/actions-shared.ts
552
+ function auditRecord(kind, seq, status) {
643
553
  return {
644
- admissionsByProposal: new Map(state.admissionsByProposal),
645
- admissionsByWorkItem: new Map(
646
- Array.from(state.admissionsByWorkItem, ([key, value]) => [key, Object.freeze([...value])])
647
- ),
648
- issues: Object.freeze([...state.issues]),
649
- audit: Object.freeze([...state.audit])
554
+ id: `${kind}:${seq}`,
555
+ kind,
556
+ subjectId: status.workItemId,
557
+ message: status.message,
558
+ issueCode: status.code,
559
+ sourceRefs: status.sourceRefs,
560
+ metadata: {
561
+ statusId: status.statusId,
562
+ state: status.state,
563
+ proposalId: status.proposalId,
564
+ admissionId: status.admissionId,
565
+ actionKind: status.actionKind,
566
+ ...status.metadata ?? {}
567
+ }
650
568
  };
651
569
  }
652
- function workItemAdmissionProposalRefs(proposal) {
653
- return uniqueSourceRefs([
654
- ref("work-item", proposal.workItemId),
655
- ref("work-item-domain-action-proposal", proposal.proposalId),
656
- ref("effect-run", proposal.effectRunId),
657
- ref("effect-run-result", proposal.effectRunResultId),
658
- ref("work-item-evidence", proposal.evidenceId),
659
- ref("work-item-effect-mapping-policy", proposal.policyId),
660
- ...proposal.sourceRefs ?? []
661
- ]);
662
- }
663
- function workItemAdmissionDecisionRefs(decision, proposal, policy) {
664
- return uniqueSourceRefs([
665
- ...proposal === void 0 ? [] : workItemAdmissionProposalRefs(proposal),
666
- ref("work-item-domain-action-admission-decision", decision.decisionId),
667
- ref("work-item-domain-action-admission", decision.admissionId),
668
- ref("work-item-domain-action-proposal", decision.proposalId),
669
- ...decision.policyId === void 0 ? [] : [ref("work-item-domain-action-admission-policy", decision.policyId)],
670
- ...policy === void 0 ? [] : [ref("work-item-domain-action-admission-policy", policy.policyId)],
671
- ...decision.targetProposalId === void 0 ? [] : [ref("work-item-domain-action-proposal", decision.targetProposalId)],
672
- ...decision.targetAdmissionId === void 0 ? [] : [ref("work-item-domain-action-admission", decision.targetAdmissionId)],
673
- ...decision.sourceRefs ?? []
674
- ]);
675
- }
676
- function uniqueSourceRefs(sourceRefs2) {
677
- const seen = /* @__PURE__ */ new Set();
678
- const unique = [];
679
- for (const sourceRef of sourceRefs2) {
680
- const key = sourceRefKey(sourceRef);
681
- if (seen.has(key)) continue;
682
- seen.add(key);
683
- unique.push(sourceRef);
684
- }
685
- return unique;
686
- }
687
- function sourceRefKey(sourceRef) {
688
- return `${sourceRef.kind}:${sourceRef.id}:${JSON.stringify(sourceRef.metadata ?? {})}`;
689
- }
690
- function projectRuntimeFact(graph, runtime, name, factory, pick) {
570
+ function project(graph, runtime, name, factory, select) {
691
571
  return graph.node(
692
572
  [runtime],
693
573
  (ctx) => {
694
- for (const fact of depBatch(ctx, 0) ?? []) {
695
- const typed = fact;
696
- const value = pick(typed);
697
- if (value !== void 0) ctx.down([["DATA", value]]);
574
+ for (const raw of depBatch(ctx, 0) ?? []) {
575
+ const selected = select(raw);
576
+ if (selected !== void 0) ctx.down([["DATA", selected]]);
698
577
  }
699
578
  },
700
- { name, factory }
579
+ { name, factory, partial: true, completeWhenDepsComplete: false, errorWhenDepsError: false }
701
580
  );
702
581
  }
703
- function forEachPolicyDepBatch(ctx, start, count, fn) {
704
- for (let i = 0; i < count; i += 1) {
705
- for (const value of depBatch(ctx, start + i) ?? []) fn(value);
706
- }
707
- }
708
- function dataIssue(code, message, opts = {}) {
582
+ function dataIssue2(code, message, opts = {}) {
709
583
  return {
710
584
  kind: "issue",
711
585
  code,
712
586
  message,
713
- severity: opts.severity ?? "error",
587
+ severity: "error",
588
+ source: "work-item-actions",
714
589
  subjectId: opts.subjectId,
715
- refs: opts.refs?.map((r) => `${r.kind}:${r.id}`),
716
- details: opts.details
590
+ refs: opts.refs?.map((sourceRef) => `${sourceRef.kind}:${sourceRef.id}`),
591
+ metadata: opts.metadata
717
592
  };
718
593
  }
719
- function ref(kind, id) {
594
+ function uniqueSourceRefs2(sourceRefs2) {
595
+ const seen = /* @__PURE__ */ new Set();
596
+ const out = [];
597
+ for (const sourceRef of sourceRefs2) {
598
+ const key = `${sourceRef.kind}:${sourceRef.id}:${JSON.stringify(sourceRef.metadata ?? {})}`;
599
+ if (seen.has(key)) continue;
600
+ seen.add(key);
601
+ out.push(sourceRef);
602
+ }
603
+ return out;
604
+ }
605
+ function sourceRefs(value) {
606
+ if (!Array.isArray(value)) return void 0;
607
+ return value.filter(
608
+ (item) => isRecord(item) && typeof item.kind === "string" && typeof item.id === "string"
609
+ );
610
+ }
611
+ function numberMetadata(metadata, key) {
612
+ const value = metadata?.[key];
613
+ return typeof value === "number" ? value : void 0;
614
+ }
615
+ function stringMetadata(metadata, key) {
616
+ const value = metadata?.[key];
617
+ return typeof value === "string" ? value : void 0;
618
+ }
619
+ function ref2(kind, id) {
720
620
  return { kind, id };
721
621
  }
622
+ function recordString(value, key) {
623
+ return isRecord(value) && typeof value[key] === "string" ? value[key] : void 0;
624
+ }
625
+ function isRecord(value) {
626
+ return value !== null && typeof value === "object";
627
+ }
722
628
 
723
- // src/solutions/work-item/actions.ts
629
+ // src/solutions/work-item/actions-types.ts
724
630
  var patchFieldKeys = /* @__PURE__ */ new Set([
725
631
  "summary",
726
632
  "detail",
@@ -735,103 +641,239 @@ var patchFieldKeys = /* @__PURE__ */ new Set([
735
641
  "sourceRefs",
736
642
  "metadata"
737
643
  ]);
738
- function workItemDomainActionProposal(proposalId, workItemId, actionKind, opts = {}) {
739
- return {
740
- kind: "work-item-domain-action-proposal",
741
- proposalId,
742
- workItemId,
743
- actionKind,
744
- effectRunId: opts.effectRunId ?? `domain-action:${proposalId}:effect-run`,
745
- effectRunResultId: opts.effectRunResultId ?? `domain-action:${proposalId}:effect-run-result`,
746
- evidenceId: opts.evidenceId ?? `domain-action:${proposalId}:evidence`,
747
- policyId: opts.policyId ?? "domain-action-proposal",
748
- payload: opts.payload,
749
- reason: opts.reason,
750
- sourceRefs: opts.sourceRefs,
751
- proposedAtMs: opts.proposedAtMs,
752
- metadata: opts.metadata
753
- };
644
+
645
+ // src/solutions/work-item/scheduling-shared.ts
646
+ function normalizePlanFromUnknown(draft) {
647
+ const verificationPlan = draft.verificationPlan;
648
+ if (verificationPlan !== void 0) {
649
+ if (!isRecord2(verificationPlan) || !Array.isArray(verificationPlan.steps)) {
650
+ return { planId: "malformed", steps: [] };
651
+ }
652
+ return verificationPlan;
653
+ }
654
+ const verificationSteps = draft.verificationSteps;
655
+ if (verificationSteps !== void 0) {
656
+ if (!Array.isArray(verificationSteps)) return { planId: "malformed", steps: [] };
657
+ return { planId: "default", steps: verificationSteps };
658
+ }
659
+ return void 0;
754
660
  }
755
- function workItemDomainActionProposalIntake(proposalId, workItemId, actionKind, opts = {}) {
756
- return {
757
- kind: "work-item-domain-action-proposal-intake",
758
- proposalId,
759
- workItemId,
760
- actionKind,
761
- payload: opts.payload,
762
- reason: opts.reason,
763
- policyId: opts.policyId,
764
- effectRunId: opts.effectRunId,
765
- effectRunResultId: opts.effectRunResultId,
766
- evidenceId: opts.evidenceId,
767
- proposedAtMs: opts.proposedAtMs,
768
- sourceRefs: opts.sourceRefs,
769
- metadata: opts.metadata
770
- };
661
+ function isVerificationPlanShape(value) {
662
+ return isRecord2(value) && typeof value.planId === "string" && Array.isArray(value.steps);
771
663
  }
772
- function workItemDomainActionApplyPolicy(policyId, opts = {}) {
664
+ function issue(code, message, workItemId, metadata) {
773
665
  return {
774
- kind: "work-item-domain-action-apply-policy",
775
- policyId,
776
- actionKinds: opts.actionKinds,
777
- patch: opts.patch,
778
- spawn: opts.spawn,
779
- metadata: opts.metadata
666
+ kind: "issue",
667
+ code,
668
+ message,
669
+ severity: "error",
670
+ source: "work-item-scheduling",
671
+ subjectId: workItemId,
672
+ metadata
780
673
  };
781
674
  }
782
- function workItemDomainActionProposalIntakeProjector(graph, opts) {
783
- const name = opts.name ?? "workItemDomainActionIntake";
784
- const now = opts.now ?? Date.now;
785
- const runtime = graph.node(
786
- [opts.proposals],
787
- (ctx) => {
788
- const state = ctx.state.get() ?? {
789
- proposals: /* @__PURE__ */ new Set(),
790
- statusSeq: 0,
791
- auditSeq: 0
792
- };
793
- for (const raw of depBatch(ctx, 0) ?? []) reduceProposalIntake(ctx, state, raw, now());
794
- ctx.state.set(state);
795
- },
796
- {
797
- name: `${name}/runtime`,
798
- factory: "workItemDomainActionProposalIntakeProjector",
799
- partial: true,
800
- completeWhenDepsComplete: false,
801
- errorWhenDepsError: false
802
- }
803
- );
804
- return {
805
- proposals: project(
806
- graph,
807
- runtime,
808
- `${name}/proposals`,
809
- "workItemDomainActionProposals",
810
- (fact) => fact.kind === "proposal" ? fact.value : void 0
811
- ),
812
- status: project(
813
- graph,
814
- runtime,
815
- `${name}/status`,
816
- "workItemDomainActionStatus",
817
- (fact) => fact.kind === "status" ? fact.value : void 0
818
- ),
819
- issues: project(
820
- graph,
821
- runtime,
822
- `${name}/issues`,
823
- "workItemDomainActionIssues",
824
- (fact) => fact.kind === "issue" ? fact.value : void 0
825
- ),
826
- audit: project(
827
- graph,
828
- runtime,
829
- `${name}/audit`,
830
- "workItemDomainActionAudit",
831
- (fact) => fact.kind === "audit" ? fact.value : void 0
675
+ function isRecord2(value) {
676
+ return value !== null && typeof value === "object";
677
+ }
678
+
679
+ // src/solutions/work-item/scheduling-validation.ts
680
+ function validateWorkItemDraft(draft, opts = {}) {
681
+ const out = [];
682
+ if (!isRecord2(draft)) {
683
+ return [issue("malformed-draft", "WorkItemDraft must be an object", opts.workItemId)];
684
+ }
685
+ if (typeof draft.summary !== "string") {
686
+ out.push(issue("missing-required-field", "WorkItemDraft.summary is required", opts.workItemId));
687
+ } else if (draft.summary.trim() === "") {
688
+ out.push(issue("missing-required-field", "WorkItemDraft.summary is required", opts.workItemId));
689
+ }
690
+ const criteria = draft.acceptanceCriteria === void 0 || Array.isArray(draft.acceptanceCriteria) ? draft.acceptanceCriteria : void 0;
691
+ if (draft.acceptanceCriteria !== void 0 && criteria === void 0) {
692
+ out.push(
693
+ issue(
694
+ "malformed-draft",
695
+ "WorkItemDraft.acceptanceCriteria must be an array",
696
+ opts.workItemId
697
+ )
698
+ );
699
+ }
700
+ out.push(...validateAcceptanceCriteria(criteria ?? [], opts));
701
+ if (draft.verificationPlan !== void 0 && !isVerificationPlanShape(draft.verificationPlan)) {
702
+ out.push(
703
+ issue("malformed-draft", "WorkItemDraft.verificationPlan is malformed", opts.workItemId)
704
+ );
705
+ }
706
+ if (draft.verificationSteps !== void 0 && !Array.isArray(draft.verificationSteps)) {
707
+ out.push(
708
+ issue("malformed-draft", "WorkItemDraft.verificationSteps must be an array", opts.workItemId)
709
+ );
710
+ }
711
+ const plan = normalizePlanFromUnknown(draft);
712
+ if (plan !== void 0) {
713
+ out.push(...validateVerificationPlan(plan, criteria ?? [], opts));
714
+ }
715
+ return out;
716
+ }
717
+ function validateAcceptanceCriteria(criteria, opts = {}) {
718
+ const out = [];
719
+ const seen = /* @__PURE__ */ new Set();
720
+ for (const criterion of criteria) {
721
+ if (!isRecord2(criterion)) {
722
+ out.push(issue("malformed-draft", "AcceptanceCriterion must be an object", opts.workItemId));
723
+ continue;
724
+ }
725
+ if (typeof criterion.criterionId !== "string" || criterion.criterionId === "") {
726
+ out.push(
727
+ issue(
728
+ "missing-required-field",
729
+ "AcceptanceCriterion.criterionId is required",
730
+ opts.workItemId
731
+ )
732
+ );
733
+ continue;
734
+ }
735
+ if (seen.has(criterion.criterionId)) {
736
+ out.push(
737
+ issue(
738
+ "duplicate-id",
739
+ `Duplicate acceptance criterion id '${criterion.criterionId}'`,
740
+ opts.workItemId,
741
+ { criterionId: criterion.criterionId }
742
+ )
743
+ );
744
+ }
745
+ seen.add(criterion.criterionId);
746
+ if (typeof criterion.statement !== "string" || criterion.statement.trim() === "") {
747
+ out.push(
748
+ issue(
749
+ "missing-required-field",
750
+ "AcceptanceCriterion.statement is required",
751
+ opts.workItemId,
752
+ { criterionId: criterion.criterionId }
753
+ )
754
+ );
755
+ }
756
+ }
757
+ return out;
758
+ }
759
+ function validateVerificationPlan(plan, criteria = [], opts = {}) {
760
+ const out = [];
761
+ if (!isRecord2(plan) || !Array.isArray(plan.steps)) {
762
+ return [issue("malformed-draft", "VerificationPlan.steps must be an array", opts.workItemId)];
763
+ }
764
+ const criterionIds = new Set(criteria.map((criterion) => criterion.criterionId));
765
+ const stepIds = /* @__PURE__ */ new Set();
766
+ const modes = new Set(opts.allowedModes ?? ["auto", "manual", "hybrid"]);
767
+ const effectKinds = new Set(opts.allowedEffectKinds ?? ["verification"]);
768
+ for (const step of plan.steps) {
769
+ if (!isRecord2(step)) {
770
+ out.push(issue("malformed-draft", "VerificationStep must be an object", opts.workItemId));
771
+ continue;
772
+ }
773
+ if (typeof step.stepId !== "string" || step.stepId === "") {
774
+ out.push(
775
+ issue("missing-required-field", "VerificationStep.stepId is required", opts.workItemId)
776
+ );
777
+ continue;
778
+ }
779
+ if (stepIds.has(step.stepId)) {
780
+ out.push(
781
+ issue("duplicate-id", `Duplicate verification step id '${step.stepId}'`, opts.workItemId, {
782
+ stepId: step.stepId
783
+ })
784
+ );
785
+ }
786
+ stepIds.add(step.stepId);
787
+ if (typeof step.mode !== "string" || !modes.has(step.mode)) {
788
+ out.push(
789
+ issue(
790
+ "unsupported-mode",
791
+ `Unsupported verification step mode '${step.mode}'`,
792
+ opts.workItemId,
793
+ { stepId: step.stepId }
794
+ )
795
+ );
796
+ }
797
+ if (step.effectKind !== void 0 && (typeof step.effectKind !== "string" || !effectKinds.has(step.effectKind))) {
798
+ out.push(
799
+ issue(
800
+ "unsupported-effect-kind",
801
+ `Unsupported verification effect kind '${step.effectKind}'`,
802
+ opts.workItemId,
803
+ { stepId: step.stepId }
804
+ )
805
+ );
806
+ }
807
+ const verifiesCriteriaIds = Array.isArray(step.verifiesCriteriaIds) ? step.verifiesCriteriaIds : [];
808
+ for (const criterionId of verifiesCriteriaIds) {
809
+ if (!criterionIds.has(criterionId)) {
810
+ out.push(
811
+ issue("dangling-ref", `Unknown acceptance criterion '${criterionId}'`, opts.workItemId, {
812
+ stepId: step.stepId,
813
+ criterionId
814
+ })
815
+ );
816
+ }
817
+ }
818
+ }
819
+ for (const step of plan.steps) {
820
+ if (!isRecord2(step) || typeof step.stepId !== "string") continue;
821
+ const dependsOnStepIds = Array.isArray(step.dependsOnStepIds) ? step.dependsOnStepIds : [];
822
+ for (const depId of dependsOnStepIds) {
823
+ if (!stepIds.has(depId)) {
824
+ out.push(
825
+ issue("dangling-ref", `Unknown verification dependency '${depId}'`, opts.workItemId, {
826
+ stepId: step.stepId
827
+ })
828
+ );
829
+ }
830
+ }
831
+ }
832
+ const cycle = findCycle(
833
+ plan.steps.filter(
834
+ (step) => isRecord2(step) && typeof step.stepId === "string" && Array.isArray(step.dependsOnStepIds)
832
835
  )
836
+ );
837
+ if (cycle !== void 0) {
838
+ out.push(
839
+ issue(
840
+ "cyclic-dependency",
841
+ `Verification step cycle detected: ${cycle.join(" -> ")}`,
842
+ opts.workItemId,
843
+ { stepId: cycle[0] }
844
+ )
845
+ );
846
+ }
847
+ return out;
848
+ }
849
+ function findCycle(steps) {
850
+ const byId = new Map(steps.map((step) => [step.stepId, step.dependsOnStepIds ?? []]));
851
+ const visiting = /* @__PURE__ */ new Set();
852
+ const visited = /* @__PURE__ */ new Set();
853
+ const stack = [];
854
+ const visit = (id) => {
855
+ if (visiting.has(id)) return [...stack.slice(stack.indexOf(id)), id];
856
+ if (visited.has(id)) return void 0;
857
+ visiting.add(id);
858
+ stack.push(id);
859
+ for (const dep of byId.get(id) ?? []) {
860
+ if (!byId.has(dep)) continue;
861
+ const cycle = visit(dep);
862
+ if (cycle !== void 0) return cycle;
863
+ }
864
+ stack.pop();
865
+ visiting.delete(id);
866
+ visited.add(id);
867
+ return void 0;
833
868
  };
869
+ for (const step of steps) {
870
+ const cycle = visit(step.stepId);
871
+ if (cycle !== void 0) return cycle;
872
+ }
873
+ return void 0;
834
874
  }
875
+
876
+ // src/solutions/work-item/actions-application.ts
835
877
  function workItemDomainActionApplicationProjector(graph, opts) {
836
878
  const name = opts.name ?? "workItemDomainActionApplication";
837
879
  const deps = opts.applyPolicies === void 0 ? [opts.proposals, opts.admissions, opts.workItems] : [opts.proposals, opts.admissions, opts.workItems, opts.applyPolicies];
@@ -940,1216 +982,1355 @@ function workItemDomainActionApplicationProjector(graph, opts) {
940
982
  )
941
983
  };
942
984
  }
943
- function workItemDomainActionCapabilityGuardProjector(graph, opts) {
944
- const name = opts.name ?? "workItemDomainActionCapabilityGuard";
945
- const deps = opts.capabilityAdmissionStatus === void 0 ? [opts.proposals, opts.capabilityAdmissions, opts.guardPolicies] : [
946
- opts.proposals,
947
- opts.capabilityAdmissions,
948
- opts.guardPolicies,
949
- opts.capabilityAdmissionStatus
950
- ];
951
- const statusIndex = opts.capabilityAdmissionStatus === void 0 ? -1 : 3;
952
- const now = opts.now ?? Date.now;
953
- const runtime = graph.node(
954
- deps,
955
- (ctx) => {
956
- const state = ctx.state.get() ?? {
957
- proposals: /* @__PURE__ */ new Map(),
958
- admissionsByCapability: /* @__PURE__ */ new Map(),
959
- issueStatusesByCapability: /* @__PURE__ */ new Map(),
960
- issueStatusesWithoutCapability: [],
961
- policies: /* @__PURE__ */ new Map(),
962
- emittedProposalDecisions: /* @__PURE__ */ new Set(),
963
- emittedStatusKeys: /* @__PURE__ */ new Set(),
964
- issueKeys: /* @__PURE__ */ new Set(),
965
- statusSeq: 0,
966
- auditSeq: 0
967
- };
968
- for (const raw of depBatch(ctx, 2) ?? []) {
969
- const policyIssue = capabilityGuardPolicyIssue(raw);
970
- if (policyIssue !== void 0) {
971
- emitCapabilityGuardIssue(
972
- ctx,
973
- state,
974
- `malformed-policy:${recordString(raw, "policyId") ?? "unknown"}`,
975
- policyIssue
976
- );
977
- continue;
978
- }
979
- const policy = raw;
980
- state.policies.set(policy.policyId, policy);
985
+ function applyAdmission(ctx, state, admission) {
986
+ if (state.appliedAdmissions.has(admission.admissionId)) return;
987
+ const proposal = state.proposals.get(admission.proposalId);
988
+ if (proposal === void 0) {
989
+ emitApplicationIssue(
990
+ ctx,
991
+ state,
992
+ `missing-proposal:${admission.admissionId}:${admission.proposalId}`,
993
+ "dangling-ref",
994
+ `Admission '${admission.admissionId}' cannot apply without proposal '${admission.proposalId}'`,
995
+ admission.workItemId,
996
+ admissionRefs(admission)
997
+ );
998
+ return;
999
+ }
1000
+ if (admission.state !== "admitted") {
1001
+ state.appliedAdmissions.add(admission.admissionId);
1002
+ emitApplication(ctx, state, admission, proposal, admission.state, []);
1003
+ return;
1004
+ }
1005
+ const mismatch = admissionProposalMismatch(admission, proposal);
1006
+ if (mismatch !== void 0) {
1007
+ state.appliedAdmissions.add(admission.admissionId);
1008
+ emitApplicationIssue(
1009
+ ctx,
1010
+ state,
1011
+ `admission-proposal-mismatch:${admission.admissionId}:${mismatch.field}`,
1012
+ "dangling-ref",
1013
+ mismatch.message,
1014
+ admission.workItemId,
1015
+ admissionRefs(admission, proposal),
1016
+ {
1017
+ field: mismatch.field,
1018
+ admissionValue: mismatch.admissionValue,
1019
+ proposalValue: mismatch.proposalValue
981
1020
  }
982
- for (const raw of depBatch(ctx, 1) ?? []) {
983
- const admissionIssue = capabilityAdmissionIssue(raw);
984
- if (admissionIssue !== void 0) {
985
- emitCapabilityGuardIssue(
986
- ctx,
987
- state,
988
- `malformed-admission:${recordString(raw, "admissionId") ?? "unknown"}`,
989
- admissionIssue
990
- );
991
- continue;
992
- }
993
- const admission = raw;
994
- const capabilityKey = capabilityBoundaryRefId(admission.capability);
995
- const byCapability = state.admissionsByCapability.get(capabilityKey) ?? [];
996
- if (!byCapability.some((item) => item.admissionId === admission.admissionId)) {
997
- state.admissionsByCapability.set(capabilityKey, [...byCapability, admission]);
998
- }
999
- }
1000
- if (statusIndex >= 0) {
1001
- for (const raw of depBatch(ctx, statusIndex) ?? []) {
1002
- const statusIssue = capabilityAdmissionStatusIssue(raw);
1003
- if (statusIssue !== void 0) {
1004
- emitCapabilityGuardIssue(
1005
- ctx,
1006
- state,
1007
- `malformed-capability-status:${recordString(raw, "statusId") ?? "unknown"}`,
1008
- statusIssue
1009
- );
1010
- continue;
1011
- }
1012
- const status = raw;
1013
- if (status.state !== "capability-admission-issue") continue;
1014
- const capabilityId = status.capabilityId;
1015
- const capabilityKind = status.capabilityKind;
1016
- if (capabilityId !== void 0 && capabilityKind !== void 0) {
1017
- const capabilityKey = capabilityBoundaryRefId({
1018
- id: capabilityId,
1019
- kind: capabilityKind
1020
- });
1021
- const statuses = state.issueStatusesByCapability.get(capabilityKey) ?? [];
1022
- if (!statuses.some((item) => item.statusId === status.statusId)) {
1023
- state.issueStatusesByCapability.set(capabilityKey, [...statuses, status]);
1024
- }
1025
- } else if (!state.issueStatusesWithoutCapability.some((item) => item.statusId === status.statusId)) {
1026
- state.issueStatusesWithoutCapability.push(status);
1027
- }
1028
- for (const proposal of state.proposals.values())
1029
- emitCapabilityStatusIssueForProposal(ctx, state, proposal, status);
1030
- }
1031
- }
1032
- for (const raw of depBatch(ctx, 0) ?? []) {
1033
- const proposalIssue = capabilityGuardProposalIssue(raw);
1034
- if (proposalIssue !== void 0) {
1035
- emitCapabilityGuardIssue(
1036
- ctx,
1037
- state,
1038
- `malformed-proposal:${recordString(raw, "proposalId") ?? "unknown"}`,
1039
- proposalIssue
1040
- );
1041
- continue;
1042
- }
1043
- const proposal = raw;
1044
- state.proposals.set(proposal.proposalId, proposal);
1045
- }
1046
- for (const proposal of state.proposals.values())
1047
- evaluateCapabilityGuardProposal(ctx, state, proposal, now());
1048
- ctx.state.set(state);
1049
- },
1050
- {
1051
- name: `${name}/runtime`,
1052
- factory: "workItemDomainActionCapabilityGuardProjector",
1053
- partial: true,
1054
- completeWhenDepsComplete: false,
1055
- errorWhenDepsError: false
1056
- }
1057
- );
1058
- return {
1059
- decisions: project(
1060
- graph,
1061
- runtime,
1062
- `${name}/decisions`,
1063
- "workItemDomainActionCapabilityGuardDecisions",
1064
- (fact) => fact.kind === "decision" ? fact.value : void 0
1065
- ),
1066
- status: project(
1067
- graph,
1068
- runtime,
1069
- `${name}/status`,
1070
- "workItemDomainActionCapabilityGuardStatus",
1071
- (fact) => fact.kind === "status" ? fact.value : void 0
1072
- ),
1073
- issues: project(
1074
- graph,
1075
- runtime,
1076
- `${name}/issues`,
1077
- "workItemDomainActionCapabilityGuardIssues",
1078
- (fact) => fact.kind === "issue" ? fact.value : void 0
1079
- ),
1080
- audit: project(
1081
- graph,
1082
- runtime,
1083
- `${name}/audit`,
1084
- "workItemDomainActionCapabilityGuardAudit",
1085
- (fact) => fact.kind === "audit" ? fact.value : void 0
1086
- )
1087
- };
1088
- }
1089
- function evaluateCapabilityGuardProposal(ctx, state, proposal, decidedAtMs) {
1090
- if (state.emittedProposalDecisions.has(proposal.proposalId)) return;
1091
- const policy = selectCapabilityGuardPolicy(state, proposal);
1092
- if (typeof policy === "string") {
1093
- emitCapabilityGuardIssue(
1021
+ );
1022
+ emitApplication(ctx, state, admission, proposal, "rejected", [], {
1023
+ code: "dangling-ref",
1024
+ message: mismatch.message
1025
+ });
1026
+ return;
1027
+ }
1028
+ const workItem = state.workItems.get(admission.workItemId);
1029
+ if (workItem === void 0) {
1030
+ emitApplicationIssue(
1094
1031
  ctx,
1095
1032
  state,
1096
- `policy:${proposal.proposalId}:${policy}`,
1097
- dataIssue2(policy, capabilityGuardPolicyMessage(policy, proposal), {
1098
- subjectId: proposal.workItemId,
1099
- refs: [ref2("work-item-domain-action-proposal", proposal.proposalId)]
1100
- })
1033
+ `missing-work-item:${admission.admissionId}:${admission.workItemId}`,
1034
+ "dangling-ref",
1035
+ `Admission '${admission.admissionId}' references unknown WorkItem '${admission.workItemId}'`,
1036
+ admission.workItemId,
1037
+ admissionRefs(admission, proposal)
1101
1038
  );
1102
1039
  return;
1103
1040
  }
1104
- for (const status of state.issueStatusesWithoutCapability)
1105
- emitCapabilityStatusIssueForProposal(ctx, state, proposal, status);
1106
- for (const capabilityRef of policy.capabilityRefs) {
1107
- for (const status of state.issueStatusesByCapability.get(
1108
- capabilityBoundaryRefId(capabilityRef)
1109
- ) ?? [])
1110
- emitCapabilityStatusIssueForProposal(ctx, state, proposal, status);
1111
- }
1112
- const admissions = policy.capabilityRefs.map(
1113
- (capabilityRef) => latestCapabilityAdmission(state, policy, capabilityRef)
1114
- );
1115
- const missingCapabilityRefs = policy.capabilityRefs.filter(
1116
- (_, index) => admissions[index] === void 0
1117
- );
1118
- const deferredCapabilityRefs = admissions.filter((admission) => admission !== void 0).filter((admission) => admission.state === "deferred").map((admission) => capabilityBoundaryRefId(admission.capability));
1119
- if (missingCapabilityRefs.length > 0 || deferredCapabilityRefs.length > 0) {
1120
- emitCapabilityGuardStatusOnce(
1041
+ const metadataConflict = conflictingApplicationMetadata(proposal, admission);
1042
+ if (metadataConflict !== void 0) {
1043
+ state.appliedAdmissions.add(admission.admissionId);
1044
+ emitApplicationIssue(
1121
1045
  ctx,
1122
1046
  state,
1123
- `deferred:${proposal.proposalId}:${policy.policyId}:missing=${missingCapabilityRefs.map(capabilityBoundaryRefId).join(",")}:deferred=${deferredCapabilityRefs.join(",")}`,
1047
+ `metadata-conflict:${admission.admissionId}:${metadataConflict.field}`,
1048
+ metadataConflict.code,
1049
+ metadataConflict.message,
1050
+ admission.workItemId,
1051
+ admissionRefs(admission, proposal),
1124
1052
  {
1125
- state: "deferred",
1126
- workItemId: proposal.workItemId,
1127
- proposalId: proposal.proposalId,
1128
- actionKind: proposal.actionKind,
1129
- policyId: policy.policyId,
1130
- capabilityRefs: policy.capabilityRefs,
1131
- admissionSubjectIds: policy.admissionSubjectIds,
1132
- message: "WorkItem domain action is waiting for capability admission",
1133
- sourceRefs: capabilityGuardRefs(proposal, policy, admissions),
1134
- metadata: {
1135
- missingCapabilityRefs: missingCapabilityRefs.map(capabilityBoundaryRefId),
1136
- deferredCapabilityRefs
1137
- }
1053
+ field: metadataConflict.field,
1054
+ admissionValue: metadataConflict.admissionValue,
1055
+ proposalValue: metadataConflict.proposalValue
1138
1056
  }
1139
1057
  );
1058
+ emitApplication(ctx, state, admission, proposal, "rejected", [], {
1059
+ code: metadataConflict.code,
1060
+ message: metadataConflict.message
1061
+ });
1140
1062
  return;
1141
1063
  }
1142
- const presentAdmissions = admissions.filter(
1143
- (admission) => admission !== void 0
1144
- );
1145
- const blocked = presentAdmissions.filter((admission) => admission.state === "blocked");
1146
- const outcome = blocked.length === 0 ? "admit" : "reject";
1147
- const decision = {
1148
- kind: "work-item-domain-action-admission-decision",
1149
- decisionId: `capability-guard:${proposal.proposalId}:decision`,
1150
- admissionId: `capability-guard:${proposal.proposalId}:admission`,
1151
- proposalId: proposal.proposalId,
1152
- outcome,
1153
- reason: blocked.length === 0 ? "Capability guard admitted required capabilities" : "Capability guard rejected blocked capabilities",
1154
- decidedAtMs,
1155
- sourceRefs: capabilityGuardRefs(proposal, policy, presentAdmissions),
1156
- metadata: {
1157
- capabilityGuardPolicyId: policy.policyId,
1158
- capabilityRefs: policy.capabilityRefs.map(capabilityBoundaryRefId),
1159
- admissionSubjectIds: policy.admissionSubjectIds,
1160
- blockedCapabilityRefs: blocked.map(
1161
- (admission) => capabilityBoundaryRefId(admission.capability)
1162
- )
1163
- }
1164
- };
1165
- state.emittedProposalDecisions.add(proposal.proposalId);
1166
- emitCapabilityGuard(ctx, "decision", decision);
1167
- emitCapabilityGuardStatus(ctx, state, {
1168
- state: outcome === "admit" ? "admitted" : "rejected",
1169
- workItemId: proposal.workItemId,
1170
- proposalId: proposal.proposalId,
1171
- actionKind: proposal.actionKind,
1172
- policyId: policy.policyId,
1173
- capabilityRefs: policy.capabilityRefs,
1174
- admissionSubjectIds: policy.admissionSubjectIds,
1175
- admissionIds: presentAdmissions.map((admission) => admission.admissionId),
1176
- sourceRefs: decision.sourceRefs,
1177
- metadata: { decisionId: decision.decisionId, admissionId: decision.admissionId }
1178
- });
1179
- }
1180
- function capabilityGuardProposalIssue(proposal) {
1181
- if (!isRecord2(proposal) || proposal.kind !== "work-item-domain-action-proposal" || typeof proposal.proposalId !== "string" || proposal.proposalId.length === 0 || typeof proposal.workItemId !== "string" || proposal.workItemId.length === 0 || typeof proposal.actionKind !== "string" || proposal.actionKind.length === 0 || proposal.sourceRefs !== void 0 && !isSourceRefArray(proposal.sourceRefs)) {
1182
- return dataIssue2(
1183
- "malformed-work-item-domain-action-capability-guard-proposal",
1184
- "WorkItem domain action capability guard requires valid proposal DATA facts",
1185
- { subjectId: recordString(proposal, "workItemId") }
1186
- );
1187
- }
1188
- return void 0;
1189
- }
1190
- function capabilityGuardPolicyIssue(policy) {
1191
- if (!isRecord2(policy) || policy.kind !== "work-item-domain-action-capability-guard-policy" || typeof policy.policyId !== "string" || policy.policyId.length === 0 || !Array.isArray(policy.capabilityRefs) || policy.capabilityRefs.length === 0 || !policy.capabilityRefs.every(isBoundaryCapabilityRef) || !Array.isArray(policy.admissionSubjectIds) || policy.admissionSubjectIds.length === 0 || !policy.admissionSubjectIds.every((id) => typeof id === "string" && id.length > 0) || policy.actionKinds !== void 0 && (!Array.isArray(policy.actionKinds) || !policy.actionKinds.every((kind) => typeof kind === "string" && kind.length > 0))) {
1192
- return dataIssue2(
1193
- "malformed-work-item-domain-action-capability-guard-policy",
1194
- "WorkItem domain action capability guard policy requires policyId, capabilityRefs, and admissionSubjectIds",
1195
- {
1196
- subjectId: recordString(policy, "policyId")
1197
- }
1064
+ const stale = staleRevision(proposal, admission, workItem);
1065
+ if (stale !== void 0) {
1066
+ state.appliedAdmissions.add(admission.admissionId);
1067
+ emitApplicationIssue(
1068
+ ctx,
1069
+ state,
1070
+ `stale:${admission.admissionId}:${stale.kind}:${stale.expected}:${stale.current}`,
1071
+ stale.kind,
1072
+ `Admission '${admission.admissionId}' targets stale WorkItem revision`,
1073
+ admission.workItemId,
1074
+ admissionRefs(admission, proposal),
1075
+ { expected: stale.expected, current: stale.current }
1198
1076
  );
1077
+ emitApplication(ctx, state, admission, proposal, "rejected", [], {
1078
+ code: stale.kind,
1079
+ message: "Stale WorkItem revision"
1080
+ });
1081
+ return;
1199
1082
  }
1200
- return void 0;
1201
- }
1202
- function capabilityAdmissionIssue(admission) {
1203
- if (!isRecord2(admission) || admission.kind !== "capability-admission" || typeof admission.admissionId !== "string" || admission.admissionId.length === 0 || typeof admission.proposalId !== "string" || admission.proposalId.length === 0 || typeof admission.subjectId !== "string" || admission.subjectId.length === 0 || !isBoundaryCapabilityRef(admission.capability) || !isCapabilityAdmissionState(admission.state) || typeof admission.decisionId !== "string" || admission.decisionId.length === 0 || admission.sourceRefs !== void 0 && !isSourceRefArray(admission.sourceRefs)) {
1204
- return dataIssue2(
1205
- "malformed-capability-admission",
1206
- "WorkItem domain action capability guard requires valid CapabilityAdmission DATA facts",
1207
- { subjectId: recordString(admission, "subjectId") }
1083
+ const policyResult = selectApplyPolicy(state, proposal, admission);
1084
+ if (typeof policyResult === "string") {
1085
+ emitApplicationIssue(
1086
+ ctx,
1087
+ state,
1088
+ `policy:${admission.admissionId}:${policyResult}`,
1089
+ policyResult,
1090
+ applyPolicyMessage(policyResult, proposal, admission),
1091
+ admission.workItemId,
1092
+ admissionRefs(admission, proposal)
1208
1093
  );
1094
+ return;
1209
1095
  }
1210
- return void 0;
1211
- }
1212
- function capabilityAdmissionStatusIssue(status) {
1213
- if (!isRecord2(status) || status.kind !== "capability-admission-status" || typeof status.statusId !== "string" || status.statusId.length === 0 || !isCapabilityAdmissionStatusState(status.state)) {
1214
- return dataIssue2(
1215
- "malformed-capability-admission-status",
1216
- "WorkItem domain action capability guard requires valid CapabilityAdmissionStatus DATA facts",
1217
- { subjectId: recordString(status, "subjectId") }
1218
- );
1096
+ const output = lowerAction(ctx, state, workItem, proposal, admission, policyResult);
1097
+ if (output === void 0) {
1098
+ state.appliedAdmissions.add(admission.admissionId);
1099
+ emitApplication(ctx, state, admission, proposal, "rejected", [], {
1100
+ code: "invalid-action",
1101
+ message: "WorkItem domain action application rejected; see emitted issues",
1102
+ policyId: policyResult.policyId
1103
+ });
1104
+ return;
1219
1105
  }
1220
- const hasCapabilityId = status.capabilityId !== void 0;
1221
- const hasCapabilityKind = status.capabilityKind !== void 0;
1222
- if (hasCapabilityId !== hasCapabilityKind || hasCapabilityId && (typeof status.capabilityId !== "string" || status.capabilityId.length === 0 || !isBoundaryCapabilityKind(status.capabilityKind)) || status.sourceRefs !== void 0 && !isSourceRefArray(status.sourceRefs) || status.issues !== void 0 && !isDataIssueArray(status.issues)) {
1223
- return dataIssue2(
1224
- "malformed-capability-admission-status",
1225
- "WorkItem domain action capability guard requires valid CapabilityAdmissionStatus DATA facts",
1226
- { subjectId: recordString(status, "subjectId") }
1106
+ const duplicateFactId = firstDuplicateFactId(state, output.facts);
1107
+ if (duplicateFactId !== void 0) {
1108
+ state.appliedAdmissions.add(admission.admissionId);
1109
+ emitApplicationIssue(
1110
+ ctx,
1111
+ state,
1112
+ `duplicate-authoring-fact:${admission.admissionId}:${duplicateFactId}`,
1113
+ "duplicate-suppressed",
1114
+ `WorkItem domain action application '${admission.admissionId}' would duplicate authoring fact '${duplicateFactId}'`,
1115
+ admission.workItemId,
1116
+ admissionRefs(admission, proposal, policyResult),
1117
+ { eventId: duplicateFactId }
1227
1118
  );
1119
+ emitApplication(ctx, state, admission, proposal, "duplicate", [], {
1120
+ code: "duplicate-suppressed",
1121
+ message: "Duplicate WorkItem authoring fact suppressed atomically",
1122
+ policyId: policyResult.policyId
1123
+ });
1124
+ return;
1228
1125
  }
1229
- return void 0;
1230
- }
1231
- function isCapabilityAdmissionState(value) {
1232
- return value === "allowed" || value === "blocked" || value === "deferred";
1233
- }
1234
- function isCapabilityAdmissionStatusState(value) {
1235
- return value === "capability-admission-allowed" || value === "capability-admission-blocked" || value === "capability-admission-deferred" || value === "capability-admission-issue";
1236
- }
1237
- function isSourceRefArray(value) {
1238
- return Array.isArray(value) && value.every(
1239
- (item) => isRecord2(item) && typeof item.kind === "string" && typeof item.id === "string"
1240
- );
1241
- }
1242
- function isDataIssueArray(value) {
1243
- return Array.isArray(value) && value.every(isDataIssue);
1244
- }
1245
- function isDataIssue(value) {
1246
- return isRecord2(value) && value.kind === "issue" && typeof value.code === "string" && value.code.length > 0 && typeof value.message === "string" && value.message.length > 0;
1247
- }
1248
- function isBoundaryCapabilityRef(value) {
1249
- return isRecord2(value) && typeof value.id === "string" && value.id.length > 0 && isBoundaryCapabilityKind(value.kind) && typeof value.required === "boolean" && (value.sourceRefs === void 0 || Array.isArray(value.sourceRefs) && value.sourceRefs.every((sourceRef) => typeof sourceRef === "string"));
1250
- }
1251
- function isBoundaryCapabilityKind(value) {
1252
- return value === "auth" || value === "permission" || value === "config" || value === "resource";
1253
- }
1254
- function selectCapabilityGuardPolicy(state, proposal) {
1255
- const explicitId = stringMetadata(proposal.metadata, "capabilityGuardPolicyId") ?? proposal.sourceRefs?.find(
1256
- (sourceRef) => sourceRef.kind === "work-item-domain-action-capability-guard-policy"
1257
- )?.id;
1258
- if (explicitId !== void 0)
1259
- return state.policies.get(explicitId) ?? "unknown-work-item-domain-action-capability-guard-policy";
1260
- const matching = [...state.policies.values()].filter(
1261
- (policy) => policy.actionKinds === void 0 || policy.actionKinds.includes(proposal.actionKind)
1262
- );
1263
- if (matching.length === 0) return "missing-policy";
1264
- if (matching.length > 1) return "policy-mismatch";
1265
- return matching[0];
1126
+ state.appliedAdmissions.add(admission.admissionId);
1127
+ for (const fact of output.facts) claimFactId(state, fact.eventId);
1128
+ for (const fact of output.facts) emitApplicationFact(ctx, "authoring-fact", fact);
1129
+ emitApplication(ctx, state, admission, proposal, output.state, output.facts, {
1130
+ code: output.code,
1131
+ message: output.message,
1132
+ policyId: policyResult.policyId
1133
+ });
1266
1134
  }
1267
- function latestCapabilityAdmission(state, policy, capabilityRef) {
1268
- const admissions = state.admissionsByCapability.get(capabilityBoundaryRefId(capabilityRef)) ?? [];
1269
- for (let index = admissions.length - 1; index >= 0; index -= 1) {
1270
- const admission = admissions[index];
1271
- if (admission !== void 0 && policy.admissionSubjectIds.includes(admission.subjectId))
1272
- return admission;
1135
+ function lowerAction(ctx, state, workItem, proposal, admission, policy) {
1136
+ if (!policyAllowsAction(policy, proposal.actionKind)) {
1137
+ emitApplicationIssue(
1138
+ ctx,
1139
+ state,
1140
+ `policy-action:${admission.admissionId}:${policy.policyId}:${proposal.actionKind}`,
1141
+ "policy-mismatch",
1142
+ `Apply policy '${policy.policyId}' does not allow actionKind '${proposal.actionKind}'`,
1143
+ proposal.workItemId,
1144
+ admissionRefs(admission, proposal, policy)
1145
+ );
1146
+ return { state: "policy-rejected", facts: [], code: "policy-mismatch" };
1273
1147
  }
1274
- return void 0;
1275
- }
1276
- function emitCapabilityStatusIssueForProposal(ctx, state, proposal, status) {
1277
- const policy = selectCapabilityGuardPolicy(state, proposal);
1278
- if (typeof policy === "string") return;
1279
- if (status.subjectId !== void 0 && !policy.admissionSubjectIds.includes(status.subjectId))
1280
- return;
1281
- const capabilityId = status.capabilityId;
1282
- const capabilityKind = status.capabilityKind;
1283
- const statusCapabilityKey = capabilityId === void 0 || capabilityKind === void 0 ? void 0 : capabilityBoundaryRefId({ id: capabilityId, kind: capabilityKind });
1284
- if (statusCapabilityKey !== void 0 && !policy.capabilityRefs.some(
1285
- (capabilityRef) => capabilityBoundaryRefId(capabilityRef) === statusCapabilityKey
1286
- ))
1287
- return;
1288
- const issue2 = status.issues?.[0] ?? dataIssue2(
1289
- "capability-admission-status-issue",
1290
- "Capability admission status reported an issue",
1291
- { subjectId: proposal.workItemId }
1292
- );
1293
- emitCapabilityGuardIssue(
1148
+ if (proposal.actionKind === "patch") {
1149
+ const lowered = lowerPatchAction(ctx, state, workItem, proposal, admission, policy);
1150
+ if (lowered === void 0) return void 0;
1151
+ return { state: "applied", facts: lowered };
1152
+ }
1153
+ if (proposal.actionKind === "mark-verified" || proposal.actionKind === "require-review") {
1154
+ return {
1155
+ state: "proposal-only",
1156
+ facts: [],
1157
+ code: "domain-fact-unimplemented",
1158
+ message: `Action '${proposal.actionKind}' recorded as visible application status only; no WorkItem domain mutation fact was emitted`
1159
+ };
1160
+ }
1161
+ if (proposal.actionKind === "spawn-proposed" || proposal.actionKind === "spawn-child") {
1162
+ return lowerSpawnAction(ctx, state, proposal, admission, policy);
1163
+ }
1164
+ emitApplicationIssue(
1294
1165
  ctx,
1295
1166
  state,
1296
- `capability-status:${proposal.proposalId}:${status.statusId}`,
1297
- dataIssue2(issue2.code, issue2.message, {
1298
- subjectId: proposal.workItemId,
1299
- refs: uniqueSourceRefs2([
1300
- ...capabilityGuardRefs(proposal, policy),
1301
- ...status.sourceRefs ?? []
1302
- ]),
1303
- metadata: {
1304
- ...statusCapabilityKey === void 0 ? {} : { capabilityRef: statusCapabilityKey },
1305
- capabilityAdmissionStatusId: status.statusId
1306
- }
1307
- })
1167
+ `unsupported-action:${admission.admissionId}:${proposal.actionKind}`,
1168
+ "unsupported-effect-kind",
1169
+ `Unsupported WorkItem domain actionKind '${proposal.actionKind}'`,
1170
+ proposal.workItemId,
1171
+ admissionRefs(admission, proposal, policy)
1308
1172
  );
1173
+ return { state: "rejected", facts: [], code: "unsupported-effect-kind" };
1309
1174
  }
1310
- function capabilityGuardPolicyMessage(code, proposal) {
1311
- if (code === "missing-policy")
1312
- return `WorkItem domain action '${proposal.proposalId}' has no capability guard policy`;
1313
- if (code === "unknown-work-item-domain-action-capability-guard-policy")
1314
- return `WorkItem domain action '${proposal.proposalId}' references a missing capability guard policy`;
1315
- return `WorkItem domain action '${proposal.proposalId}' matches multiple capability guard policies`;
1316
- }
1317
- function capabilityGuardRefs(proposal, policy, admissions = []) {
1318
- return uniqueSourceRefs2([
1319
- ref2("work-item", proposal.workItemId),
1320
- ref2("work-item-domain-action-proposal", proposal.proposalId),
1321
- ref2("work-item-domain-action-capability-guard-policy", policy.policyId),
1322
- ...admissions.filter((admission) => admission !== void 0).flatMap((admission) => [
1323
- ref2("capability-admission", admission.admissionId),
1324
- ref2("capability-admission-proposal", admission.proposalId),
1325
- ref2("boundary-capability", capabilityBoundaryRefId(admission.capability)),
1326
- ...admission.sourceRefs ?? []
1327
- ]),
1328
- ...proposal.sourceRefs ?? []
1329
- ]);
1330
- }
1331
- function capabilityBoundaryRefId(capability) {
1332
- return `${capability.kind}:${capability.id}`;
1333
- }
1334
- function emitCapabilityGuardStatus(ctx, state, status) {
1335
- state.statusSeq += 1;
1336
- const statusFact = {
1337
- kind: "work-item-domain-action-capability-guard-status",
1338
- statusId: `work-item-domain-action-capability-guard-status:${state.statusSeq}`,
1339
- ...status
1340
- };
1341
- emitCapabilityGuard(ctx, "status", statusFact);
1342
- state.auditSeq += 1;
1343
- emitCapabilityGuard(ctx, "audit", {
1344
- id: `work-item-domain-action-capability-guard-status:${state.auditSeq}`,
1345
- kind: "work-item-domain-action-capability-guard-status",
1346
- subjectId: status.workItemId,
1347
- message: status.message,
1348
- sourceRefs: status.sourceRefs,
1349
- metadata: {
1350
- statusId: statusFact.statusId,
1351
- state: status.state,
1352
- proposalId: status.proposalId,
1353
- policyId: status.policyId,
1354
- ...status.metadata ?? {}
1175
+ function lowerPatchAction(ctx, state, workItem, proposal, admission, policy) {
1176
+ const payload = normalizePatchPayload(proposal.payload);
1177
+ if (typeof payload === "string") {
1178
+ emitApplicationIssue(
1179
+ ctx,
1180
+ state,
1181
+ `invalid-patch:${admission.admissionId}`,
1182
+ "invalid-patch",
1183
+ payload,
1184
+ proposal.workItemId,
1185
+ admissionRefs(admission, proposal, policy)
1186
+ );
1187
+ return void 0;
1188
+ }
1189
+ const splitPayload = splitDedicatedPatchFields(payload, workItem);
1190
+ const facts = [];
1191
+ if (splitPayload.patch !== void 0) {
1192
+ const invalid = invalidPatchKeys(splitPayload.patch, policy);
1193
+ if (invalid.length > 0) {
1194
+ emitApplicationIssue(
1195
+ ctx,
1196
+ state,
1197
+ `invalid-patch-fields:${admission.admissionId}:${invalid.join(",")}`,
1198
+ "invalid-patch",
1199
+ `Patch action uses unsupported fields: ${invalid.join(", ")}`,
1200
+ proposal.workItemId,
1201
+ admissionRefs(admission, proposal, policy),
1202
+ { invalidFields: invalid }
1203
+ );
1204
+ return void 0;
1355
1205
  }
1356
- });
1357
- }
1358
- function emitCapabilityGuardStatusOnce(ctx, state, key, status) {
1359
- if (state.emittedStatusKeys.has(key)) return;
1360
- state.emittedStatusKeys.add(key);
1361
- emitCapabilityGuardStatus(ctx, state, status);
1362
- }
1363
- function emitCapabilityGuardIssue(ctx, state, key, issue2) {
1364
- if (state.issueKeys.has(key)) return;
1365
- state.issueKeys.add(key);
1366
- emitCapabilityGuard(ctx, "issue", issue2);
1367
- emitCapabilityGuardStatus(ctx, state, {
1368
- state: "issue",
1369
- workItemId: issue2.subjectId,
1370
- issues: [issue2],
1371
- message: issue2.message,
1372
- metadata: { issueCode: issue2.code }
1373
- });
1374
- }
1375
- function emitCapabilityGuard(ctx, kind, value) {
1376
- ctx.down([["DATA", { kind, value }]]);
1377
- }
1378
- function reduceProposalIntake(ctx, state, input, proposedAtMs) {
1379
- const proposal = normalizeProposal(input, proposedAtMs);
1380
- if (typeof proposal === "string") {
1381
- const issue2 = dataIssue2("malformed-domain-action-proposal", proposal);
1382
- emitIntake(ctx, "issue", issue2);
1383
- emitIntakeStatus(ctx, state, {
1384
- state: "rejected",
1385
- code: issue2.code,
1386
- message: issue2.message
1206
+ facts.push({
1207
+ kind: "work-item-patched",
1208
+ eventId: splitPayload.eventId ?? `${admission.admissionId}:work-item-patched`,
1209
+ workItemId: workItem.workItemId,
1210
+ patch: splitPayload.patch,
1211
+ executionRelevantFields: splitPayload.executionRelevantFields ?? policy.patch?.executionRelevantFields,
1212
+ authorId: splitPayload.authorId,
1213
+ sourceRefs: actionSourceRefs(admission, proposal, policy),
1214
+ metadata: { ...splitPayload.metadata ?? {}, actionKind: proposal.actionKind }
1387
1215
  });
1388
- return;
1389
1216
  }
1390
- if (state.proposals.has(proposal.proposalId)) {
1391
- emitIntakeStatus(ctx, state, {
1392
- state: "duplicate",
1393
- code: "duplicate-suppressed",
1394
- workItemId: proposal.workItemId,
1395
- proposalId: proposal.proposalId,
1396
- actionKind: proposal.actionKind,
1397
- message: `Duplicate WorkItem domain action proposal '${proposal.proposalId}' suppressed`
1217
+ if (splitPayload.acceptanceCriteria !== void 0) {
1218
+ if (!allowsAcceptanceCriteria(policy)) {
1219
+ emitApplicationIssue(
1220
+ ctx,
1221
+ state,
1222
+ `ac-policy:${admission.admissionId}`,
1223
+ "policy-mismatch",
1224
+ `Apply policy '${policy.policyId}' does not allow acceptance criteria changes`,
1225
+ proposal.workItemId,
1226
+ admissionRefs(admission, proposal, policy)
1227
+ );
1228
+ return void 0;
1229
+ }
1230
+ const issues = validateAcceptanceCriteria(splitPayload.acceptanceCriteria, {
1231
+ workItemId: workItem.workItemId
1232
+ });
1233
+ if (issues.length > 0) {
1234
+ for (const item of issues) emitApplicationFact(ctx, "issue", item);
1235
+ emitApplicationStatus(ctx, state, {
1236
+ state: "rejected",
1237
+ code: issues[0]?.code,
1238
+ message: issues[0]?.message,
1239
+ workItemId: workItem.workItemId,
1240
+ proposalId: proposal.proposalId,
1241
+ admissionId: admission.admissionId,
1242
+ actionKind: proposal.actionKind,
1243
+ sourceRefs: admissionRefs(admission, proposal, policy)
1244
+ });
1245
+ return void 0;
1246
+ }
1247
+ facts.push({
1248
+ kind: "acceptance-criteria-changed",
1249
+ eventId: `${admission.admissionId}:acceptance-criteria-changed`,
1250
+ workItemId: workItem.workItemId,
1251
+ acceptanceCriteria: splitPayload.acceptanceCriteria,
1252
+ authorId: splitPayload.authorId,
1253
+ sourceRefs: actionSourceRefs(admission, proposal, policy),
1254
+ metadata: { ...splitPayload.metadata ?? {}, actionKind: proposal.actionKind }
1398
1255
  });
1399
- return;
1400
- }
1401
- state.proposals.add(proposal.proposalId);
1402
- emitIntake(ctx, "proposal", proposal);
1403
- emitIntakeStatus(ctx, state, {
1404
- state: "proposed",
1405
- workItemId: proposal.workItemId,
1406
- proposalId: proposal.proposalId,
1407
- actionKind: proposal.actionKind,
1408
- sourceRefs: proposal.sourceRefs
1409
- });
1410
- }
1411
- function normalizeProposal(input, proposedAtMs) {
1412
- if (!isRecord2(input)) return "WorkItemDomainActionProposal intake must be an object";
1413
- if (input.kind !== "work-item-domain-action-proposal" && input.kind !== "work-item-domain-action-proposal-intake")
1414
- return "WorkItemDomainActionProposal intake has unsupported kind";
1415
- if (typeof input.proposalId !== "string" || input.proposalId.trim() === "")
1416
- return "WorkItemDomainActionProposal.proposalId is required";
1417
- if (typeof input.workItemId !== "string" || input.workItemId.trim() === "")
1418
- return "WorkItemDomainActionProposal.workItemId is required";
1419
- if (typeof input.actionKind !== "string" || input.actionKind.trim() === "")
1420
- return "WorkItemDomainActionProposal.actionKind is required";
1421
- if (input.kind === "work-item-domain-action-proposal") {
1422
- if (typeof input.effectRunId !== "string")
1423
- return "WorkItemDomainActionProposal.effectRunId is required";
1424
- if (typeof input.effectRunResultId !== "string")
1425
- return "WorkItemDomainActionProposal.effectRunResultId is required";
1426
- if (typeof input.evidenceId !== "string")
1427
- return "WorkItemDomainActionProposal.evidenceId is required";
1428
- if (typeof input.policyId !== "string")
1429
- return "WorkItemDomainActionProposal.policyId is required";
1430
- return input;
1431
1256
  }
1432
- return workItemDomainActionProposal(
1433
- input.proposalId,
1434
- input.workItemId,
1435
- input.actionKind,
1436
- {
1437
- payload: input.payload,
1438
- reason: typeof input.reason === "string" ? input.reason : void 0,
1439
- policyId: typeof input.policyId === "string" ? input.policyId : void 0,
1440
- effectRunId: typeof input.effectRunId === "string" ? input.effectRunId : void 0,
1441
- effectRunResultId: typeof input.effectRunResultId === "string" ? input.effectRunResultId : void 0,
1442
- evidenceId: typeof input.evidenceId === "string" ? input.evidenceId : void 0,
1443
- proposedAtMs: typeof input.proposedAtMs === "number" ? input.proposedAtMs : proposedAtMs,
1444
- sourceRefs: sourceRefs(input.sourceRefs),
1445
- metadata: isRecord2(input.metadata) ? input.metadata : void 0
1257
+ if (splitPayload.verificationPlan !== void 0) {
1258
+ if (!allowsVerificationPlan(policy)) {
1259
+ emitApplicationIssue(
1260
+ ctx,
1261
+ state,
1262
+ `plan-policy:${admission.admissionId}`,
1263
+ "policy-mismatch",
1264
+ `Apply policy '${policy.policyId}' does not allow verification plan changes`,
1265
+ proposal.workItemId,
1266
+ admissionRefs(admission, proposal, policy)
1267
+ );
1268
+ return void 0;
1446
1269
  }
1447
- );
1448
- }
1449
- function applyAdmission(ctx, state, admission) {
1450
- if (state.appliedAdmissions.has(admission.admissionId)) return;
1451
- const proposal = state.proposals.get(admission.proposalId);
1452
- if (proposal === void 0) {
1270
+ const issues = validateVerificationPlan(
1271
+ splitPayload.verificationPlan,
1272
+ splitPayload.acceptanceCriteria ?? workItem.acceptanceCriteria ?? [],
1273
+ { workItemId: workItem.workItemId }
1274
+ );
1275
+ if (issues.length > 0) {
1276
+ for (const item of issues) emitApplicationFact(ctx, "issue", item);
1277
+ emitApplicationStatus(ctx, state, {
1278
+ state: "rejected",
1279
+ code: issues[0]?.code,
1280
+ message: issues[0]?.message,
1281
+ workItemId: workItem.workItemId,
1282
+ proposalId: proposal.proposalId,
1283
+ admissionId: admission.admissionId,
1284
+ actionKind: proposal.actionKind,
1285
+ sourceRefs: admissionRefs(admission, proposal, policy)
1286
+ });
1287
+ return void 0;
1288
+ }
1289
+ facts.push({
1290
+ kind: "verification-plan-changed",
1291
+ eventId: `${admission.admissionId}:verification-plan-changed`,
1292
+ workItemId: workItem.workItemId,
1293
+ verificationPlan: splitPayload.verificationPlan,
1294
+ authorId: splitPayload.authorId,
1295
+ sourceRefs: actionSourceRefs(admission, proposal, policy),
1296
+ metadata: { ...splitPayload.metadata ?? {}, actionKind: proposal.actionKind }
1297
+ });
1298
+ }
1299
+ if (facts.length === 0) {
1453
1300
  emitApplicationIssue(
1454
1301
  ctx,
1455
1302
  state,
1456
- `missing-proposal:${admission.admissionId}:${admission.proposalId}`,
1457
- "dangling-ref",
1458
- `Admission '${admission.admissionId}' cannot apply without proposal '${admission.proposalId}'`,
1459
- admission.workItemId,
1460
- admissionRefs(admission)
1303
+ `empty-patch:${admission.admissionId}`,
1304
+ "invalid-patch",
1305
+ "Patch action payload did not contain patch, acceptanceCriteria, or verificationPlan",
1306
+ proposal.workItemId,
1307
+ admissionRefs(admission, proposal, policy)
1461
1308
  );
1462
- return;
1309
+ return void 0;
1463
1310
  }
1464
- if (admission.state !== "admitted") {
1465
- state.appliedAdmissions.add(admission.admissionId);
1466
- emitApplication(ctx, state, admission, proposal, admission.state, []);
1467
- return;
1311
+ return facts;
1312
+ }
1313
+ function lowerSpawnAction(ctx, state, proposal, admission, policy) {
1314
+ if (policy.spawn?.create !== true) {
1315
+ return {
1316
+ state: "proposal-only",
1317
+ facts: [],
1318
+ message: "Spawn action remains proposal/admission only without explicit create policy"
1319
+ };
1468
1320
  }
1469
- const mismatch = admissionProposalMismatch(admission, proposal);
1470
- if (mismatch !== void 0) {
1471
- state.appliedAdmissions.add(admission.admissionId);
1321
+ const policyIssue = validateSpawnCreatePolicy(policy, proposal);
1322
+ if (policyIssue !== void 0) {
1472
1323
  emitApplicationIssue(
1473
1324
  ctx,
1474
1325
  state,
1475
- `admission-proposal-mismatch:${admission.admissionId}:${mismatch.field}`,
1476
- "dangling-ref",
1477
- mismatch.message,
1478
- admission.workItemId,
1479
- admissionRefs(admission, proposal),
1480
- {
1481
- field: mismatch.field,
1482
- admissionValue: mismatch.admissionValue,
1483
- proposalValue: mismatch.proposalValue
1484
- }
1326
+ `spawn-create-policy:${admission.admissionId}:${policy.policyId}:${policyIssue.field}`,
1327
+ "policy-mismatch",
1328
+ policyIssue.message,
1329
+ proposal.workItemId,
1330
+ admissionRefs(admission, proposal, policy),
1331
+ { field: policyIssue.field }
1485
1332
  );
1486
- emitApplication(ctx, state, admission, proposal, "rejected", [], {
1487
- code: "dangling-ref",
1488
- message: mismatch.message
1489
- });
1490
- return;
1333
+ return {
1334
+ state: "policy-rejected",
1335
+ facts: [],
1336
+ code: "policy-mismatch",
1337
+ message: policyIssue.message
1338
+ };
1491
1339
  }
1492
- const workItem = state.workItems.get(admission.workItemId);
1493
- if (workItem === void 0) {
1340
+ const payload = normalizeSpawnPayload(proposal.payload);
1341
+ if (typeof payload === "string") {
1494
1342
  emitApplicationIssue(
1495
1343
  ctx,
1496
1344
  state,
1497
- `missing-work-item:${admission.admissionId}:${admission.workItemId}`,
1498
- "dangling-ref",
1499
- `Admission '${admission.admissionId}' references unknown WorkItem '${admission.workItemId}'`,
1500
- admission.workItemId,
1501
- admissionRefs(admission, proposal)
1345
+ `invalid-spawn-payload:${admission.admissionId}`,
1346
+ "invalid-patch",
1347
+ payload,
1348
+ proposal.workItemId,
1349
+ admissionRefs(admission, proposal, policy)
1502
1350
  );
1503
- return;
1351
+ return { state: "rejected", facts: [], code: "invalid-patch", message: payload };
1504
1352
  }
1505
- const metadataConflict = conflictingApplicationMetadata(proposal, admission);
1506
- if (metadataConflict !== void 0) {
1507
- state.appliedAdmissions.add(admission.admissionId);
1353
+ const childWorkItemId = payload.childWorkItemId ?? payload.workItemId ?? policy.spawn.childWorkItemId ?? (policy.spawn.childIdPrefix === void 0 ? void 0 : `${policy.spawn.childIdPrefix}${admission.admissionId}`);
1354
+ if (childWorkItemId === void 0 || childWorkItemId.trim() === "") {
1355
+ const message = "Spawn create action requires a childWorkItemId or create policy child id";
1508
1356
  emitApplicationIssue(
1509
1357
  ctx,
1510
1358
  state,
1511
- `metadata-conflict:${admission.admissionId}:${metadataConflict.field}`,
1512
- metadataConflict.code,
1513
- metadataConflict.message,
1514
- admission.workItemId,
1515
- admissionRefs(admission, proposal),
1516
- {
1517
- field: metadataConflict.field,
1518
- admissionValue: metadataConflict.admissionValue,
1519
- proposalValue: metadataConflict.proposalValue
1520
- }
1359
+ `missing-spawn-child-id:${admission.admissionId}`,
1360
+ "missing-required-field",
1361
+ message,
1362
+ proposal.workItemId,
1363
+ admissionRefs(admission, proposal, policy)
1521
1364
  );
1522
- emitApplication(ctx, state, admission, proposal, "rejected", [], {
1523
- code: metadataConflict.code,
1524
- message: metadataConflict.message
1525
- });
1526
- return;
1365
+ return { state: "rejected", facts: [], code: "missing-required-field", message };
1527
1366
  }
1528
- const stale = staleRevision(proposal, admission, workItem);
1529
- if (stale !== void 0) {
1530
- state.appliedAdmissions.add(admission.admissionId);
1367
+ if (state.workItems.has(childWorkItemId)) {
1368
+ const message = `Spawn create action would duplicate WorkItem '${childWorkItemId}'`;
1531
1369
  emitApplicationIssue(
1532
1370
  ctx,
1533
1371
  state,
1534
- `stale:${admission.admissionId}:${stale.kind}:${stale.expected}:${stale.current}`,
1535
- stale.kind,
1536
- `Admission '${admission.admissionId}' targets stale WorkItem revision`,
1537
- admission.workItemId,
1538
- admissionRefs(admission, proposal),
1539
- { expected: stale.expected, current: stale.current }
1372
+ `duplicate-spawn-child:${admission.admissionId}:${childWorkItemId}`,
1373
+ "duplicate-id",
1374
+ message,
1375
+ childWorkItemId,
1376
+ admissionRefs(admission, proposal, policy)
1540
1377
  );
1541
- emitApplication(ctx, state, admission, proposal, "rejected", [], {
1542
- code: stale.kind,
1543
- message: "Stale WorkItem revision"
1544
- });
1545
- return;
1378
+ return { state: "duplicate", facts: [], code: "duplicate-id", message };
1546
1379
  }
1547
- const policyResult = selectApplyPolicy(state, proposal, admission);
1548
- if (typeof policyResult === "string") {
1380
+ if (payload.draft === void 0) {
1381
+ const message = "Spawn create action requires a draft payload";
1549
1382
  emitApplicationIssue(
1550
1383
  ctx,
1551
1384
  state,
1552
- `policy:${admission.admissionId}:${policyResult}`,
1553
- policyResult,
1554
- applyPolicyMessage(policyResult, proposal, admission),
1555
- admission.workItemId,
1556
- admissionRefs(admission, proposal)
1385
+ `missing-spawn-draft:${admission.admissionId}`,
1386
+ "missing-required-field",
1387
+ message,
1388
+ childWorkItemId,
1389
+ admissionRefs(admission, proposal, policy)
1557
1390
  );
1558
- return;
1391
+ return { state: "rejected", facts: [], code: "missing-required-field", message };
1559
1392
  }
1560
- const output = lowerAction(ctx, state, workItem, proposal, admission, policyResult);
1561
- if (output === void 0) {
1562
- state.appliedAdmissions.add(admission.admissionId);
1563
- emitApplication(ctx, state, admission, proposal, "rejected", [], {
1564
- code: "invalid-action",
1565
- message: "WorkItem domain action application rejected; see emitted issues",
1566
- policyId: policyResult.policyId
1567
- });
1568
- return;
1393
+ const issues = validateWorkItemDraft(payload.draft, { workItemId: childWorkItemId });
1394
+ if (issues.length > 0) {
1395
+ for (const item of issues) emitApplicationFact(ctx, "issue", item);
1396
+ return {
1397
+ state: "rejected",
1398
+ facts: [],
1399
+ code: issues[0]?.code,
1400
+ message: issues[0]?.message
1401
+ };
1569
1402
  }
1570
- const duplicateFactId = firstDuplicateFactId(state, output.facts);
1571
- if (duplicateFactId !== void 0) {
1572
- state.appliedAdmissions.add(admission.admissionId);
1573
- emitApplicationIssue(
1574
- ctx,
1575
- state,
1576
- `duplicate-authoring-fact:${admission.admissionId}:${duplicateFactId}`,
1577
- "duplicate-suppressed",
1578
- `WorkItem domain action application '${admission.admissionId}' would duplicate authoring fact '${duplicateFactId}'`,
1579
- admission.workItemId,
1580
- admissionRefs(admission, proposal, policyResult),
1581
- { eventId: duplicateFactId }
1582
- );
1583
- emitApplication(ctx, state, admission, proposal, "duplicate", [], {
1584
- code: "duplicate-suppressed",
1585
- message: "Duplicate WorkItem authoring fact suppressed atomically",
1586
- policyId: policyResult.policyId
1587
- });
1588
- return;
1403
+ const metadata = {
1404
+ ...payload.metadata ?? {},
1405
+ actionKind: proposal.actionKind,
1406
+ parentWorkItemId: policy.spawn.linkParent === true ? proposal.workItemId : void 0,
1407
+ sourceProposalId: proposal.proposalId,
1408
+ idempotencyKey: payload.idempotencyKey ?? policy.spawn.idempotencyKey
1409
+ };
1410
+ const created = {
1411
+ kind: "work-item-created",
1412
+ eventId: payload.eventId ?? `${admission.admissionId}:work-item-created:${childWorkItemId}`,
1413
+ workItemId: childWorkItemId,
1414
+ draft: payload.draft,
1415
+ authorId: payload.authorId,
1416
+ sourceRefs: actionSourceRefs(admission, proposal, policy),
1417
+ metadata
1418
+ };
1419
+ return { state: "applied", facts: [created] };
1420
+ }
1421
+ function validateSpawnCreatePolicy(policy, proposal) {
1422
+ if (policy.spawn?.idempotencyKey === void 0 || policy.spawn.idempotencyKey.trim() === "") {
1423
+ return {
1424
+ field: "idempotencyKey",
1425
+ message: `Spawn create policy '${policy.policyId}' requires an idempotencyKey`
1426
+ };
1589
1427
  }
1590
- state.appliedAdmissions.add(admission.admissionId);
1591
- for (const fact of output.facts) claimFactId(state, fact.eventId);
1592
- for (const fact of output.facts) emitApplicationFact(ctx, "authoring-fact", fact);
1593
- emitApplication(ctx, state, admission, proposal, output.state, output.facts, {
1594
- code: output.code,
1595
- message: output.message,
1596
- policyId: policyResult.policyId
1597
- });
1428
+ if (policy.spawn.maxChildrenPerAdmission === void 0 || policy.spawn.maxChildrenPerAdmission < 1) {
1429
+ return {
1430
+ field: "maxChildrenPerAdmission",
1431
+ message: `Spawn create policy '${policy.policyId}' requires maxChildrenPerAdmission >= 1`
1432
+ };
1433
+ }
1434
+ if (proposal.actionKind === "spawn-child" && policy.spawn.linkParent !== true) {
1435
+ return {
1436
+ field: "linkParent",
1437
+ message: `Spawn child create policy '${policy.policyId}' requires linkParent: true`
1438
+ };
1439
+ }
1440
+ return void 0;
1598
1441
  }
1599
- function lowerAction(ctx, state, workItem, proposal, admission, policy) {
1600
- if (!policyAllowsAction(policy, proposal.actionKind)) {
1601
- emitApplicationIssue(
1602
- ctx,
1603
- state,
1604
- `policy-action:${admission.admissionId}:${policy.policyId}:${proposal.actionKind}`,
1605
- "policy-mismatch",
1606
- `Apply policy '${policy.policyId}' does not allow actionKind '${proposal.actionKind}'`,
1607
- proposal.workItemId,
1608
- admissionRefs(admission, proposal, policy)
1609
- );
1610
- return { state: "policy-rejected", facts: [], code: "policy-mismatch" };
1442
+ function normalizeSpawnPayload(payload) {
1443
+ if (!isRecord(payload)) return "Spawn action payload must be an object";
1444
+ if ("draft" in payload && payload.draft !== void 0 && !isRecord(payload.draft)) {
1445
+ return "Spawn action payload.draft must be an object";
1611
1446
  }
1612
- if (proposal.actionKind === "patch") {
1613
- const lowered = lowerPatchAction(ctx, state, workItem, proposal, admission, policy);
1614
- if (lowered === void 0) return void 0;
1615
- return { state: "applied", facts: lowered };
1447
+ if ("metadata" in payload && payload.metadata !== void 0 && !isRecord(payload.metadata)) {
1448
+ return "Spawn action payload.metadata must be an object";
1616
1449
  }
1617
- if (proposal.actionKind === "mark-verified" || proposal.actionKind === "require-review") {
1450
+ return payload;
1451
+ }
1452
+ function normalizePatchPayload(payload) {
1453
+ if (!isRecord(payload)) return "Patch action payload must be an object";
1454
+ if ("patch" in payload || "acceptanceCriteria" in payload || "verificationPlan" in payload || "executionRelevantFields" in payload) {
1455
+ if ("patch" in payload && payload.patch !== void 0 && !isRecord(payload.patch))
1456
+ return "Patch action payload.patch must be an object";
1457
+ if ("acceptanceCriteria" in payload && payload.acceptanceCriteria !== void 0 && !Array.isArray(payload.acceptanceCriteria))
1458
+ return "Patch action payload.acceptanceCriteria must be an array";
1459
+ if ("verificationPlan" in payload && payload.verificationPlan !== void 0 && !isRecord(payload.verificationPlan))
1460
+ return "Patch action payload.verificationPlan must be an object";
1461
+ const inlinePatch = inlinePatchFields(payload);
1462
+ const explicitPatch = isRecord(payload.patch) ? payload.patch : void 0;
1463
+ const patch = explicitPatch === void 0 ? inlinePatch : { ...inlinePatch, ...explicitPatch };
1618
1464
  return {
1619
- state: "proposal-only",
1620
- facts: [],
1621
- code: "domain-fact-unimplemented",
1622
- message: `Action '${proposal.actionKind}' recorded as visible application status only; no WorkItem domain mutation fact was emitted`
1465
+ ...payload,
1466
+ patch: Object.keys(patch).length === 0 ? void 0 : patch
1623
1467
  };
1624
1468
  }
1625
- if (proposal.actionKind === "spawn-proposed" || proposal.actionKind === "spawn-child") {
1626
- return lowerSpawnAction(ctx, state, proposal, admission, policy);
1469
+ return { patch: payload };
1470
+ }
1471
+ function inlinePatchFields(payload) {
1472
+ const patch = {};
1473
+ for (const [key, value] of Object.entries(payload)) {
1474
+ if (key === "metadata") continue;
1475
+ if (patchFieldKeys.has(key)) patch[key] = value;
1627
1476
  }
1628
- emitApplicationIssue(
1629
- ctx,
1630
- state,
1631
- `unsupported-action:${admission.admissionId}:${proposal.actionKind}`,
1632
- "unsupported-effect-kind",
1633
- `Unsupported WorkItem domain actionKind '${proposal.actionKind}'`,
1634
- proposal.workItemId,
1635
- admissionRefs(admission, proposal, policy)
1477
+ return patch;
1478
+ }
1479
+ function splitDedicatedPatchFields(payload, workItem) {
1480
+ if (!isRecord(payload.patch)) return payload;
1481
+ const patchRecord = payload.patch;
1482
+ const patch = {};
1483
+ for (const [key, value] of Object.entries(patchRecord)) {
1484
+ if (key === "acceptanceCriteria" || key === "verificationPlan" || key === "verificationSteps")
1485
+ continue;
1486
+ patch[key] = value;
1487
+ }
1488
+ const acceptanceCriteria = payload.acceptanceCriteria ?? (Array.isArray(patchRecord.acceptanceCriteria) ? patchRecord.acceptanceCriteria : void 0);
1489
+ const verificationPlan = payload.verificationPlan ?? (isRecord(patchRecord.verificationPlan) ? patchRecord.verificationPlan : Array.isArray(patchRecord.verificationSteps) ? {
1490
+ ...workItem.verificationPlan ?? { planId: "default" },
1491
+ steps: patchRecord.verificationSteps
1492
+ } : void 0);
1493
+ return {
1494
+ ...payload,
1495
+ patch: Object.keys(patch).length === 0 ? void 0 : patch,
1496
+ acceptanceCriteria,
1497
+ verificationPlan
1498
+ };
1499
+ }
1500
+ function invalidPatchKeys(patch, policy) {
1501
+ const allowed = new Set(policy.patch?.allowedFields ?? Array.from(patchFieldKeys));
1502
+ return Object.keys(patch).filter((key) => !patchFieldKeys.has(key) || !allowed.has(key));
1503
+ }
1504
+ function allowsAcceptanceCriteria(policy) {
1505
+ if (policy.patch?.allowAcceptanceCriteria === false) return false;
1506
+ const allowed = policy.patch?.allowedFields;
1507
+ return allowed === void 0 || allowed.includes("acceptanceCriteria") || policy.patch?.allowAcceptanceCriteria === true;
1508
+ }
1509
+ function allowsVerificationPlan(policy) {
1510
+ if (policy.patch?.allowVerificationPlan === false) return false;
1511
+ const allowed = policy.patch?.allowedFields;
1512
+ return allowed === void 0 || allowed.includes("verificationPlan") || allowed.includes("verificationSteps") || policy.patch?.allowVerificationPlan === true;
1513
+ }
1514
+ function selectApplyPolicy(state, proposal, admission) {
1515
+ const explicitId = explicitApplyPolicyId(proposal, admission);
1516
+ if (explicitId !== void 0)
1517
+ return state.policies.get(explicitId) ?? "unknown-work-item-domain-action-apply-policy";
1518
+ const matching = Array.from(state.policies.values()).filter(
1519
+ (policy) => policyAllowsAction(policy, proposal.actionKind)
1520
+ );
1521
+ if (matching.length === 0) return "missing-policy";
1522
+ if (matching.length > 1) return "policy-mismatch";
1523
+ return matching[0];
1524
+ }
1525
+ function explicitApplyPolicyId(proposal, admission) {
1526
+ const metadataPolicy = stringMetadata(admission.metadata, "applyPolicyId") ?? stringMetadata(proposal.metadata, "applyPolicyId");
1527
+ if (metadataPolicy !== void 0) return metadataPolicy;
1528
+ return [...admission.sourceRefs ?? [], ...proposal.sourceRefs ?? []].find(
1529
+ (sourceRef) => sourceRef.kind === "work-item-domain-action-apply-policy"
1530
+ )?.id;
1531
+ }
1532
+ function policyAllowsAction(policy, actionKind) {
1533
+ return policy.actionKinds === void 0 || policy.actionKinds.includes(actionKind);
1534
+ }
1535
+ function admissionProposalMismatch(admission, proposal) {
1536
+ if (admission.workItemId !== proposal.workItemId)
1537
+ return {
1538
+ field: "workItemId",
1539
+ admissionValue: admission.workItemId,
1540
+ proposalValue: proposal.workItemId,
1541
+ message: `Admission '${admission.admissionId}' workItemId does not match proposal '${proposal.proposalId}'`
1542
+ };
1543
+ if (admission.actionKind !== proposal.actionKind)
1544
+ return {
1545
+ field: "actionKind",
1546
+ admissionValue: admission.actionKind,
1547
+ proposalValue: proposal.actionKind,
1548
+ message: `Admission '${admission.admissionId}' actionKind does not match proposal '${proposal.proposalId}'`
1549
+ };
1550
+ return void 0;
1551
+ }
1552
+ function conflictingApplicationMetadata(proposal, admission) {
1553
+ const authoringRevision = conflictingMetadataNumber(proposal, admission, "authoringRevision");
1554
+ if (authoringRevision !== void 0)
1555
+ return {
1556
+ field: "authoringRevision",
1557
+ code: "stale-revision",
1558
+ ...authoringRevision,
1559
+ message: `Admission '${admission.admissionId}' carries conflicting authoringRevision metadata`
1560
+ };
1561
+ const executionInputRevision = conflictingMetadataNumber(
1562
+ proposal,
1563
+ admission,
1564
+ "executionInputRevision"
1565
+ );
1566
+ if (executionInputRevision !== void 0)
1567
+ return {
1568
+ field: "executionInputRevision",
1569
+ code: "stale-execution-input",
1570
+ ...executionInputRevision,
1571
+ message: `Admission '${admission.admissionId}' carries conflicting executionInputRevision metadata`
1572
+ };
1573
+ const applyPolicyId = conflictingMetadataString(proposal, admission, "applyPolicyId");
1574
+ if (applyPolicyId !== void 0)
1575
+ return {
1576
+ field: "applyPolicyId",
1577
+ code: "policy-mismatch",
1578
+ ...applyPolicyId,
1579
+ message: `Admission '${admission.admissionId}' carries conflicting applyPolicyId metadata`
1580
+ };
1581
+ return void 0;
1582
+ }
1583
+ function conflictingMetadataNumber(proposal, admission, key) {
1584
+ const proposalValue = numberMetadata(proposal.metadata, key);
1585
+ const admissionValue = numberMetadata(admission.metadata, key);
1586
+ if (proposalValue === void 0 || admissionValue === void 0 || proposalValue === admissionValue)
1587
+ return void 0;
1588
+ return { proposalValue, admissionValue };
1589
+ }
1590
+ function conflictingMetadataString(proposal, admission, key) {
1591
+ const proposalValue = stringMetadata(proposal.metadata, key);
1592
+ const admissionValue = stringMetadata(admission.metadata, key);
1593
+ if (proposalValue === void 0 || admissionValue === void 0 || proposalValue === admissionValue)
1594
+ return void 0;
1595
+ return { proposalValue, admissionValue };
1596
+ }
1597
+ function staleRevision(proposal, admission, workItem) {
1598
+ const authoringRevision = numberMetadata(admission.metadata, "authoringRevision") ?? numberMetadata(proposal.metadata, "authoringRevision");
1599
+ if (authoringRevision !== void 0 && authoringRevision !== workItem.authoringRevision)
1600
+ return {
1601
+ kind: "stale-revision",
1602
+ expected: authoringRevision,
1603
+ current: workItem.authoringRevision
1604
+ };
1605
+ const executionInputRevision = numberMetadata(admission.metadata, "executionInputRevision") ?? numberMetadata(proposal.metadata, "executionInputRevision");
1606
+ if (executionInputRevision !== void 0 && executionInputRevision !== workItem.executionInputRevision)
1607
+ return {
1608
+ kind: "stale-execution-input",
1609
+ expected: executionInputRevision,
1610
+ current: workItem.executionInputRevision
1611
+ };
1612
+ return void 0;
1613
+ }
1614
+ function applyPolicyMessage(code, proposal, admission) {
1615
+ if (code === "missing-policy")
1616
+ return `Admitted action '${admission.admissionId}' requires an explicit apply policy`;
1617
+ if (code === "unknown-work-item-domain-action-apply-policy")
1618
+ return `Admitted action '${admission.admissionId}' references a missing apply policy`;
1619
+ return `Admitted action '${proposal.proposalId}' matches multiple apply policies`;
1620
+ }
1621
+ function emitApplication(ctx, state, admission, proposal, applicationState, facts, opts = {}) {
1622
+ const producedFactIds = facts.map((fact) => fact.eventId);
1623
+ const application = {
1624
+ kind: "work-item-domain-action-application",
1625
+ applicationId: `${admission.admissionId}:application`,
1626
+ admissionId: admission.admissionId,
1627
+ proposalId: proposal.proposalId,
1628
+ workItemId: proposal.workItemId,
1629
+ actionKind: proposal.actionKind,
1630
+ state: applicationState,
1631
+ producedFactIds,
1632
+ reason: admission.reason ?? proposal.reason,
1633
+ sourceRefs: admissionRefs(admission, proposal),
1634
+ metadata: { policyId: opts.policyId, code: opts.code }
1635
+ };
1636
+ emitApplicationFact(ctx, "application", application);
1637
+ emitApplicationStatus(ctx, state, {
1638
+ state: applicationState,
1639
+ code: opts.code,
1640
+ message: opts.message,
1641
+ workItemId: proposal.workItemId,
1642
+ proposalId: proposal.proposalId,
1643
+ admissionId: admission.admissionId,
1644
+ actionKind: proposal.actionKind,
1645
+ sourceRefs: application.sourceRefs,
1646
+ metadata: { producedFactIds, policyId: opts.policyId }
1647
+ });
1648
+ }
1649
+ function emitApplicationIssue(ctx, state, key, code, message, workItemId, sourceRefs2, metadata) {
1650
+ if (state.issueKeys.has(key)) return;
1651
+ state.issueKeys.add(key);
1652
+ const item = dataIssue2(code, message, { subjectId: workItemId, refs: sourceRefs2, metadata });
1653
+ emitApplicationFact(ctx, "issue", item);
1654
+ emitApplicationStatus(ctx, state, {
1655
+ state: code === "missing-policy" ? "missing-policy" : "rejected",
1656
+ code,
1657
+ message,
1658
+ workItemId,
1659
+ sourceRefs: sourceRefs2,
1660
+ metadata
1661
+ });
1662
+ }
1663
+ function emitApplicationStatus(ctx, state, status) {
1664
+ state.statusSeq += 1;
1665
+ const statusFact = {
1666
+ kind: "work-item-domain-action-status",
1667
+ statusId: `work-item-domain-action-application-status:${state.statusSeq}`,
1668
+ ...status
1669
+ };
1670
+ emitApplicationFact(ctx, "status", statusFact);
1671
+ emitApplicationAudit(ctx, state, "work-item-domain-action-application-status", statusFact);
1672
+ }
1673
+ function emitApplicationAudit(ctx, state, kind, status) {
1674
+ state.auditSeq += 1;
1675
+ emitApplicationFact(ctx, "audit", auditRecord(kind, state.auditSeq, status));
1676
+ }
1677
+ function emitApplicationFact(ctx, kind, value) {
1678
+ ctx.down([["DATA", { kind, value }]]);
1679
+ }
1680
+ function firstDuplicateFactId(state, facts) {
1681
+ const pending = /* @__PURE__ */ new Set();
1682
+ for (const fact of facts) {
1683
+ if (state.emittedFactIds.has(fact.eventId) || pending.has(fact.eventId)) return fact.eventId;
1684
+ pending.add(fact.eventId);
1685
+ }
1686
+ return void 0;
1687
+ }
1688
+ function claimFactId(state, factId) {
1689
+ if (state.emittedFactIds.has(factId)) return false;
1690
+ state.emittedFactIds.add(factId);
1691
+ return true;
1692
+ }
1693
+ function admissionRefs(admission, proposal, policy) {
1694
+ return uniqueSourceRefs2([
1695
+ ref2("work-item-domain-action-admission", admission.admissionId),
1696
+ ref2("work-item-domain-action-proposal", admission.proposalId),
1697
+ ...proposal === void 0 ? [] : [
1698
+ ref2("work-item", proposal.workItemId),
1699
+ ref2("work-item-evidence", proposal.evidenceId),
1700
+ ...proposal.sourceRefs ?? []
1701
+ ],
1702
+ ...policy === void 0 ? [] : [ref2("work-item-domain-action-apply-policy", policy.policyId)],
1703
+ ...admission.sourceRefs ?? []
1704
+ ]);
1705
+ }
1706
+ function actionSourceRefs(admission, proposal, policy) {
1707
+ return admissionRefs(admission, proposal, policy);
1708
+ }
1709
+
1710
+ // src/solutions/work-item/actions-builders.ts
1711
+ function workItemDomainActionProposal(proposalId, workItemId, actionKind, opts = {}) {
1712
+ return {
1713
+ kind: "work-item-domain-action-proposal",
1714
+ proposalId,
1715
+ workItemId,
1716
+ actionKind,
1717
+ effectRunId: opts.effectRunId ?? `domain-action:${proposalId}:effect-run`,
1718
+ effectRunResultId: opts.effectRunResultId ?? `domain-action:${proposalId}:effect-run-result`,
1719
+ evidenceId: opts.evidenceId ?? `domain-action:${proposalId}:evidence`,
1720
+ policyId: opts.policyId ?? "domain-action-proposal",
1721
+ payload: opts.payload,
1722
+ reason: opts.reason,
1723
+ sourceRefs: opts.sourceRefs,
1724
+ proposedAtMs: opts.proposedAtMs,
1725
+ metadata: opts.metadata
1726
+ };
1727
+ }
1728
+ function workItemDomainActionProposalIntake(proposalId, workItemId, actionKind, opts = {}) {
1729
+ return {
1730
+ kind: "work-item-domain-action-proposal-intake",
1731
+ proposalId,
1732
+ workItemId,
1733
+ actionKind,
1734
+ payload: opts.payload,
1735
+ reason: opts.reason,
1736
+ policyId: opts.policyId,
1737
+ effectRunId: opts.effectRunId,
1738
+ effectRunResultId: opts.effectRunResultId,
1739
+ evidenceId: opts.evidenceId,
1740
+ proposedAtMs: opts.proposedAtMs,
1741
+ sourceRefs: opts.sourceRefs,
1742
+ metadata: opts.metadata
1743
+ };
1744
+ }
1745
+ function workItemDomainActionApplyPolicy(policyId, opts = {}) {
1746
+ return {
1747
+ kind: "work-item-domain-action-apply-policy",
1748
+ policyId,
1749
+ actionKinds: opts.actionKinds,
1750
+ patch: opts.patch,
1751
+ spawn: opts.spawn,
1752
+ metadata: opts.metadata
1753
+ };
1754
+ }
1755
+
1756
+ // src/solutions/work-item/actions-capability-guard.ts
1757
+ function workItemDomainActionCapabilityGuardProjector(graph, opts) {
1758
+ const name = opts.name ?? "workItemDomainActionCapabilityGuard";
1759
+ const deps = opts.capabilityAdmissionStatus === void 0 ? [opts.proposals, opts.capabilityAdmissions, opts.guardPolicies] : [
1760
+ opts.proposals,
1761
+ opts.capabilityAdmissions,
1762
+ opts.guardPolicies,
1763
+ opts.capabilityAdmissionStatus
1764
+ ];
1765
+ const statusIndex = opts.capabilityAdmissionStatus === void 0 ? -1 : 3;
1766
+ const now = opts.now ?? Date.now;
1767
+ const runtime = graph.node(
1768
+ deps,
1769
+ (ctx) => {
1770
+ const state = ctx.state.get() ?? {
1771
+ proposals: /* @__PURE__ */ new Map(),
1772
+ admissionsByCapability: /* @__PURE__ */ new Map(),
1773
+ issueStatusesByCapability: /* @__PURE__ */ new Map(),
1774
+ issueStatusesWithoutCapability: [],
1775
+ policies: /* @__PURE__ */ new Map(),
1776
+ emittedProposalDecisions: /* @__PURE__ */ new Set(),
1777
+ emittedStatusKeys: /* @__PURE__ */ new Set(),
1778
+ issueKeys: /* @__PURE__ */ new Set(),
1779
+ statusSeq: 0,
1780
+ auditSeq: 0
1781
+ };
1782
+ for (const raw of depBatch(ctx, 2) ?? []) {
1783
+ const policyIssue = capabilityGuardPolicyIssue(raw);
1784
+ if (policyIssue !== void 0) {
1785
+ emitCapabilityGuardIssue(
1786
+ ctx,
1787
+ state,
1788
+ `malformed-policy:${recordString(raw, "policyId") ?? "unknown"}`,
1789
+ policyIssue
1790
+ );
1791
+ continue;
1792
+ }
1793
+ const policy = raw;
1794
+ state.policies.set(policy.policyId, policy);
1795
+ }
1796
+ for (const raw of depBatch(ctx, 1) ?? []) {
1797
+ const admissionIssue = capabilityAdmissionIssue(raw);
1798
+ if (admissionIssue !== void 0) {
1799
+ emitCapabilityGuardIssue(
1800
+ ctx,
1801
+ state,
1802
+ `malformed-admission:${recordString(raw, "admissionId") ?? "unknown"}`,
1803
+ admissionIssue
1804
+ );
1805
+ continue;
1806
+ }
1807
+ const admission = raw;
1808
+ const capabilityKey = capabilityBoundaryRefId(admission.capability);
1809
+ const byCapability = state.admissionsByCapability.get(capabilityKey) ?? [];
1810
+ if (!byCapability.some((item) => item.admissionId === admission.admissionId)) {
1811
+ state.admissionsByCapability.set(capabilityKey, [...byCapability, admission]);
1812
+ }
1813
+ }
1814
+ if (statusIndex >= 0) {
1815
+ for (const raw of depBatch(ctx, statusIndex) ?? []) {
1816
+ const statusIssue = capabilityAdmissionStatusIssue(raw);
1817
+ if (statusIssue !== void 0) {
1818
+ emitCapabilityGuardIssue(
1819
+ ctx,
1820
+ state,
1821
+ `malformed-capability-status:${recordString(raw, "statusId") ?? "unknown"}`,
1822
+ statusIssue
1823
+ );
1824
+ continue;
1825
+ }
1826
+ const status = raw;
1827
+ if (status.state !== "capability-admission-issue") continue;
1828
+ const capabilityId = status.capabilityId;
1829
+ const capabilityKind = status.capabilityKind;
1830
+ if (capabilityId !== void 0 && capabilityKind !== void 0) {
1831
+ const capabilityKey = capabilityBoundaryRefId({
1832
+ id: capabilityId,
1833
+ kind: capabilityKind
1834
+ });
1835
+ const statuses = state.issueStatusesByCapability.get(capabilityKey) ?? [];
1836
+ if (!statuses.some((item) => item.statusId === status.statusId)) {
1837
+ state.issueStatusesByCapability.set(capabilityKey, [...statuses, status]);
1838
+ }
1839
+ } else if (!state.issueStatusesWithoutCapability.some((item) => item.statusId === status.statusId)) {
1840
+ state.issueStatusesWithoutCapability.push(status);
1841
+ }
1842
+ for (const proposal of state.proposals.values())
1843
+ emitCapabilityStatusIssueForProposal(ctx, state, proposal, status);
1844
+ }
1845
+ }
1846
+ for (const raw of depBatch(ctx, 0) ?? []) {
1847
+ const proposalIssue = capabilityGuardProposalIssue(raw);
1848
+ if (proposalIssue !== void 0) {
1849
+ emitCapabilityGuardIssue(
1850
+ ctx,
1851
+ state,
1852
+ `malformed-proposal:${recordString(raw, "proposalId") ?? "unknown"}`,
1853
+ proposalIssue
1854
+ );
1855
+ continue;
1856
+ }
1857
+ const proposal = raw;
1858
+ state.proposals.set(proposal.proposalId, proposal);
1859
+ }
1860
+ for (const proposal of state.proposals.values())
1861
+ evaluateCapabilityGuardProposal(ctx, state, proposal, now());
1862
+ ctx.state.set(state);
1863
+ },
1864
+ {
1865
+ name: `${name}/runtime`,
1866
+ factory: "workItemDomainActionCapabilityGuardProjector",
1867
+ partial: true,
1868
+ completeWhenDepsComplete: false,
1869
+ errorWhenDepsError: false
1870
+ }
1871
+ );
1872
+ return {
1873
+ decisions: project(
1874
+ graph,
1875
+ runtime,
1876
+ `${name}/decisions`,
1877
+ "workItemDomainActionCapabilityGuardDecisions",
1878
+ (fact) => fact.kind === "decision" ? fact.value : void 0
1879
+ ),
1880
+ status: project(
1881
+ graph,
1882
+ runtime,
1883
+ `${name}/status`,
1884
+ "workItemDomainActionCapabilityGuardStatus",
1885
+ (fact) => fact.kind === "status" ? fact.value : void 0
1886
+ ),
1887
+ issues: project(
1888
+ graph,
1889
+ runtime,
1890
+ `${name}/issues`,
1891
+ "workItemDomainActionCapabilityGuardIssues",
1892
+ (fact) => fact.kind === "issue" ? fact.value : void 0
1893
+ ),
1894
+ audit: project(
1895
+ graph,
1896
+ runtime,
1897
+ `${name}/audit`,
1898
+ "workItemDomainActionCapabilityGuardAudit",
1899
+ (fact) => fact.kind === "audit" ? fact.value : void 0
1900
+ )
1901
+ };
1902
+ }
1903
+ function evaluateCapabilityGuardProposal(ctx, state, proposal, decidedAtMs) {
1904
+ if (state.emittedProposalDecisions.has(proposal.proposalId)) return;
1905
+ const policy = selectCapabilityGuardPolicy(state, proposal);
1906
+ if (typeof policy === "string") {
1907
+ emitCapabilityGuardIssue(
1908
+ ctx,
1909
+ state,
1910
+ `policy:${proposal.proposalId}:${policy}`,
1911
+ dataIssue2(policy, capabilityGuardPolicyMessage(policy, proposal), {
1912
+ subjectId: proposal.workItemId,
1913
+ refs: [ref2("work-item-domain-action-proposal", proposal.proposalId)]
1914
+ })
1915
+ );
1916
+ return;
1917
+ }
1918
+ for (const status of state.issueStatusesWithoutCapability)
1919
+ emitCapabilityStatusIssueForProposal(ctx, state, proposal, status);
1920
+ for (const capabilityRef of policy.capabilityRefs) {
1921
+ for (const status of state.issueStatusesByCapability.get(
1922
+ capabilityBoundaryRefId(capabilityRef)
1923
+ ) ?? [])
1924
+ emitCapabilityStatusIssueForProposal(ctx, state, proposal, status);
1925
+ }
1926
+ const admissions = policy.capabilityRefs.map(
1927
+ (capabilityRef) => latestCapabilityAdmission(state, policy, capabilityRef)
1928
+ );
1929
+ const missingCapabilityRefs = policy.capabilityRefs.filter(
1930
+ (_, index) => admissions[index] === void 0
1636
1931
  );
1637
- return { state: "rejected", facts: [], code: "unsupported-effect-kind" };
1638
- }
1639
- function lowerPatchAction(ctx, state, workItem, proposal, admission, policy) {
1640
- const payload = normalizePatchPayload(proposal.payload);
1641
- if (typeof payload === "string") {
1642
- emitApplicationIssue(
1932
+ const deferredCapabilityRefs = admissions.filter((admission) => admission !== void 0).filter((admission) => admission.state === "deferred").map((admission) => capabilityBoundaryRefId(admission.capability));
1933
+ if (missingCapabilityRefs.length > 0 || deferredCapabilityRefs.length > 0) {
1934
+ emitCapabilityGuardStatusOnce(
1643
1935
  ctx,
1644
1936
  state,
1645
- `invalid-patch:${admission.admissionId}`,
1646
- "invalid-patch",
1647
- payload,
1648
- proposal.workItemId,
1649
- admissionRefs(admission, proposal, policy)
1650
- );
1651
- return void 0;
1652
- }
1653
- const splitPayload = splitDedicatedPatchFields(payload, workItem);
1654
- const facts = [];
1655
- if (splitPayload.patch !== void 0) {
1656
- const invalid = invalidPatchKeys(splitPayload.patch, policy);
1657
- if (invalid.length > 0) {
1658
- emitApplicationIssue(
1659
- ctx,
1660
- state,
1661
- `invalid-patch-fields:${admission.admissionId}:${invalid.join(",")}`,
1662
- "invalid-patch",
1663
- `Patch action uses unsupported fields: ${invalid.join(", ")}`,
1664
- proposal.workItemId,
1665
- admissionRefs(admission, proposal, policy),
1666
- { invalidFields: invalid }
1667
- );
1668
- return void 0;
1669
- }
1670
- facts.push({
1671
- kind: "work-item-patched",
1672
- eventId: splitPayload.eventId ?? `${admission.admissionId}:work-item-patched`,
1673
- workItemId: workItem.workItemId,
1674
- patch: splitPayload.patch,
1675
- executionRelevantFields: splitPayload.executionRelevantFields ?? policy.patch?.executionRelevantFields,
1676
- authorId: splitPayload.authorId,
1677
- sourceRefs: actionSourceRefs(admission, proposal, policy),
1678
- metadata: { ...splitPayload.metadata ?? {}, actionKind: proposal.actionKind }
1679
- });
1680
- }
1681
- if (splitPayload.acceptanceCriteria !== void 0) {
1682
- if (!allowsAcceptanceCriteria(policy)) {
1683
- emitApplicationIssue(
1684
- ctx,
1685
- state,
1686
- `ac-policy:${admission.admissionId}`,
1687
- "policy-mismatch",
1688
- `Apply policy '${policy.policyId}' does not allow acceptance criteria changes`,
1689
- proposal.workItemId,
1690
- admissionRefs(admission, proposal, policy)
1691
- );
1692
- return void 0;
1693
- }
1694
- const issues = validateAcceptanceCriteria(splitPayload.acceptanceCriteria, {
1695
- workItemId: workItem.workItemId
1696
- });
1697
- if (issues.length > 0) {
1698
- for (const item of issues) emitApplicationFact(ctx, "issue", item);
1699
- emitApplicationStatus(ctx, state, {
1700
- state: "rejected",
1701
- code: issues[0]?.code,
1702
- message: issues[0]?.message,
1703
- workItemId: workItem.workItemId,
1937
+ `deferred:${proposal.proposalId}:${policy.policyId}:missing=${missingCapabilityRefs.map(capabilityBoundaryRefId).join(",")}:deferred=${deferredCapabilityRefs.join(",")}`,
1938
+ {
1939
+ state: "deferred",
1940
+ workItemId: proposal.workItemId,
1704
1941
  proposalId: proposal.proposalId,
1705
- admissionId: admission.admissionId,
1706
1942
  actionKind: proposal.actionKind,
1707
- sourceRefs: admissionRefs(admission, proposal, policy)
1708
- });
1709
- return void 0;
1710
- }
1711
- facts.push({
1712
- kind: "acceptance-criteria-changed",
1713
- eventId: `${admission.admissionId}:acceptance-criteria-changed`,
1714
- workItemId: workItem.workItemId,
1715
- acceptanceCriteria: splitPayload.acceptanceCriteria,
1716
- authorId: splitPayload.authorId,
1717
- sourceRefs: actionSourceRefs(admission, proposal, policy),
1718
- metadata: { ...splitPayload.metadata ?? {}, actionKind: proposal.actionKind }
1719
- });
1943
+ policyId: policy.policyId,
1944
+ capabilityRefs: policy.capabilityRefs,
1945
+ admissionSubjectIds: policy.admissionSubjectIds,
1946
+ message: "WorkItem domain action is waiting for capability admission",
1947
+ sourceRefs: capabilityGuardRefs(proposal, policy, admissions),
1948
+ metadata: {
1949
+ missingCapabilityRefs: missingCapabilityRefs.map(capabilityBoundaryRefId),
1950
+ deferredCapabilityRefs
1951
+ }
1952
+ }
1953
+ );
1954
+ return;
1720
1955
  }
1721
- if (splitPayload.verificationPlan !== void 0) {
1722
- if (!allowsVerificationPlan(policy)) {
1723
- emitApplicationIssue(
1724
- ctx,
1725
- state,
1726
- `plan-policy:${admission.admissionId}`,
1727
- "policy-mismatch",
1728
- `Apply policy '${policy.policyId}' does not allow verification plan changes`,
1729
- proposal.workItemId,
1730
- admissionRefs(admission, proposal, policy)
1731
- );
1732
- return void 0;
1956
+ const presentAdmissions = admissions.filter(
1957
+ (admission) => admission !== void 0
1958
+ );
1959
+ const blocked = presentAdmissions.filter((admission) => admission.state === "blocked");
1960
+ const outcome = blocked.length === 0 ? "admit" : "reject";
1961
+ const decision = {
1962
+ kind: "work-item-domain-action-admission-decision",
1963
+ decisionId: `capability-guard:${proposal.proposalId}:decision`,
1964
+ admissionId: `capability-guard:${proposal.proposalId}:admission`,
1965
+ proposalId: proposal.proposalId,
1966
+ outcome,
1967
+ reason: blocked.length === 0 ? "Capability guard admitted required capabilities" : "Capability guard rejected blocked capabilities",
1968
+ decidedAtMs,
1969
+ sourceRefs: capabilityGuardRefs(proposal, policy, presentAdmissions),
1970
+ metadata: {
1971
+ capabilityGuardPolicyId: policy.policyId,
1972
+ capabilityRefs: policy.capabilityRefs.map(capabilityBoundaryRefId),
1973
+ admissionSubjectIds: policy.admissionSubjectIds,
1974
+ blockedCapabilityRefs: blocked.map(
1975
+ (admission) => capabilityBoundaryRefId(admission.capability)
1976
+ )
1733
1977
  }
1734
- const issues = validateVerificationPlan(
1735
- splitPayload.verificationPlan,
1736
- splitPayload.acceptanceCriteria ?? workItem.acceptanceCriteria ?? [],
1737
- { workItemId: workItem.workItemId }
1978
+ };
1979
+ state.emittedProposalDecisions.add(proposal.proposalId);
1980
+ emitCapabilityGuard(ctx, "decision", decision);
1981
+ emitCapabilityGuardStatus(ctx, state, {
1982
+ state: outcome === "admit" ? "admitted" : "rejected",
1983
+ workItemId: proposal.workItemId,
1984
+ proposalId: proposal.proposalId,
1985
+ actionKind: proposal.actionKind,
1986
+ policyId: policy.policyId,
1987
+ capabilityRefs: policy.capabilityRefs,
1988
+ admissionSubjectIds: policy.admissionSubjectIds,
1989
+ admissionIds: presentAdmissions.map((admission) => admission.admissionId),
1990
+ sourceRefs: decision.sourceRefs,
1991
+ metadata: { decisionId: decision.decisionId, admissionId: decision.admissionId }
1992
+ });
1993
+ }
1994
+ function capabilityGuardProposalIssue(proposal) {
1995
+ if (!isRecord(proposal) || proposal.kind !== "work-item-domain-action-proposal" || typeof proposal.proposalId !== "string" || proposal.proposalId.length === 0 || typeof proposal.workItemId !== "string" || proposal.workItemId.length === 0 || typeof proposal.actionKind !== "string" || proposal.actionKind.length === 0 || proposal.sourceRefs !== void 0 && !isSourceRefArray(proposal.sourceRefs)) {
1996
+ return dataIssue2(
1997
+ "malformed-work-item-domain-action-capability-guard-proposal",
1998
+ "WorkItem domain action capability guard requires valid proposal DATA facts",
1999
+ { subjectId: recordString(proposal, "workItemId") }
1738
2000
  );
1739
- if (issues.length > 0) {
1740
- for (const item of issues) emitApplicationFact(ctx, "issue", item);
1741
- emitApplicationStatus(ctx, state, {
1742
- state: "rejected",
1743
- code: issues[0]?.code,
1744
- message: issues[0]?.message,
1745
- workItemId: workItem.workItemId,
1746
- proposalId: proposal.proposalId,
1747
- admissionId: admission.admissionId,
1748
- actionKind: proposal.actionKind,
1749
- sourceRefs: admissionRefs(admission, proposal, policy)
1750
- });
1751
- return void 0;
1752
- }
1753
- facts.push({
1754
- kind: "verification-plan-changed",
1755
- eventId: `${admission.admissionId}:verification-plan-changed`,
1756
- workItemId: workItem.workItemId,
1757
- verificationPlan: splitPayload.verificationPlan,
1758
- authorId: splitPayload.authorId,
1759
- sourceRefs: actionSourceRefs(admission, proposal, policy),
1760
- metadata: { ...splitPayload.metadata ?? {}, actionKind: proposal.actionKind }
1761
- });
1762
2001
  }
1763
- if (facts.length === 0) {
1764
- emitApplicationIssue(
1765
- ctx,
1766
- state,
1767
- `empty-patch:${admission.admissionId}`,
1768
- "invalid-patch",
1769
- "Patch action payload did not contain patch, acceptanceCriteria, or verificationPlan",
1770
- proposal.workItemId,
1771
- admissionRefs(admission, proposal, policy)
2002
+ return void 0;
2003
+ }
2004
+ function capabilityGuardPolicyIssue(policy) {
2005
+ if (!isRecord(policy) || policy.kind !== "work-item-domain-action-capability-guard-policy" || typeof policy.policyId !== "string" || policy.policyId.length === 0 || !Array.isArray(policy.capabilityRefs) || policy.capabilityRefs.length === 0 || !policy.capabilityRefs.every(isBoundaryCapabilityRef) || !Array.isArray(policy.admissionSubjectIds) || policy.admissionSubjectIds.length === 0 || !policy.admissionSubjectIds.every((id) => typeof id === "string" && id.length > 0) || policy.actionKinds !== void 0 && (!Array.isArray(policy.actionKinds) || !policy.actionKinds.every((kind) => typeof kind === "string" && kind.length > 0))) {
2006
+ return dataIssue2(
2007
+ "malformed-work-item-domain-action-capability-guard-policy",
2008
+ "WorkItem domain action capability guard policy requires policyId, capabilityRefs, and admissionSubjectIds",
2009
+ {
2010
+ subjectId: recordString(policy, "policyId")
2011
+ }
1772
2012
  );
1773
- return void 0;
1774
2013
  }
1775
- return facts;
2014
+ return void 0;
1776
2015
  }
1777
- function lowerSpawnAction(ctx, state, proposal, admission, policy) {
1778
- if (policy.spawn?.create !== true) {
1779
- return {
1780
- state: "proposal-only",
1781
- facts: [],
1782
- message: "Spawn action remains proposal/admission only without explicit create policy"
1783
- };
2016
+ function capabilityAdmissionIssue(admission) {
2017
+ if (!isRecord(admission) || admission.kind !== "capability-admission" || typeof admission.admissionId !== "string" || admission.admissionId.length === 0 || typeof admission.proposalId !== "string" || admission.proposalId.length === 0 || typeof admission.subjectId !== "string" || admission.subjectId.length === 0 || !isBoundaryCapabilityRef(admission.capability) || !isCapabilityAdmissionState(admission.state) || typeof admission.decisionId !== "string" || admission.decisionId.length === 0 || admission.sourceRefs !== void 0 && !isSourceRefArray(admission.sourceRefs)) {
2018
+ return dataIssue2(
2019
+ "malformed-capability-admission",
2020
+ "WorkItem domain action capability guard requires valid CapabilityAdmission DATA facts",
2021
+ { subjectId: recordString(admission, "subjectId") }
2022
+ );
1784
2023
  }
1785
- emitApplicationIssue(
1786
- ctx,
1787
- state,
1788
- `spawn-create-policy:${admission.admissionId}:${policy.policyId}`,
1789
- "policy-mismatch",
1790
- `Spawn create policy '${policy.policyId}' is incomplete: child creation requires an explicit create/link/idempotency policy surface`,
1791
- proposal.workItemId,
1792
- admissionRefs(admission, proposal, policy),
1793
- {
1794
- requires: ["linkParent", "idempotencyKey", "maxChildrenPerAdmission", "link-fact-vocabulary"]
1795
- }
1796
- );
1797
- return {
1798
- state: "proposal-only",
1799
- facts: [],
1800
- code: "policy-mismatch",
1801
- message: "Spawn action remains proposal/admission only until explicit create/link policy is available"
1802
- };
2024
+ return void 0;
1803
2025
  }
1804
- function normalizePatchPayload(payload) {
1805
- if (!isRecord2(payload)) return "Patch action payload must be an object";
1806
- if ("patch" in payload || "acceptanceCriteria" in payload || "verificationPlan" in payload || "executionRelevantFields" in payload) {
1807
- if ("patch" in payload && payload.patch !== void 0 && !isRecord2(payload.patch))
1808
- return "Patch action payload.patch must be an object";
1809
- if ("acceptanceCriteria" in payload && payload.acceptanceCriteria !== void 0 && !Array.isArray(payload.acceptanceCriteria))
1810
- return "Patch action payload.acceptanceCriteria must be an array";
1811
- if ("verificationPlan" in payload && payload.verificationPlan !== void 0 && !isRecord2(payload.verificationPlan))
1812
- return "Patch action payload.verificationPlan must be an object";
1813
- const inlinePatch = inlinePatchFields(payload);
1814
- const explicitPatch = isRecord2(payload.patch) ? payload.patch : void 0;
1815
- const patch = explicitPatch === void 0 ? inlinePatch : { ...inlinePatch, ...explicitPatch };
1816
- return {
1817
- ...payload,
1818
- patch: Object.keys(patch).length === 0 ? void 0 : patch
1819
- };
2026
+ function capabilityAdmissionStatusIssue(status) {
2027
+ if (!isRecord(status) || status.kind !== "capability-admission-status" || typeof status.statusId !== "string" || status.statusId.length === 0 || !isCapabilityAdmissionStatusState(status.state)) {
2028
+ return dataIssue2(
2029
+ "malformed-capability-admission-status",
2030
+ "WorkItem domain action capability guard requires valid CapabilityAdmissionStatus DATA facts",
2031
+ { subjectId: recordString(status, "subjectId") }
2032
+ );
1820
2033
  }
1821
- return { patch: payload };
1822
- }
1823
- function inlinePatchFields(payload) {
1824
- const patch = {};
1825
- for (const [key, value] of Object.entries(payload)) {
1826
- if (key === "metadata") continue;
1827
- if (patchFieldKeys.has(key)) patch[key] = value;
2034
+ const hasCapabilityId = status.capabilityId !== void 0;
2035
+ const hasCapabilityKind = status.capabilityKind !== void 0;
2036
+ if (hasCapabilityId !== hasCapabilityKind || hasCapabilityId && (typeof status.capabilityId !== "string" || status.capabilityId.length === 0 || !isBoundaryCapabilityKind(status.capabilityKind)) || status.sourceRefs !== void 0 && !isSourceRefArray(status.sourceRefs) || status.issues !== void 0 && !isDataIssueArray(status.issues)) {
2037
+ return dataIssue2(
2038
+ "malformed-capability-admission-status",
2039
+ "WorkItem domain action capability guard requires valid CapabilityAdmissionStatus DATA facts",
2040
+ { subjectId: recordString(status, "subjectId") }
2041
+ );
1828
2042
  }
1829
- return patch;
2043
+ return void 0;
1830
2044
  }
1831
- function splitDedicatedPatchFields(payload, workItem) {
1832
- if (!isRecord2(payload.patch)) return payload;
1833
- const patchRecord = payload.patch;
1834
- const patch = {};
1835
- for (const [key, value] of Object.entries(patchRecord)) {
1836
- if (key === "acceptanceCriteria" || key === "verificationPlan" || key === "verificationSteps")
1837
- continue;
1838
- patch[key] = value;
1839
- }
1840
- const acceptanceCriteria = payload.acceptanceCriteria ?? (Array.isArray(patchRecord.acceptanceCriteria) ? patchRecord.acceptanceCriteria : void 0);
1841
- const verificationPlan = payload.verificationPlan ?? (isRecord2(patchRecord.verificationPlan) ? patchRecord.verificationPlan : Array.isArray(patchRecord.verificationSteps) ? {
1842
- ...workItem.verificationPlan ?? { planId: "default" },
1843
- steps: patchRecord.verificationSteps
1844
- } : void 0);
1845
- return {
1846
- ...payload,
1847
- patch: Object.keys(patch).length === 0 ? void 0 : patch,
1848
- acceptanceCriteria,
1849
- verificationPlan
1850
- };
2045
+ function isCapabilityAdmissionState(value) {
2046
+ return value === "allowed" || value === "blocked" || value === "deferred";
1851
2047
  }
1852
- function invalidPatchKeys(patch, policy) {
1853
- const allowed = new Set(policy.patch?.allowedFields ?? Array.from(patchFieldKeys));
1854
- return Object.keys(patch).filter((key) => !patchFieldKeys.has(key) || !allowed.has(key));
2048
+ function isCapabilityAdmissionStatusState(value) {
2049
+ return value === "capability-admission-allowed" || value === "capability-admission-blocked" || value === "capability-admission-deferred" || value === "capability-admission-issue";
1855
2050
  }
1856
- function allowsAcceptanceCriteria(policy) {
1857
- if (policy.patch?.allowAcceptanceCriteria === false) return false;
1858
- const allowed = policy.patch?.allowedFields;
1859
- return allowed === void 0 || allowed.includes("acceptanceCriteria") || policy.patch?.allowAcceptanceCriteria === true;
2051
+ function isSourceRefArray(value) {
2052
+ return Array.isArray(value) && value.every(
2053
+ (item) => isRecord(item) && typeof item.kind === "string" && typeof item.id === "string"
2054
+ );
1860
2055
  }
1861
- function allowsVerificationPlan(policy) {
1862
- if (policy.patch?.allowVerificationPlan === false) return false;
1863
- const allowed = policy.patch?.allowedFields;
1864
- return allowed === void 0 || allowed.includes("verificationPlan") || allowed.includes("verificationSteps") || policy.patch?.allowVerificationPlan === true;
2056
+ function isDataIssueArray(value) {
2057
+ return Array.isArray(value) && value.every(isDataIssue);
1865
2058
  }
1866
- function selectApplyPolicy(state, proposal, admission) {
1867
- const explicitId = explicitApplyPolicyId(proposal, admission);
2059
+ function isDataIssue(value) {
2060
+ return isRecord(value) && value.kind === "issue" && typeof value.code === "string" && value.code.length > 0 && typeof value.message === "string" && value.message.length > 0;
2061
+ }
2062
+ function isBoundaryCapabilityRef(value) {
2063
+ return isRecord(value) && typeof value.id === "string" && value.id.length > 0 && isBoundaryCapabilityKind(value.kind) && typeof value.required === "boolean" && (value.sourceRefs === void 0 || Array.isArray(value.sourceRefs) && value.sourceRefs.every((sourceRef) => typeof sourceRef === "string"));
2064
+ }
2065
+ function isBoundaryCapabilityKind(value) {
2066
+ return value === "auth" || value === "permission" || value === "config" || value === "resource";
2067
+ }
2068
+ function selectCapabilityGuardPolicy(state, proposal) {
2069
+ const explicitId = stringMetadata(proposal.metadata, "capabilityGuardPolicyId") ?? proposal.sourceRefs?.find(
2070
+ (sourceRef) => sourceRef.kind === "work-item-domain-action-capability-guard-policy"
2071
+ )?.id;
1868
2072
  if (explicitId !== void 0)
1869
- return state.policies.get(explicitId) ?? "unknown-work-item-domain-action-apply-policy";
1870
- const matching = Array.from(state.policies.values()).filter(
1871
- (policy) => policyAllowsAction(policy, proposal.actionKind)
2073
+ return state.policies.get(explicitId) ?? "unknown-work-item-domain-action-capability-guard-policy";
2074
+ const matching = [...state.policies.values()].filter(
2075
+ (policy) => policy.actionKinds === void 0 || policy.actionKinds.includes(proposal.actionKind)
1872
2076
  );
1873
2077
  if (matching.length === 0) return "missing-policy";
1874
2078
  if (matching.length > 1) return "policy-mismatch";
1875
2079
  return matching[0];
1876
2080
  }
1877
- function explicitApplyPolicyId(proposal, admission) {
1878
- const metadataPolicy = stringMetadata(admission.metadata, "applyPolicyId") ?? stringMetadata(proposal.metadata, "applyPolicyId");
1879
- if (metadataPolicy !== void 0) return metadataPolicy;
1880
- return [...admission.sourceRefs ?? [], ...proposal.sourceRefs ?? []].find(
1881
- (sourceRef) => sourceRef.kind === "work-item-domain-action-apply-policy"
1882
- )?.id;
1883
- }
1884
- function policyAllowsAction(policy, actionKind) {
1885
- return policy.actionKinds === void 0 || policy.actionKinds.includes(actionKind);
1886
- }
1887
- function admissionProposalMismatch(admission, proposal) {
1888
- if (admission.workItemId !== proposal.workItemId)
1889
- return {
1890
- field: "workItemId",
1891
- admissionValue: admission.workItemId,
1892
- proposalValue: proposal.workItemId,
1893
- message: `Admission '${admission.admissionId}' workItemId does not match proposal '${proposal.proposalId}'`
1894
- };
1895
- if (admission.actionKind !== proposal.actionKind)
1896
- return {
1897
- field: "actionKind",
1898
- admissionValue: admission.actionKind,
1899
- proposalValue: proposal.actionKind,
1900
- message: `Admission '${admission.admissionId}' actionKind does not match proposal '${proposal.proposalId}'`
1901
- };
2081
+ function latestCapabilityAdmission(state, policy, capabilityRef) {
2082
+ const admissions = state.admissionsByCapability.get(capabilityBoundaryRefId(capabilityRef)) ?? [];
2083
+ for (let index = admissions.length - 1; index >= 0; index -= 1) {
2084
+ const admission = admissions[index];
2085
+ if (admission !== void 0 && policy.admissionSubjectIds.includes(admission.subjectId))
2086
+ return admission;
2087
+ }
1902
2088
  return void 0;
1903
2089
  }
1904
- function conflictingApplicationMetadata(proposal, admission) {
1905
- const authoringRevision = conflictingMetadataNumber(proposal, admission, "authoringRevision");
1906
- if (authoringRevision !== void 0)
1907
- return {
1908
- field: "authoringRevision",
1909
- code: "stale-revision",
1910
- ...authoringRevision,
1911
- message: `Admission '${admission.admissionId}' carries conflicting authoringRevision metadata`
1912
- };
1913
- const executionInputRevision = conflictingMetadataNumber(
1914
- proposal,
1915
- admission,
1916
- "executionInputRevision"
2090
+ function emitCapabilityStatusIssueForProposal(ctx, state, proposal, status) {
2091
+ const policy = selectCapabilityGuardPolicy(state, proposal);
2092
+ if (typeof policy === "string") return;
2093
+ if (status.subjectId !== void 0 && !policy.admissionSubjectIds.includes(status.subjectId))
2094
+ return;
2095
+ const capabilityId = status.capabilityId;
2096
+ const capabilityKind = status.capabilityKind;
2097
+ const statusCapabilityKey = capabilityId === void 0 || capabilityKind === void 0 ? void 0 : capabilityBoundaryRefId({ id: capabilityId, kind: capabilityKind });
2098
+ if (statusCapabilityKey !== void 0 && !policy.capabilityRefs.some(
2099
+ (capabilityRef) => capabilityBoundaryRefId(capabilityRef) === statusCapabilityKey
2100
+ ))
2101
+ return;
2102
+ const issue2 = status.issues?.[0] ?? dataIssue2(
2103
+ "capability-admission-status-issue",
2104
+ "Capability admission status reported an issue",
2105
+ { subjectId: proposal.workItemId }
2106
+ );
2107
+ emitCapabilityGuardIssue(
2108
+ ctx,
2109
+ state,
2110
+ `capability-status:${proposal.proposalId}:${status.statusId}`,
2111
+ dataIssue2(issue2.code, issue2.message, {
2112
+ subjectId: proposal.workItemId,
2113
+ refs: uniqueSourceRefs2([
2114
+ ...capabilityGuardRefs(proposal, policy),
2115
+ ...status.sourceRefs ?? []
2116
+ ]),
2117
+ metadata: {
2118
+ ...statusCapabilityKey === void 0 ? {} : { capabilityRef: statusCapabilityKey },
2119
+ capabilityAdmissionStatusId: status.statusId
2120
+ }
2121
+ })
1917
2122
  );
1918
- if (executionInputRevision !== void 0)
1919
- return {
1920
- field: "executionInputRevision",
1921
- code: "stale-execution-input",
1922
- ...executionInputRevision,
1923
- message: `Admission '${admission.admissionId}' carries conflicting executionInputRevision metadata`
1924
- };
1925
- const applyPolicyId = conflictingMetadataString(proposal, admission, "applyPolicyId");
1926
- if (applyPolicyId !== void 0)
1927
- return {
1928
- field: "applyPolicyId",
1929
- code: "policy-mismatch",
1930
- ...applyPolicyId,
1931
- message: `Admission '${admission.admissionId}' carries conflicting applyPolicyId metadata`
1932
- };
1933
- return void 0;
1934
- }
1935
- function conflictingMetadataNumber(proposal, admission, key) {
1936
- const proposalValue = numberMetadata(proposal.metadata, key);
1937
- const admissionValue = numberMetadata(admission.metadata, key);
1938
- if (proposalValue === void 0 || admissionValue === void 0 || proposalValue === admissionValue)
1939
- return void 0;
1940
- return { proposalValue, admissionValue };
1941
- }
1942
- function conflictingMetadataString(proposal, admission, key) {
1943
- const proposalValue = stringMetadata(proposal.metadata, key);
1944
- const admissionValue = stringMetadata(admission.metadata, key);
1945
- if (proposalValue === void 0 || admissionValue === void 0 || proposalValue === admissionValue)
1946
- return void 0;
1947
- return { proposalValue, admissionValue };
1948
- }
1949
- function staleRevision(proposal, admission, workItem) {
1950
- const authoringRevision = numberMetadata(admission.metadata, "authoringRevision") ?? numberMetadata(proposal.metadata, "authoringRevision");
1951
- if (authoringRevision !== void 0 && authoringRevision !== workItem.authoringRevision)
1952
- return {
1953
- kind: "stale-revision",
1954
- expected: authoringRevision,
1955
- current: workItem.authoringRevision
1956
- };
1957
- const executionInputRevision = numberMetadata(admission.metadata, "executionInputRevision") ?? numberMetadata(proposal.metadata, "executionInputRevision");
1958
- if (executionInputRevision !== void 0 && executionInputRevision !== workItem.executionInputRevision)
1959
- return {
1960
- kind: "stale-execution-input",
1961
- expected: executionInputRevision,
1962
- current: workItem.executionInputRevision
1963
- };
1964
- return void 0;
1965
2123
  }
1966
- function applyPolicyMessage(code, proposal, admission) {
2124
+ function capabilityGuardPolicyMessage(code, proposal) {
1967
2125
  if (code === "missing-policy")
1968
- return `Admitted action '${admission.admissionId}' requires an explicit apply policy`;
1969
- if (code === "unknown-work-item-domain-action-apply-policy")
1970
- return `Admitted action '${admission.admissionId}' references a missing apply policy`;
1971
- return `Admitted action '${proposal.proposalId}' matches multiple apply policies`;
1972
- }
1973
- function emitApplication(ctx, state, admission, proposal, applicationState, facts, opts = {}) {
1974
- const producedFactIds = facts.map((fact) => fact.eventId);
1975
- const application = {
1976
- kind: "work-item-domain-action-application",
1977
- applicationId: `${admission.admissionId}:application`,
1978
- admissionId: admission.admissionId,
1979
- proposalId: proposal.proposalId,
1980
- workItemId: proposal.workItemId,
1981
- actionKind: proposal.actionKind,
1982
- state: applicationState,
1983
- producedFactIds,
1984
- reason: admission.reason ?? proposal.reason,
1985
- sourceRefs: admissionRefs(admission, proposal),
1986
- metadata: { policyId: opts.policyId, code: opts.code }
1987
- };
1988
- emitApplicationFact(ctx, "application", application);
1989
- emitApplicationStatus(ctx, state, {
1990
- state: applicationState,
1991
- code: opts.code,
1992
- message: opts.message,
1993
- workItemId: proposal.workItemId,
1994
- proposalId: proposal.proposalId,
1995
- admissionId: admission.admissionId,
1996
- actionKind: proposal.actionKind,
1997
- sourceRefs: application.sourceRefs,
1998
- metadata: { producedFactIds, policyId: opts.policyId }
1999
- });
2000
- }
2001
- function emitApplicationIssue(ctx, state, key, code, message, workItemId, sourceRefs2, metadata) {
2002
- if (state.issueKeys.has(key)) return;
2003
- state.issueKeys.add(key);
2004
- const item = dataIssue2(code, message, { subjectId: workItemId, refs: sourceRefs2, metadata });
2005
- emitApplicationFact(ctx, "issue", item);
2006
- emitApplicationStatus(ctx, state, {
2007
- state: code === "missing-policy" ? "missing-policy" : "rejected",
2008
- code,
2009
- message,
2010
- workItemId,
2011
- sourceRefs: sourceRefs2,
2012
- metadata
2013
- });
2126
+ return `WorkItem domain action '${proposal.proposalId}' has no capability guard policy`;
2127
+ if (code === "unknown-work-item-domain-action-capability-guard-policy")
2128
+ return `WorkItem domain action '${proposal.proposalId}' references a missing capability guard policy`;
2129
+ return `WorkItem domain action '${proposal.proposalId}' matches multiple capability guard policies`;
2014
2130
  }
2015
- function emitIntakeStatus(ctx, state, status) {
2016
- state.statusSeq += 1;
2017
- const statusFact = {
2018
- kind: "work-item-domain-action-status",
2019
- statusId: `work-item-domain-action-intake-status:${state.statusSeq}`,
2020
- ...status
2021
- };
2022
- emitIntake(ctx, "status", statusFact);
2023
- emitIntakeAudit(ctx, state, "work-item-domain-action-intake-status", statusFact);
2131
+ function capabilityGuardRefs(proposal, policy, admissions = []) {
2132
+ return uniqueSourceRefs2([
2133
+ ref2("work-item", proposal.workItemId),
2134
+ ref2("work-item-domain-action-proposal", proposal.proposalId),
2135
+ ref2("work-item-domain-action-capability-guard-policy", policy.policyId),
2136
+ ...admissions.filter((admission) => admission !== void 0).flatMap((admission) => [
2137
+ ref2("capability-admission", admission.admissionId),
2138
+ ref2("capability-admission-proposal", admission.proposalId),
2139
+ ref2("boundary-capability", capabilityBoundaryRefId(admission.capability)),
2140
+ ...admission.sourceRefs ?? []
2141
+ ]),
2142
+ ...proposal.sourceRefs ?? []
2143
+ ]);
2024
2144
  }
2025
- function emitApplicationStatus(ctx, state, status) {
2145
+ function capabilityBoundaryRefId(capability) {
2146
+ return `${capability.kind}:${capability.id}`;
2147
+ }
2148
+ function emitCapabilityGuardStatus(ctx, state, status) {
2026
2149
  state.statusSeq += 1;
2027
2150
  const statusFact = {
2028
- kind: "work-item-domain-action-status",
2029
- statusId: `work-item-domain-action-application-status:${state.statusSeq}`,
2151
+ kind: "work-item-domain-action-capability-guard-status",
2152
+ statusId: `work-item-domain-action-capability-guard-status:${state.statusSeq}`,
2030
2153
  ...status
2031
2154
  };
2032
- emitApplicationFact(ctx, "status", statusFact);
2033
- emitApplicationAudit(ctx, state, "work-item-domain-action-application-status", statusFact);
2034
- }
2035
- function emitIntakeAudit(ctx, state, kind, status) {
2036
- state.auditSeq += 1;
2037
- emitIntake(ctx, "audit", auditRecord(kind, state.auditSeq, status));
2038
- }
2039
- function emitApplicationAudit(ctx, state, kind, status) {
2155
+ emitCapabilityGuard(ctx, "status", statusFact);
2040
2156
  state.auditSeq += 1;
2041
- emitApplicationFact(ctx, "audit", auditRecord(kind, state.auditSeq, status));
2042
- }
2043
- function auditRecord(kind, seq, status) {
2044
- return {
2045
- id: `${kind}:${seq}`,
2046
- kind,
2157
+ emitCapabilityGuard(ctx, "audit", {
2158
+ id: `work-item-domain-action-capability-guard-status:${state.auditSeq}`,
2159
+ kind: "work-item-domain-action-capability-guard-status",
2047
2160
  subjectId: status.workItemId,
2048
2161
  message: status.message,
2049
- issueCode: status.code,
2050
2162
  sourceRefs: status.sourceRefs,
2051
2163
  metadata: {
2052
- statusId: status.statusId,
2164
+ statusId: statusFact.statusId,
2053
2165
  state: status.state,
2054
2166
  proposalId: status.proposalId,
2055
- admissionId: status.admissionId,
2056
- actionKind: status.actionKind,
2167
+ policyId: status.policyId,
2057
2168
  ...status.metadata ?? {}
2058
2169
  }
2059
- };
2170
+ });
2060
2171
  }
2061
- function emitIntake(ctx, kind, value) {
2062
- ctx.down([["DATA", { kind, value }]]);
2172
+ function emitCapabilityGuardStatusOnce(ctx, state, key, status) {
2173
+ if (state.emittedStatusKeys.has(key)) return;
2174
+ state.emittedStatusKeys.add(key);
2175
+ emitCapabilityGuardStatus(ctx, state, status);
2063
2176
  }
2064
- function emitApplicationFact(ctx, kind, value) {
2177
+ function emitCapabilityGuardIssue(ctx, state, key, issue2) {
2178
+ if (state.issueKeys.has(key)) return;
2179
+ state.issueKeys.add(key);
2180
+ emitCapabilityGuard(ctx, "issue", issue2);
2181
+ emitCapabilityGuardStatus(ctx, state, {
2182
+ state: "issue",
2183
+ workItemId: issue2.subjectId,
2184
+ issues: [issue2],
2185
+ message: issue2.message,
2186
+ metadata: { issueCode: issue2.code }
2187
+ });
2188
+ }
2189
+ function emitCapabilityGuard(ctx, kind, value) {
2065
2190
  ctx.down([["DATA", { kind, value }]]);
2066
2191
  }
2067
- function project(graph, runtime, name, factory, select) {
2068
- return graph.node(
2069
- [runtime],
2192
+
2193
+ // src/solutions/work-item/actions-intake.ts
2194
+ function workItemDomainActionProposalIntakeProjector(graph, opts) {
2195
+ const name = opts.name ?? "workItemDomainActionIntake";
2196
+ const now = opts.now ?? Date.now;
2197
+ const runtime = graph.node(
2198
+ [opts.proposals],
2070
2199
  (ctx) => {
2071
- for (const raw of depBatch(ctx, 0) ?? []) {
2072
- const selected = select(raw);
2073
- if (selected !== void 0) ctx.down([["DATA", selected]]);
2074
- }
2200
+ const state = ctx.state.get() ?? {
2201
+ proposals: /* @__PURE__ */ new Set(),
2202
+ statusSeq: 0,
2203
+ auditSeq: 0
2204
+ };
2205
+ for (const raw of depBatch(ctx, 0) ?? []) reduceProposalIntake(ctx, state, raw, now());
2206
+ ctx.state.set(state);
2075
2207
  },
2076
- { name, factory, partial: true, completeWhenDepsComplete: false, errorWhenDepsError: false }
2208
+ {
2209
+ name: `${name}/runtime`,
2210
+ factory: "workItemDomainActionProposalIntakeProjector",
2211
+ partial: true,
2212
+ completeWhenDepsComplete: false,
2213
+ errorWhenDepsError: false
2214
+ }
2077
2215
  );
2078
- }
2079
- function firstDuplicateFactId(state, facts) {
2080
- const pending = /* @__PURE__ */ new Set();
2081
- for (const fact of facts) {
2082
- if (state.emittedFactIds.has(fact.eventId) || pending.has(fact.eventId)) return fact.eventId;
2083
- pending.add(fact.eventId);
2084
- }
2085
- return void 0;
2086
- }
2087
- function claimFactId(state, factId) {
2088
- if (state.emittedFactIds.has(factId)) return false;
2089
- state.emittedFactIds.add(factId);
2090
- return true;
2091
- }
2092
- function admissionRefs(admission, proposal, policy) {
2093
- return uniqueSourceRefs2([
2094
- ref2("work-item-domain-action-admission", admission.admissionId),
2095
- ref2("work-item-domain-action-proposal", admission.proposalId),
2096
- ...proposal === void 0 ? [] : [
2097
- ref2("work-item", proposal.workItemId),
2098
- ref2("work-item-evidence", proposal.evidenceId),
2099
- ...proposal.sourceRefs ?? []
2100
- ],
2101
- ...policy === void 0 ? [] : [ref2("work-item-domain-action-apply-policy", policy.policyId)],
2102
- ...admission.sourceRefs ?? []
2103
- ]);
2104
- }
2105
- function actionSourceRefs(admission, proposal, policy) {
2106
- return admissionRefs(admission, proposal, policy);
2107
- }
2108
- function dataIssue2(code, message, opts = {}) {
2109
2216
  return {
2110
- kind: "issue",
2111
- code,
2112
- message,
2113
- severity: "error",
2114
- source: "work-item-actions",
2115
- subjectId: opts.subjectId,
2116
- refs: opts.refs?.map((sourceRef) => `${sourceRef.kind}:${sourceRef.id}`),
2117
- metadata: opts.metadata
2217
+ proposals: project(
2218
+ graph,
2219
+ runtime,
2220
+ `${name}/proposals`,
2221
+ "workItemDomainActionProposals",
2222
+ (fact) => fact.kind === "proposal" ? fact.value : void 0
2223
+ ),
2224
+ status: project(
2225
+ graph,
2226
+ runtime,
2227
+ `${name}/status`,
2228
+ "workItemDomainActionStatus",
2229
+ (fact) => fact.kind === "status" ? fact.value : void 0
2230
+ ),
2231
+ issues: project(
2232
+ graph,
2233
+ runtime,
2234
+ `${name}/issues`,
2235
+ "workItemDomainActionIssues",
2236
+ (fact) => fact.kind === "issue" ? fact.value : void 0
2237
+ ),
2238
+ audit: project(
2239
+ graph,
2240
+ runtime,
2241
+ `${name}/audit`,
2242
+ "workItemDomainActionAudit",
2243
+ (fact) => fact.kind === "audit" ? fact.value : void 0
2244
+ )
2118
2245
  };
2119
2246
  }
2120
- function uniqueSourceRefs2(sourceRefs2) {
2121
- const seen = /* @__PURE__ */ new Set();
2122
- const out = [];
2123
- for (const sourceRef of sourceRefs2) {
2124
- const key = `${sourceRef.kind}:${sourceRef.id}:${JSON.stringify(sourceRef.metadata ?? {})}`;
2125
- if (seen.has(key)) continue;
2126
- seen.add(key);
2127
- out.push(sourceRef);
2247
+ function reduceProposalIntake(ctx, state, input, proposedAtMs) {
2248
+ const proposal = normalizeProposal(input, proposedAtMs);
2249
+ if (typeof proposal === "string") {
2250
+ const issue2 = dataIssue2("malformed-domain-action-proposal", proposal);
2251
+ emitIntake(ctx, "issue", issue2);
2252
+ emitIntakeStatus(ctx, state, {
2253
+ state: "rejected",
2254
+ code: issue2.code,
2255
+ message: issue2.message
2256
+ });
2257
+ return;
2128
2258
  }
2129
- return out;
2259
+ if (state.proposals.has(proposal.proposalId)) {
2260
+ emitIntakeStatus(ctx, state, {
2261
+ state: "duplicate",
2262
+ code: "duplicate-suppressed",
2263
+ workItemId: proposal.workItemId,
2264
+ proposalId: proposal.proposalId,
2265
+ actionKind: proposal.actionKind,
2266
+ message: `Duplicate WorkItem domain action proposal '${proposal.proposalId}' suppressed`
2267
+ });
2268
+ return;
2269
+ }
2270
+ state.proposals.add(proposal.proposalId);
2271
+ emitIntake(ctx, "proposal", proposal);
2272
+ emitIntakeStatus(ctx, state, {
2273
+ state: "proposed",
2274
+ workItemId: proposal.workItemId,
2275
+ proposalId: proposal.proposalId,
2276
+ actionKind: proposal.actionKind,
2277
+ sourceRefs: proposal.sourceRefs
2278
+ });
2130
2279
  }
2131
- function sourceRefs(value) {
2132
- if (!Array.isArray(value)) return void 0;
2133
- return value.filter(
2134
- (item) => isRecord2(item) && typeof item.kind === "string" && typeof item.id === "string"
2280
+ function normalizeProposal(input, proposedAtMs) {
2281
+ if (!isRecord(input)) return "WorkItemDomainActionProposal intake must be an object";
2282
+ if (input.kind !== "work-item-domain-action-proposal" && input.kind !== "work-item-domain-action-proposal-intake")
2283
+ return "WorkItemDomainActionProposal intake has unsupported kind";
2284
+ if (typeof input.proposalId !== "string" || input.proposalId.trim() === "")
2285
+ return "WorkItemDomainActionProposal.proposalId is required";
2286
+ if (typeof input.workItemId !== "string" || input.workItemId.trim() === "")
2287
+ return "WorkItemDomainActionProposal.workItemId is required";
2288
+ if (typeof input.actionKind !== "string" || input.actionKind.trim() === "")
2289
+ return "WorkItemDomainActionProposal.actionKind is required";
2290
+ if (input.kind === "work-item-domain-action-proposal") {
2291
+ if (typeof input.effectRunId !== "string")
2292
+ return "WorkItemDomainActionProposal.effectRunId is required";
2293
+ if (typeof input.effectRunResultId !== "string")
2294
+ return "WorkItemDomainActionProposal.effectRunResultId is required";
2295
+ if (typeof input.evidenceId !== "string")
2296
+ return "WorkItemDomainActionProposal.evidenceId is required";
2297
+ if (typeof input.policyId !== "string")
2298
+ return "WorkItemDomainActionProposal.policyId is required";
2299
+ return input;
2300
+ }
2301
+ return workItemDomainActionProposal(
2302
+ input.proposalId,
2303
+ input.workItemId,
2304
+ input.actionKind,
2305
+ {
2306
+ payload: input.payload,
2307
+ reason: typeof input.reason === "string" ? input.reason : void 0,
2308
+ policyId: typeof input.policyId === "string" ? input.policyId : void 0,
2309
+ effectRunId: typeof input.effectRunId === "string" ? input.effectRunId : void 0,
2310
+ effectRunResultId: typeof input.effectRunResultId === "string" ? input.effectRunResultId : void 0,
2311
+ evidenceId: typeof input.evidenceId === "string" ? input.evidenceId : void 0,
2312
+ proposedAtMs: typeof input.proposedAtMs === "number" ? input.proposedAtMs : proposedAtMs,
2313
+ sourceRefs: sourceRefs(input.sourceRefs),
2314
+ metadata: isRecord(input.metadata) ? input.metadata : void 0
2315
+ }
2135
2316
  );
2136
2317
  }
2137
- function numberMetadata(metadata, key) {
2138
- const value = metadata?.[key];
2139
- return typeof value === "number" ? value : void 0;
2140
- }
2141
- function stringMetadata(metadata, key) {
2142
- const value = metadata?.[key];
2143
- return typeof value === "string" ? value : void 0;
2144
- }
2145
- function ref2(kind, id) {
2146
- return { kind, id };
2318
+ function emitIntakeStatus(ctx, state, status) {
2319
+ state.statusSeq += 1;
2320
+ const statusFact = {
2321
+ kind: "work-item-domain-action-status",
2322
+ statusId: `work-item-domain-action-intake-status:${state.statusSeq}`,
2323
+ ...status
2324
+ };
2325
+ emitIntake(ctx, "status", statusFact);
2326
+ emitIntakeAudit(ctx, state, "work-item-domain-action-intake-status", statusFact);
2147
2327
  }
2148
- function recordString(value, key) {
2149
- return isRecord2(value) && typeof value[key] === "string" ? value[key] : void 0;
2328
+ function emitIntakeAudit(ctx, state, kind, status) {
2329
+ state.auditSeq += 1;
2330
+ emitIntake(ctx, "audit", auditRecord(kind, state.auditSeq, status));
2150
2331
  }
2151
- function isRecord2(value) {
2152
- return value !== null && typeof value === "object";
2332
+ function emitIntake(ctx, kind, value) {
2333
+ ctx.down([["DATA", { kind, value }]]);
2153
2334
  }
2154
2335
  // Annotate the CommonJS export names for ESM import in node:
2155
2336
  0 && (module.exports = {