@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.
- package/dist/actions-types-CSC3NMDl.d.ts +657 -0
- package/dist/actions-types-wl9Mgkmr.d.cts +657 -0
- package/dist/adapters/index.cjs +5157 -2951
- package/dist/adapters/index.cjs.map +1 -1
- package/dist/adapters/index.d.cts +207 -70
- package/dist/adapters/index.d.ts +207 -70
- package/dist/adapters/index.js +3738 -1688
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/nestjs/microservices.cjs +841 -0
- package/dist/adapters/nestjs/microservices.cjs.map +1 -0
- package/dist/adapters/nestjs/microservices.d.cts +45 -0
- package/dist/adapters/nestjs/microservices.d.ts +45 -0
- package/dist/adapters/nestjs/microservices.js +206 -0
- package/dist/adapters/nestjs/microservices.js.map +1 -0
- package/dist/adapters/nestjs/native.cjs +1391 -0
- package/dist/adapters/nestjs/native.cjs.map +1 -0
- package/dist/adapters/nestjs/native.d.cts +111 -0
- package/dist/adapters/nestjs/native.d.ts +111 -0
- package/dist/adapters/nestjs/native.js +577 -0
- package/dist/adapters/nestjs/native.js.map +1 -0
- package/dist/adapters/nestjs/websockets.cjs +924 -0
- package/dist/adapters/nestjs/websockets.cjs.map +1 -0
- package/dist/adapters/nestjs/websockets.d.cts +47 -0
- package/dist/adapters/nestjs/websockets.d.ts +47 -0
- package/dist/adapters/nestjs/websockets.js +286 -0
- package/dist/adapters/nestjs/websockets.js.map +1 -0
- package/dist/adapters/nestjs.cjs +1085 -0
- package/dist/adapters/nestjs.cjs.map +1 -0
- package/dist/adapters/nestjs.d.cts +385 -0
- package/dist/adapters/nestjs.d.ts +385 -0
- package/dist/adapters/nestjs.js +112 -0
- package/dist/adapters/nestjs.js.map +1 -0
- package/dist/adapters/observe-storage.d.cts +3 -3
- package/dist/adapters/observe-storage.d.ts +3 -3
- package/dist/adapters/observe-storage.js +4 -4
- package/dist/adapters/react.cjs.map +1 -1
- package/dist/adapters/react.d.cts +2 -2
- package/dist/adapters/react.d.ts +2 -2
- package/dist/adapters/react.js +1 -1
- package/dist/adapters/solid.cjs.map +1 -1
- package/dist/adapters/solid.d.cts +2 -2
- package/dist/adapters/solid.d.ts +2 -2
- package/dist/adapters/solid.js +1 -1
- package/dist/adapters/svelte.cjs.map +1 -1
- package/dist/adapters/svelte.d.cts +2 -2
- package/dist/adapters/svelte.d.ts +2 -2
- package/dist/adapters/svelte.js +1 -1
- package/dist/adapters/vue.cjs.map +1 -1
- package/dist/adapters/vue.d.cts +2 -2
- package/dist/adapters/vue.d.ts +2 -2
- package/dist/adapters/vue.js +1 -1
- package/dist/{agent-runtime-DZTJ3C-p.d.cts → agent-runtime-types-core-CaXfKNtY.d.cts} +171 -320
- package/dist/{agent-runtime-BesGDZcB.d.ts → agent-runtime-types-core-hU299uIC.d.ts} +171 -320
- package/dist/agent-runtime-types-tool-BEpSINol.d.ts +695 -0
- package/dist/agent-runtime-types-tool-DninPEzk.d.cts +695 -0
- package/dist/agentic-memory-retention-BmGX368S.d.ts +14 -0
- package/dist/agentic-memory-retention-Ih1qhX9Z.d.cts +14 -0
- package/dist/{agentic-memory-BmRCvh0d.d.ts → agentic-memory-types-CVSUS6XR.d.ts} +13 -57
- package/dist/{agentic-memory-DfuVZ-lY.d.cts → agentic-memory-types-FnnYlVUs.d.cts} +13 -57
- package/dist/{capability-admission-g0mn9hpq.d.ts → capability-admission-BW2CziT6.d.ts} +3 -3
- package/dist/{capability-admission-B0BzqAm5.d.cts → capability-admission-lwSecxXs.d.cts} +3 -3
- package/dist/chunk-2KUAG2GZ.js +9693 -0
- package/dist/chunk-2KUAG2GZ.js.map +1 -0
- package/dist/chunk-2NJP6GO4.js +1320 -0
- package/dist/chunk-2NJP6GO4.js.map +1 -0
- package/dist/{chunk-YDSIVHRR.js → chunk-4NWSNI4K.js} +1598 -1584
- package/dist/chunk-4NWSNI4K.js.map +1 -0
- package/dist/{chunk-Z2ANQ6JW.js → chunk-4Q77MMJY.js} +2 -2
- package/dist/chunk-6E76H4OU.js +132 -0
- package/dist/chunk-6E76H4OU.js.map +1 -0
- package/dist/chunk-7GOTIIQ7.js +819 -0
- package/dist/chunk-7GOTIIQ7.js.map +1 -0
- package/dist/{chunk-IXZBUWSP.js → chunk-7HZRJMN6.js} +2 -2
- package/dist/{chunk-SS2A464I.js → chunk-CJIFGJQG.js} +3 -3
- package/dist/{chunk-V7CIJE4B.js → chunk-CX6B6PO2.js} +92 -9
- package/dist/chunk-CX6B6PO2.js.map +1 -0
- package/dist/{chunk-FBUU43PM.js → chunk-E4QCH32M.js} +448 -446
- package/dist/chunk-E4QCH32M.js.map +1 -0
- package/dist/{chunk-HKK2JD7Z.js → chunk-ECURQ3HD.js} +12 -125
- package/dist/chunk-ECURQ3HD.js.map +1 -0
- package/dist/{chunk-3JR3EDB2.js → chunk-EOKT4QFF.js} +121 -115
- package/dist/chunk-EOKT4QFF.js.map +1 -0
- package/dist/{chunk-K3FXTC3I.js → chunk-HJACH5PU.js} +3 -3
- package/dist/{chunk-GTPCOEOZ.js → chunk-IDMLXLLG.js} +3 -3
- package/dist/{chunk-DKP56CSW.js → chunk-JKAYLDQV.js} +2 -2
- package/dist/{chunk-4VLAQHZI.js → chunk-K7FB3JTD.js} +17 -1
- package/dist/chunk-K7FB3JTD.js.map +1 -0
- package/dist/{chunk-H75VXTGT.js → chunk-L6LDXRWE.js} +3 -3
- package/dist/{chunk-H75VXTGT.js.map → chunk-L6LDXRWE.js.map} +1 -1
- package/dist/{chunk-YXMZ2N6J.js → chunk-M7J6Z4IJ.js} +1071 -1025
- package/dist/chunk-M7J6Z4IJ.js.map +1 -0
- package/dist/chunk-MQEQMS7B.js +1009 -0
- package/dist/chunk-MQEQMS7B.js.map +1 -0
- package/dist/chunk-MTELVIYR.js +1421 -0
- package/dist/chunk-MTELVIYR.js.map +1 -0
- package/dist/{chunk-QFY4AFY4.js → chunk-PVC7WDKI.js} +58 -52
- package/dist/chunk-PVC7WDKI.js.map +1 -0
- package/dist/{chunk-VTHLCFN4.js → chunk-RAHNUYA5.js} +2 -2
- package/dist/{chunk-XKXWK2KP.js → chunk-SHZDP2MQ.js} +1157 -1021
- package/dist/chunk-SHZDP2MQ.js.map +1 -0
- package/dist/{chunk-CZNV3VXM.js → chunk-SIDJO2T4.js} +11 -11
- package/dist/chunk-STXRMPW4.js +4131 -0
- package/dist/chunk-STXRMPW4.js.map +1 -0
- package/dist/{chunk-2747WN4Y.js → chunk-TPND5MJS.js} +3 -3
- package/dist/chunk-TQPCAIQI.js +612 -0
- package/dist/chunk-TQPCAIQI.js.map +1 -0
- package/dist/{chunk-43BKLFZU.js → chunk-YDWBXXQZ.js} +10 -10
- package/dist/{chunk-6PHB5CJS.js → chunk-ZLFRB6FI.js} +2 -2
- package/dist/composition/index.cjs +1228 -1076
- package/dist/composition/index.cjs.map +1 -1
- package/dist/composition/index.d.cts +3 -3
- package/dist/composition/index.d.ts +3 -3
- package/dist/composition/index.js +4 -4
- package/dist/core/index.cjs +1198 -1046
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +2 -2
- package/dist/cqrs/index.d.cts +3 -3
- package/dist/cqrs/index.d.ts +3 -3
- package/dist/cqrs/messaging.d.cts +4 -4
- package/dist/cqrs/messaging.d.ts +4 -4
- package/dist/cqrs/work-queue.d.cts +5 -5
- package/dist/cqrs/work-queue.d.ts +5 -5
- package/dist/data-structures/index.cjs +1226 -1074
- package/dist/data-structures/index.cjs.map +1 -1
- package/dist/data-structures/index.d.cts +8 -7
- package/dist/data-structures/index.d.ts +8 -7
- package/dist/data-structures/index.js +6 -5
- package/dist/{describe-BpyJuWkw.d.ts → describe-B_A5fbXH.d.ts} +1 -1
- package/dist/{describe-DYMw_l1C.d.cts → describe-CQRrkv-Q.d.cts} +1 -1
- package/dist/executors/tool-provider-adapters.cjs +3091 -0
- package/dist/executors/tool-provider-adapters.cjs.map +1 -0
- package/dist/executors/tool-provider-adapters.d.cts +174 -0
- package/dist/executors/tool-provider-adapters.d.ts +174 -0
- package/dist/executors/tool-provider-adapters.js +1336 -0
- package/dist/executors/tool-provider-adapters.js.map +1 -0
- package/dist/executors/tool-provider-runtime.cjs +5178 -0
- package/dist/executors/tool-provider-runtime.cjs.map +1 -0
- package/dist/executors/tool-provider-runtime.d.cts +17 -0
- package/dist/executors/tool-provider-runtime.d.ts +17 -0
- package/dist/executors/tool-provider-runtime.js +15 -0
- package/dist/executors/tool-provider-runtime.js.map +1 -0
- package/dist/executors/tool-provider.cjs +6392 -0
- package/dist/executors/tool-provider.cjs.map +1 -0
- package/dist/executors/tool-provider.d.cts +60 -0
- package/dist/executors/tool-provider.d.ts +60 -0
- package/dist/executors/tool-provider.js +89 -0
- package/dist/executors/tool-provider.js.map +1 -0
- package/dist/executors/work-queue.d.cts +6 -6
- package/dist/executors/work-queue.d.ts +6 -6
- package/dist/graph/index.cjs +1499 -1341
- package/dist/graph/index.cjs.map +1 -1
- package/dist/graph/index.d.cts +9 -8
- package/dist/graph/index.d.ts +9 -8
- package/dist/graph/index.js +23 -22
- package/dist/{graph-DCEWh4T2.d.cts → graph-CWK3HLPB.d.cts} +133 -131
- package/dist/{graph-D-1-SQeC.d.ts → graph-ho5sotHP.d.ts} +133 -131
- package/dist/{index-Bmxeu0nK.d.ts → index-CQWcE0Dw.d.ts} +18 -4
- package/dist/{index-72VZC0-E.d.cts → index-DHA1i67c.d.cts} +18 -4
- package/dist/index.cjs +3397 -3140
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -9
- package/dist/index.d.ts +12 -9
- package/dist/index.js +110 -109
- package/dist/inspection/boundary.d.cts +4 -4
- package/dist/inspection/boundary.d.ts +4 -4
- package/dist/measurements-C0mAvWU1.d.cts +164 -0
- package/dist/measurements-OMpCeBwh.d.ts +164 -0
- package/dist/messaging/index.cjs +57 -51
- package/dist/messaging/index.cjs.map +1 -1
- package/dist/messaging/index.d.cts +11 -276
- package/dist/messaging/index.d.ts +11 -276
- package/dist/messaging/index.js +1 -1
- package/dist/operators/index.cjs +1228 -1076
- package/dist/operators/index.cjs.map +1 -1
- package/dist/operators/index.d.cts +5 -5
- package/dist/operators/index.d.ts +5 -5
- package/dist/operators/index.js +5 -5
- package/dist/orchestration/index.cjs +8844 -6071
- package/dist/orchestration/index.cjs.map +1 -1
- package/dist/orchestration/index.d.cts +171 -224
- package/dist/orchestration/index.d.ts +171 -224
- package/dist/orchestration/index.js +42 -991
- package/dist/orchestration/index.js.map +1 -1
- package/dist/orchestration/messaging.d.cts +5 -8
- package/dist/orchestration/messaging.d.ts +5 -8
- package/dist/orchestration/work-queue.cjs +1243 -5
- package/dist/orchestration/work-queue.cjs.map +1 -1
- package/dist/orchestration/work-queue.d.cts +121 -10
- package/dist/orchestration/work-queue.d.ts +121 -10
- package/dist/orchestration/work-queue.js +1067 -4
- package/dist/orchestration/work-queue.js.map +1 -1
- package/dist/patterns/event-flow.cjs +6 -20
- package/dist/patterns/event-flow.cjs.map +1 -1
- package/dist/patterns/event-flow.d.cts +4 -4
- package/dist/patterns/event-flow.d.ts +4 -4
- package/dist/patterns/event-flow.js +2 -2
- package/dist/patterns/index.cjs +453 -465
- package/dist/patterns/index.cjs.map +1 -1
- package/dist/patterns/index.d.cts +6 -5
- package/dist/patterns/index.d.ts +6 -5
- package/dist/patterns/index.js +3 -3
- package/dist/process-types-C9uMYz3i.d.ts +200 -0
- package/dist/process-types-h3uRlzDc.d.cts +200 -0
- package/dist/{reactive-collection-DNV0-gFi.d.cts → reactive-collection-B9R0Bl1P.d.cts} +4 -34
- package/dist/{reactive-collection-BAmfrGjd.d.ts → reactive-collection-BXFrH0U2.d.ts} +4 -34
- package/dist/{reactive-collection-storage-BNEldDyi.d.cts → reactive-collection-storage-BLGv8Xp8.d.cts} +5 -5
- package/dist/{reactive-collection-storage-YZsAQsWK.d.ts → reactive-collection-storage-a4zD3665.d.ts} +5 -5
- package/dist/{reactive-map-DRD1LJ2H.d.cts → reactive-map-C_8sOfdS.d.cts} +3 -2
- package/dist/{reactive-map-EsDiCmJk.d.ts → reactive-map-jle5dnok.d.ts} +3 -2
- package/dist/render/index.d.cts +2 -2
- package/dist/render/index.d.ts +2 -2
- package/dist/scheduled-readiness-5rwQlyjV.d.ts +91 -0
- package/dist/scheduled-readiness-DpRY8jI0.d.cts +91 -0
- package/dist/semantic-memory-graph-C2ZUwIlq.d.ts +30 -0
- package/dist/semantic-memory-graph-DlZBD_Eu.d.cts +30 -0
- package/dist/{semantic-memory-graph-DKuNzuWw.d.cts → semantic-memory-graph-types-BvzdqmkY.d.cts} +2 -28
- package/dist/{semantic-memory-graph-uwbwJdhe.d.ts → semantic-memory-graph-types-Ci-OtahO.d.ts} +2 -28
- package/dist/solutions/index.cjs +6353 -6169
- package/dist/solutions/index.cjs.map +1 -1
- package/dist/solutions/index.d.cts +12 -10
- package/dist/solutions/index.d.ts +12 -10
- package/dist/solutions/index.js +5 -5
- package/dist/solutions/reactive-layout/browser/index.cjs +116 -108
- package/dist/solutions/reactive-layout/browser/index.cjs.map +1 -1
- package/dist/solutions/reactive-layout/browser/index.d.cts +4 -4
- package/dist/solutions/reactive-layout/browser/index.d.ts +4 -4
- package/dist/solutions/reactive-layout/browser/index.js +3 -3
- package/dist/solutions/reactive-layout/index.cjs +3578 -3350
- package/dist/solutions/reactive-layout/index.cjs.map +1 -1
- package/dist/solutions/reactive-layout/index.d.cts +32 -576
- package/dist/solutions/reactive-layout/index.d.ts +32 -576
- package/dist/solutions/reactive-layout/index.js +65 -5
- package/dist/solutions/reactive-layout/node-canvas/index.cjs +127 -117
- package/dist/solutions/reactive-layout/node-canvas/index.cjs.map +1 -1
- package/dist/solutions/reactive-layout/node-canvas/index.d.cts +4 -4
- package/dist/solutions/reactive-layout/node-canvas/index.d.ts +4 -4
- package/dist/solutions/reactive-layout/node-canvas/index.js +3 -3
- package/dist/solutions/reactive-layout/react-native/index.cjs +127 -117
- package/dist/solutions/reactive-layout/react-native/index.cjs.map +1 -1
- package/dist/solutions/reactive-layout/react-native/index.d.cts +4 -4
- package/dist/solutions/reactive-layout/react-native/index.d.ts +4 -4
- package/dist/solutions/reactive-layout/react-native/index.js +3 -3
- package/dist/solutions/reactive-layout/skia/index.cjs +127 -117
- package/dist/solutions/reactive-layout/skia/index.cjs.map +1 -1
- package/dist/solutions/reactive-layout/skia/index.d.cts +4 -4
- package/dist/solutions/reactive-layout/skia/index.d.ts +4 -4
- package/dist/solutions/reactive-layout/skia/index.js +3 -3
- package/dist/solutions/work-item/actions.cjs +1609 -1428
- package/dist/solutions/work-item/actions.cjs.map +1 -1
- package/dist/solutions/work-item/actions.d.cts +23 -184
- package/dist/solutions/work-item/actions.d.ts +23 -184
- package/dist/solutions/work-item/actions.js +1558 -934
- package/dist/solutions/work-item/actions.js.map +1 -1
- package/dist/solutions/work-item/scheduling.cjs +9181 -1838
- package/dist/solutions/work-item/scheduling.cjs.map +1 -1
- package/dist/solutions/work-item/scheduling.d.cts +1763 -328
- package/dist/solutions/work-item/scheduling.d.ts +1763 -328
- package/dist/solutions/work-item/scheduling.js +117 -3
- package/dist/solutions/work-item/work-queue.d.cts +7 -7
- package/dist/solutions/work-item/work-queue.d.ts +7 -7
- package/dist/sources/browser.cjs +1318 -1083
- package/dist/sources/browser.cjs.map +1 -1
- package/dist/sources/browser.d.cts +4 -4
- package/dist/sources/browser.d.ts +4 -4
- package/dist/sources/browser.js +4 -4
- package/dist/sources/index.cjs +1318 -1083
- package/dist/sources/index.cjs.map +1 -1
- package/dist/sources/index.d.cts +4 -4
- package/dist/sources/index.d.ts +4 -4
- package/dist/sources/index.js +4 -4
- package/dist/sources/node.d.cts +3 -3
- package/dist/sources/node.d.ts +3 -3
- package/dist/storage/browser.cjs +16 -0
- package/dist/storage/browser.cjs.map +1 -1
- package/dist/storage/browser.js +3 -3
- package/dist/storage/index.cjs +16 -0
- package/dist/storage/index.cjs.map +1 -1
- package/dist/storage/index.d.cts +5 -4
- package/dist/storage/index.d.ts +5 -4
- package/dist/storage/index.js +6 -6
- package/dist/storage/node.cjs +16 -0
- package/dist/storage/node.cjs.map +1 -1
- package/dist/storage/node.js +3 -3
- package/dist/{store-C-joM4gh.d.ts → store-BmtEs9mG.d.ts} +1 -1
- package/dist/{store-CNJwmT-h.d.cts → store-PAAsYvML.d.cts} +1 -1
- package/dist/types-BRNKoFHj.d.ts +357 -0
- package/dist/{types-CcPIn7sy.d.cts → types-BS6CyR-x.d.cts} +27 -108
- package/dist/types-BWK2ay3e.d.cts +457 -0
- package/dist/types-BXKSAoE1.d.cts +357 -0
- package/dist/types-BaPpZItj.d.ts +457 -0
- package/dist/types-BhveCMg5.d.cts +274 -0
- package/dist/{types-BsJvElYZ.d.ts → types-C9NOgNJw.d.ts} +27 -108
- package/dist/types-CTsQ8Ww4.d.ts +274 -0
- package/dist/types-DL_05n2M.d.ts +34 -0
- package/dist/types-XDoqxYO9.d.cts +34 -0
- package/dist/{work-item-runtime-C3M79vE9.d.ts → work-item-runtime-types-BqtWnSCS.d.ts} +3 -93
- package/dist/{work-item-runtime-qaVgsL0T.d.cts → work-item-runtime-types-CHO03AKh.d.cts} +3 -93
- package/dist/work-queue/index.cjs +957 -953
- package/dist/work-queue/index.cjs.map +1 -1
- package/dist/work-queue/index.d.cts +9 -357
- package/dist/work-queue/index.d.ts +9 -357
- package/dist/work-queue/index.js +957 -953
- package/dist/work-queue/index.js.map +1 -1
- package/package.json +603 -513
- package/dist/chunk-3JR3EDB2.js.map +0 -1
- package/dist/chunk-4VLAQHZI.js.map +0 -1
- package/dist/chunk-6XAIOL3X.js +0 -2407
- package/dist/chunk-6XAIOL3X.js.map +0 -1
- package/dist/chunk-FBUU43PM.js.map +0 -1
- package/dist/chunk-HKK2JD7Z.js.map +0 -1
- package/dist/chunk-PC3BTTBE.js +0 -3217
- package/dist/chunk-PC3BTTBE.js.map +0 -1
- package/dist/chunk-QFY4AFY4.js.map +0 -1
- package/dist/chunk-V7CIJE4B.js.map +0 -1
- package/dist/chunk-XKXWK2KP.js.map +0 -1
- package/dist/chunk-YDSIVHRR.js.map +0 -1
- package/dist/chunk-YXMZ2N6J.js.map +0 -1
- /package/dist/{chunk-Z2ANQ6JW.js.map → chunk-4Q77MMJY.js.map} +0 -0
- /package/dist/{chunk-IXZBUWSP.js.map → chunk-7HZRJMN6.js.map} +0 -0
- /package/dist/{chunk-SS2A464I.js.map → chunk-CJIFGJQG.js.map} +0 -0
- /package/dist/{chunk-K3FXTC3I.js.map → chunk-HJACH5PU.js.map} +0 -0
- /package/dist/{chunk-GTPCOEOZ.js.map → chunk-IDMLXLLG.js.map} +0 -0
- /package/dist/{chunk-DKP56CSW.js.map → chunk-JKAYLDQV.js.map} +0 -0
- /package/dist/{chunk-VTHLCFN4.js.map → chunk-RAHNUYA5.js.map} +0 -0
- /package/dist/{chunk-CZNV3VXM.js.map → chunk-SIDJO2T4.js.map} +0 -0
- /package/dist/{chunk-2747WN4Y.js.map → chunk-TPND5MJS.js.map} +0 -0
- /package/dist/{chunk-43BKLFZU.js.map → chunk-YDWBXXQZ.js.map} +0 -0
- /package/dist/{chunk-6PHB5CJS.js.map → chunk-ZLFRB6FI.js.map} +0 -0
|
@@ -1,743 +1,722 @@
|
|
|
1
1
|
import {
|
|
2
2
|
validateAcceptanceCriteria,
|
|
3
|
-
validateVerificationPlan
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
workItemDomainActionAdmissionProjector
|
|
7
|
-
} from "../../chunk-PC3BTTBE.js";
|
|
3
|
+
validateVerificationPlan,
|
|
4
|
+
validateWorkItemDraft
|
|
5
|
+
} from "../../chunk-2KUAG2GZ.js";
|
|
8
6
|
import {
|
|
9
7
|
depBatch
|
|
10
8
|
} from "../../chunk-WQIA3GFB.js";
|
|
11
9
|
import "../../chunk-S6HXABGL.js";
|
|
12
10
|
import "../../chunk-DGUM43GV.js";
|
|
13
11
|
|
|
14
|
-
// src/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
]
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
evidenceId: opts.evidenceId ?? `domain-action:${proposalId}:evidence`,
|
|
38
|
-
policyId: opts.policyId ?? "domain-action-proposal",
|
|
39
|
-
payload: opts.payload,
|
|
40
|
-
reason: opts.reason,
|
|
41
|
-
sourceRefs: opts.sourceRefs,
|
|
42
|
-
proposedAtMs: opts.proposedAtMs,
|
|
43
|
-
metadata: opts.metadata
|
|
12
|
+
// src/orchestration/work-item-runtime-shared.ts
|
|
13
|
+
function emitWorkItemAdmissionIssue(ctx, state, key, code, message, subjectId, sourceRefs2) {
|
|
14
|
+
if (state.issueKeys.has(key)) return;
|
|
15
|
+
state.issueKeys.add(key);
|
|
16
|
+
state.issueSeq += 1;
|
|
17
|
+
state.statusSeq += 1;
|
|
18
|
+
state.auditSeq += 1;
|
|
19
|
+
const issue = dataIssue(code, message, { subjectId, refs: sourceRefs2 });
|
|
20
|
+
const status = {
|
|
21
|
+
kind: "work-item-status",
|
|
22
|
+
statusId: `${subjectId ?? "work-item"}:mapping-issue:${state.statusSeq}`,
|
|
23
|
+
workItemId: subjectId ?? "unknown",
|
|
24
|
+
state: "mapping-issue",
|
|
25
|
+
sourceRefs: sourceRefs2,
|
|
26
|
+
issues: [issue]
|
|
27
|
+
};
|
|
28
|
+
const audit = {
|
|
29
|
+
id: `${subjectId ?? "work-item"}:${code}:${state.auditSeq}`,
|
|
30
|
+
kind: "work-item-domain-action-admission-issue",
|
|
31
|
+
subjectId,
|
|
32
|
+
issueCode: code,
|
|
33
|
+
message,
|
|
34
|
+
sourceRefs: sourceRefs2
|
|
44
35
|
};
|
|
36
|
+
ctx.down([
|
|
37
|
+
["DATA", { kind: "issue", issue }],
|
|
38
|
+
["DATA", { kind: "status", status }],
|
|
39
|
+
["DATA", { kind: "audit", audit }]
|
|
40
|
+
]);
|
|
45
41
|
}
|
|
46
|
-
function
|
|
42
|
+
function emptyWorkItemDomainActionAdmissionState() {
|
|
47
43
|
return {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
proposedAtMs: opts.proposedAtMs,
|
|
59
|
-
sourceRefs: opts.sourceRefs,
|
|
60
|
-
metadata: opts.metadata
|
|
44
|
+
proposals: /* @__PURE__ */ new Map(),
|
|
45
|
+
policies: /* @__PURE__ */ new Map(),
|
|
46
|
+
decisions: /* @__PURE__ */ new Map(),
|
|
47
|
+
admissionsById: /* @__PURE__ */ new Map(),
|
|
48
|
+
admissionsByProposal: /* @__PURE__ */ new Map(),
|
|
49
|
+
terminalDecisionIds: /* @__PURE__ */ new Set(),
|
|
50
|
+
issueKeys: /* @__PURE__ */ new Set(),
|
|
51
|
+
statusSeq: 0,
|
|
52
|
+
issueSeq: 0,
|
|
53
|
+
auditSeq: 0
|
|
61
54
|
};
|
|
62
55
|
}
|
|
63
|
-
function
|
|
56
|
+
function freezeWorkItemDomainActionAdmissionViews(state) {
|
|
64
57
|
return {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
58
|
+
admissionsByProposal: new Map(state.admissionsByProposal),
|
|
59
|
+
admissionsByWorkItem: new Map(
|
|
60
|
+
Array.from(state.admissionsByWorkItem, ([key, value]) => [key, Object.freeze([...value])])
|
|
61
|
+
),
|
|
62
|
+
issues: Object.freeze([...state.issues]),
|
|
63
|
+
audit: Object.freeze([...state.audit])
|
|
71
64
|
};
|
|
72
65
|
}
|
|
73
|
-
function
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
66
|
+
function workItemAdmissionProposalRefs(proposal) {
|
|
67
|
+
return uniqueSourceRefs([
|
|
68
|
+
ref("work-item", proposal.workItemId),
|
|
69
|
+
ref("work-item-domain-action-proposal", proposal.proposalId),
|
|
70
|
+
ref("effect-run", proposal.effectRunId),
|
|
71
|
+
ref("effect-run-result", proposal.effectRunResultId),
|
|
72
|
+
ref("work-item-evidence", proposal.evidenceId),
|
|
73
|
+
ref("work-item-effect-mapping-policy", proposal.policyId),
|
|
74
|
+
...proposal.sourceRefs ?? []
|
|
75
|
+
]);
|
|
76
|
+
}
|
|
77
|
+
function workItemAdmissionDecisionRefs(decision, proposal, policy) {
|
|
78
|
+
return uniqueSourceRefs([
|
|
79
|
+
...proposal === void 0 ? [] : workItemAdmissionProposalRefs(proposal),
|
|
80
|
+
ref("work-item-domain-action-admission-decision", decision.decisionId),
|
|
81
|
+
ref("work-item-domain-action-admission", decision.admissionId),
|
|
82
|
+
ref("work-item-domain-action-proposal", decision.proposalId),
|
|
83
|
+
...decision.policyId === void 0 ? [] : [ref("work-item-domain-action-admission-policy", decision.policyId)],
|
|
84
|
+
...policy === void 0 ? [] : [ref("work-item-domain-action-admission-policy", policy.policyId)],
|
|
85
|
+
...decision.targetProposalId === void 0 ? [] : [ref("work-item-domain-action-proposal", decision.targetProposalId)],
|
|
86
|
+
...decision.targetAdmissionId === void 0 ? [] : [ref("work-item-domain-action-admission", decision.targetAdmissionId)],
|
|
87
|
+
...decision.sourceRefs ?? []
|
|
88
|
+
]);
|
|
89
|
+
}
|
|
90
|
+
function uniqueSourceRefs(sourceRefs2) {
|
|
91
|
+
const seen = /* @__PURE__ */ new Set();
|
|
92
|
+
const unique = [];
|
|
93
|
+
for (const sourceRef of sourceRefs2) {
|
|
94
|
+
const key = sourceRefKey(sourceRef);
|
|
95
|
+
if (seen.has(key)) continue;
|
|
96
|
+
seen.add(key);
|
|
97
|
+
unique.push(sourceRef);
|
|
98
|
+
}
|
|
99
|
+
return unique;
|
|
100
|
+
}
|
|
101
|
+
function sourceRefKey(sourceRef) {
|
|
102
|
+
return `${sourceRef.kind}:${sourceRef.id}:${JSON.stringify(sourceRef.metadata ?? {})}`;
|
|
103
|
+
}
|
|
104
|
+
function projectRuntimeFact(graph, runtime, name, factory, pick) {
|
|
105
|
+
return graph.node(
|
|
106
|
+
[runtime],
|
|
78
107
|
(ctx) => {
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
for (const raw of depBatch(ctx, 0) ?? []) reduceProposalIntake(ctx, state, raw, now());
|
|
85
|
-
ctx.state.set(state);
|
|
108
|
+
for (const fact of depBatch(ctx, 0) ?? []) {
|
|
109
|
+
const typed = fact;
|
|
110
|
+
const value = pick(typed);
|
|
111
|
+
if (value !== void 0) ctx.down([["DATA", value]]);
|
|
112
|
+
}
|
|
86
113
|
},
|
|
87
|
-
{
|
|
88
|
-
name: `${name}/runtime`,
|
|
89
|
-
factory: "workItemDomainActionProposalIntakeProjector",
|
|
90
|
-
partial: true,
|
|
91
|
-
completeWhenDepsComplete: false,
|
|
92
|
-
errorWhenDepsError: false
|
|
93
|
-
}
|
|
114
|
+
{ name, factory }
|
|
94
115
|
);
|
|
116
|
+
}
|
|
117
|
+
function forEachPolicyDepBatch(ctx, start, count, fn) {
|
|
118
|
+
for (let i = 0; i < count; i += 1) {
|
|
119
|
+
for (const value of depBatch(ctx, start + i) ?? []) fn(value);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function dataIssue(code, message, opts = {}) {
|
|
95
123
|
return {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
status: project(
|
|
104
|
-
graph,
|
|
105
|
-
runtime,
|
|
106
|
-
`${name}/status`,
|
|
107
|
-
"workItemDomainActionStatus",
|
|
108
|
-
(fact) => fact.kind === "status" ? fact.value : void 0
|
|
109
|
-
),
|
|
110
|
-
issues: project(
|
|
111
|
-
graph,
|
|
112
|
-
runtime,
|
|
113
|
-
`${name}/issues`,
|
|
114
|
-
"workItemDomainActionIssues",
|
|
115
|
-
(fact) => fact.kind === "issue" ? fact.value : void 0
|
|
116
|
-
),
|
|
117
|
-
audit: project(
|
|
118
|
-
graph,
|
|
119
|
-
runtime,
|
|
120
|
-
`${name}/audit`,
|
|
121
|
-
"workItemDomainActionAudit",
|
|
122
|
-
(fact) => fact.kind === "audit" ? fact.value : void 0
|
|
123
|
-
)
|
|
124
|
+
kind: "issue",
|
|
125
|
+
code,
|
|
126
|
+
message,
|
|
127
|
+
severity: opts.severity ?? "error",
|
|
128
|
+
subjectId: opts.subjectId,
|
|
129
|
+
refs: opts.refs?.map((r) => `${r.kind}:${r.id}`),
|
|
130
|
+
details: opts.details
|
|
124
131
|
};
|
|
125
132
|
}
|
|
126
|
-
function
|
|
127
|
-
|
|
128
|
-
|
|
133
|
+
function ref(kind, id) {
|
|
134
|
+
return { kind, id };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// src/orchestration/work-item-runtime-admission.ts
|
|
138
|
+
function workItemDomainActionAdmissionProjector(graph, opts) {
|
|
139
|
+
const name = opts.name ?? "workItemDomainActionAdmissions";
|
|
140
|
+
const policyDeps = opts.admissionPolicies ?? [];
|
|
141
|
+
const policyStart = 2;
|
|
142
|
+
const now = opts.now ?? Date.now;
|
|
129
143
|
const runtime = graph.node(
|
|
130
|
-
|
|
144
|
+
[opts.proposals, opts.decisions, ...policyDeps],
|
|
131
145
|
(ctx) => {
|
|
132
|
-
const state = ctx.state.get() ??
|
|
133
|
-
proposals: /* @__PURE__ */ new Map(),
|
|
134
|
-
admissions: /* @__PURE__ */ new Map(),
|
|
135
|
-
workItems: /* @__PURE__ */ new Map(),
|
|
136
|
-
policies: /* @__PURE__ */ new Map(),
|
|
137
|
-
appliedAdmissions: /* @__PURE__ */ new Set(),
|
|
138
|
-
emittedFactIds: /* @__PURE__ */ new Set(),
|
|
139
|
-
issueKeys: /* @__PURE__ */ new Set(),
|
|
140
|
-
statusSeq: 0,
|
|
141
|
-
auditSeq: 0
|
|
142
|
-
};
|
|
146
|
+
const state = ctx.state.get() ?? emptyWorkItemDomainActionAdmissionState();
|
|
143
147
|
for (const raw of depBatch(ctx, 0) ?? []) {
|
|
144
148
|
const proposal = raw;
|
|
145
149
|
if (state.proposals.has(proposal.proposalId)) {
|
|
146
|
-
|
|
150
|
+
emitWorkItemAdmissionIssue(
|
|
147
151
|
ctx,
|
|
148
152
|
state,
|
|
149
|
-
`duplicate-proposal:${proposal.proposalId}`,
|
|
150
|
-
"duplicate-
|
|
151
|
-
`
|
|
153
|
+
`duplicate-admission-proposal:${proposal.proposalId}`,
|
|
154
|
+
"duplicate-work-item-domain-action-proposal",
|
|
155
|
+
`WorkItemDomainActionProposal '${proposal.proposalId}' was already seen by the admission projector`,
|
|
152
156
|
proposal.workItemId,
|
|
153
|
-
proposal
|
|
157
|
+
workItemAdmissionProposalRefs(proposal)
|
|
154
158
|
);
|
|
155
159
|
continue;
|
|
156
160
|
}
|
|
157
161
|
state.proposals.set(proposal.proposalId, proposal);
|
|
158
162
|
}
|
|
159
|
-
|
|
160
|
-
const
|
|
161
|
-
state.
|
|
162
|
-
}
|
|
163
|
-
if (opts.applyPolicies !== void 0) {
|
|
164
|
-
for (const raw of depBatch(ctx, 3) ?? []) {
|
|
165
|
-
const policy = raw;
|
|
166
|
-
state.policies.set(policy.policyId, policy);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
163
|
+
forEachPolicyDepBatch(ctx, policyStart, policyDeps.length, (raw) => {
|
|
164
|
+
const policy = raw;
|
|
165
|
+
state.policies.set(policy.policyId, policy);
|
|
166
|
+
});
|
|
169
167
|
for (const raw of depBatch(ctx, 1) ?? []) {
|
|
170
|
-
const
|
|
171
|
-
if (state.
|
|
172
|
-
|
|
168
|
+
const decision = raw;
|
|
169
|
+
if (state.decisions.has(decision.decisionId)) {
|
|
170
|
+
emitWorkItemAdmissionIssue(
|
|
173
171
|
ctx,
|
|
174
172
|
state,
|
|
175
|
-
`duplicate-admission:${
|
|
176
|
-
"duplicate-
|
|
177
|
-
`
|
|
178
|
-
|
|
179
|
-
|
|
173
|
+
`duplicate-admission-decision:${decision.decisionId}`,
|
|
174
|
+
"duplicate-work-item-domain-action-admission-decision",
|
|
175
|
+
`WorkItemDomainActionAdmissionDecision '${decision.decisionId}' was already seen by the admission projector`,
|
|
176
|
+
decision.proposalId,
|
|
177
|
+
workItemAdmissionDecisionRefs(decision)
|
|
180
178
|
);
|
|
181
179
|
continue;
|
|
182
180
|
}
|
|
183
|
-
state.
|
|
181
|
+
state.decisions.set(decision.decisionId, decision);
|
|
184
182
|
}
|
|
185
|
-
|
|
183
|
+
evaluateWorkItemDomainActionAdmissions(ctx, state, now());
|
|
186
184
|
ctx.state.set(state);
|
|
187
185
|
},
|
|
188
|
-
{
|
|
189
|
-
name: `${name}/runtime`,
|
|
190
|
-
factory: "workItemDomainActionApplicationProjector",
|
|
191
|
-
partial: true,
|
|
192
|
-
completeWhenDepsComplete: false,
|
|
193
|
-
errorWhenDepsError: false
|
|
194
|
-
}
|
|
186
|
+
{ name: `${name}/runtime`, factory: "workItemDomainActionAdmissionProjector", partial: true }
|
|
195
187
|
);
|
|
196
188
|
return {
|
|
197
|
-
|
|
198
|
-
graph,
|
|
199
|
-
runtime,
|
|
200
|
-
`${name}/authoringFacts`,
|
|
201
|
-
"workItemDomainActionAuthoringFacts",
|
|
202
|
-
(fact) => fact.kind === "authoring-fact" ? fact.value : void 0
|
|
203
|
-
),
|
|
204
|
-
applications: project(
|
|
189
|
+
admissions: projectRuntimeFact(
|
|
205
190
|
graph,
|
|
206
191
|
runtime,
|
|
207
|
-
`${name}/
|
|
208
|
-
"
|
|
209
|
-
(fact) => fact.kind === "
|
|
192
|
+
`${name}/admissions`,
|
|
193
|
+
"workItemDomainActionAdmissions",
|
|
194
|
+
(fact) => fact.kind === "admission" ? fact.admission : void 0
|
|
210
195
|
),
|
|
211
|
-
status:
|
|
196
|
+
status: projectRuntimeFact(
|
|
212
197
|
graph,
|
|
213
198
|
runtime,
|
|
214
199
|
`${name}/status`,
|
|
215
|
-
"
|
|
216
|
-
(fact) => fact.kind === "status" ? fact.
|
|
200
|
+
"workItemDomainActionAdmissionStatus",
|
|
201
|
+
(fact) => fact.kind === "status" ? fact.status : void 0
|
|
217
202
|
),
|
|
218
|
-
issues:
|
|
203
|
+
issues: projectRuntimeFact(
|
|
219
204
|
graph,
|
|
220
205
|
runtime,
|
|
221
206
|
`${name}/issues`,
|
|
222
|
-
"
|
|
223
|
-
(fact) => fact.kind === "issue" ? fact.
|
|
207
|
+
"workItemDomainActionAdmissionIssues",
|
|
208
|
+
(fact) => fact.kind === "issue" ? fact.issue : void 0
|
|
224
209
|
),
|
|
225
|
-
audit:
|
|
210
|
+
audit: projectRuntimeFact(
|
|
226
211
|
graph,
|
|
227
212
|
runtime,
|
|
228
213
|
`${name}/audit`,
|
|
229
|
-
"
|
|
230
|
-
(fact) => fact.kind === "audit" ? fact.
|
|
214
|
+
"workItemDomainActionAdmissionAudit",
|
|
215
|
+
(fact) => fact.kind === "audit" ? fact.audit : void 0
|
|
216
|
+
),
|
|
217
|
+
views: graph.node(
|
|
218
|
+
[runtime],
|
|
219
|
+
(ctx) => {
|
|
220
|
+
const state = ctx.state.get() ?? {
|
|
221
|
+
admissionsByProposal: /* @__PURE__ */ new Map(),
|
|
222
|
+
admissionsByWorkItem: /* @__PURE__ */ new Map(),
|
|
223
|
+
issues: [],
|
|
224
|
+
audit: []
|
|
225
|
+
};
|
|
226
|
+
for (const raw of depBatch(ctx, 0) ?? []) {
|
|
227
|
+
const fact = raw;
|
|
228
|
+
if (fact.kind === "admission") {
|
|
229
|
+
const admission = fact.admission;
|
|
230
|
+
state.admissionsByProposal.set(admission.proposalId, admission);
|
|
231
|
+
const byWorkItem = state.admissionsByWorkItem.get(admission.workItemId) ?? [];
|
|
232
|
+
byWorkItem.push(admission);
|
|
233
|
+
state.admissionsByWorkItem.set(admission.workItemId, byWorkItem);
|
|
234
|
+
} else if (fact.kind === "issue") state.issues.push(fact.issue);
|
|
235
|
+
else if (fact.kind === "audit") state.audit.push(fact.audit);
|
|
236
|
+
}
|
|
237
|
+
ctx.state.set(state);
|
|
238
|
+
ctx.down([["DATA", freezeWorkItemDomainActionAdmissionViews(state)]]);
|
|
239
|
+
},
|
|
240
|
+
{ name: `${name}/views`, factory: "workItemDomainActionAdmissionViews" }
|
|
231
241
|
)
|
|
232
242
|
};
|
|
233
243
|
}
|
|
234
|
-
function
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const now = opts.now ?? Date.now;
|
|
244
|
-
const runtime = graph.node(
|
|
245
|
-
deps,
|
|
246
|
-
(ctx) => {
|
|
247
|
-
const state = ctx.state.get() ?? {
|
|
248
|
-
proposals: /* @__PURE__ */ new Map(),
|
|
249
|
-
admissionsByCapability: /* @__PURE__ */ new Map(),
|
|
250
|
-
issueStatusesByCapability: /* @__PURE__ */ new Map(),
|
|
251
|
-
issueStatusesWithoutCapability: [],
|
|
252
|
-
policies: /* @__PURE__ */ new Map(),
|
|
253
|
-
emittedProposalDecisions: /* @__PURE__ */ new Set(),
|
|
254
|
-
emittedStatusKeys: /* @__PURE__ */ new Set(),
|
|
255
|
-
issueKeys: /* @__PURE__ */ new Set(),
|
|
256
|
-
statusSeq: 0,
|
|
257
|
-
auditSeq: 0
|
|
258
|
-
};
|
|
259
|
-
for (const raw of depBatch(ctx, 2) ?? []) {
|
|
260
|
-
const policyIssue = capabilityGuardPolicyIssue(raw);
|
|
261
|
-
if (policyIssue !== void 0) {
|
|
262
|
-
emitCapabilityGuardIssue(
|
|
263
|
-
ctx,
|
|
264
|
-
state,
|
|
265
|
-
`malformed-policy:${recordString(raw, "policyId") ?? "unknown"}`,
|
|
266
|
-
policyIssue
|
|
267
|
-
);
|
|
268
|
-
continue;
|
|
269
|
-
}
|
|
270
|
-
const policy = raw;
|
|
271
|
-
state.policies.set(policy.policyId, policy);
|
|
272
|
-
}
|
|
273
|
-
for (const raw of depBatch(ctx, 1) ?? []) {
|
|
274
|
-
const admissionIssue = capabilityAdmissionIssue(raw);
|
|
275
|
-
if (admissionIssue !== void 0) {
|
|
276
|
-
emitCapabilityGuardIssue(
|
|
277
|
-
ctx,
|
|
278
|
-
state,
|
|
279
|
-
`malformed-admission:${recordString(raw, "admissionId") ?? "unknown"}`,
|
|
280
|
-
admissionIssue
|
|
281
|
-
);
|
|
282
|
-
continue;
|
|
283
|
-
}
|
|
284
|
-
const admission = raw;
|
|
285
|
-
const capabilityKey = capabilityBoundaryRefId(admission.capability);
|
|
286
|
-
const byCapability = state.admissionsByCapability.get(capabilityKey) ?? [];
|
|
287
|
-
if (!byCapability.some((item) => item.admissionId === admission.admissionId)) {
|
|
288
|
-
state.admissionsByCapability.set(capabilityKey, [...byCapability, admission]);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
if (statusIndex >= 0) {
|
|
292
|
-
for (const raw of depBatch(ctx, statusIndex) ?? []) {
|
|
293
|
-
const statusIssue = capabilityAdmissionStatusIssue(raw);
|
|
294
|
-
if (statusIssue !== void 0) {
|
|
295
|
-
emitCapabilityGuardIssue(
|
|
296
|
-
ctx,
|
|
297
|
-
state,
|
|
298
|
-
`malformed-capability-status:${recordString(raw, "statusId") ?? "unknown"}`,
|
|
299
|
-
statusIssue
|
|
300
|
-
);
|
|
301
|
-
continue;
|
|
302
|
-
}
|
|
303
|
-
const status = raw;
|
|
304
|
-
if (status.state !== "capability-admission-issue") continue;
|
|
305
|
-
const capabilityId = status.capabilityId;
|
|
306
|
-
const capabilityKind = status.capabilityKind;
|
|
307
|
-
if (capabilityId !== void 0 && capabilityKind !== void 0) {
|
|
308
|
-
const capabilityKey = capabilityBoundaryRefId({
|
|
309
|
-
id: capabilityId,
|
|
310
|
-
kind: capabilityKind
|
|
311
|
-
});
|
|
312
|
-
const statuses = state.issueStatusesByCapability.get(capabilityKey) ?? [];
|
|
313
|
-
if (!statuses.some((item) => item.statusId === status.statusId)) {
|
|
314
|
-
state.issueStatusesByCapability.set(capabilityKey, [...statuses, status]);
|
|
315
|
-
}
|
|
316
|
-
} else if (!state.issueStatusesWithoutCapability.some((item) => item.statusId === status.statusId)) {
|
|
317
|
-
state.issueStatusesWithoutCapability.push(status);
|
|
318
|
-
}
|
|
319
|
-
for (const proposal of state.proposals.values())
|
|
320
|
-
emitCapabilityStatusIssueForProposal(ctx, state, proposal, status);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
for (const raw of depBatch(ctx, 0) ?? []) {
|
|
324
|
-
const proposalIssue = capabilityGuardProposalIssue(raw);
|
|
325
|
-
if (proposalIssue !== void 0) {
|
|
326
|
-
emitCapabilityGuardIssue(
|
|
327
|
-
ctx,
|
|
328
|
-
state,
|
|
329
|
-
`malformed-proposal:${recordString(raw, "proposalId") ?? "unknown"}`,
|
|
330
|
-
proposalIssue
|
|
331
|
-
);
|
|
332
|
-
continue;
|
|
333
|
-
}
|
|
334
|
-
const proposal = raw;
|
|
335
|
-
state.proposals.set(proposal.proposalId, proposal);
|
|
336
|
-
}
|
|
337
|
-
for (const proposal of state.proposals.values())
|
|
338
|
-
evaluateCapabilityGuardProposal(ctx, state, proposal, now());
|
|
339
|
-
ctx.state.set(state);
|
|
340
|
-
},
|
|
341
|
-
{
|
|
342
|
-
name: `${name}/runtime`,
|
|
343
|
-
factory: "workItemDomainActionCapabilityGuardProjector",
|
|
344
|
-
partial: true,
|
|
345
|
-
completeWhenDepsComplete: false,
|
|
346
|
-
errorWhenDepsError: false
|
|
244
|
+
function evaluateWorkItemDomainActionAdmissions(ctx, state, admittedAtMs) {
|
|
245
|
+
let emitted = true;
|
|
246
|
+
let passes = 0;
|
|
247
|
+
while (emitted && passes <= state.decisions.size) {
|
|
248
|
+
emitted = false;
|
|
249
|
+
passes += 1;
|
|
250
|
+
for (const decision of state.decisions.values()) {
|
|
251
|
+
if (state.terminalDecisionIds.has(decision.decisionId)) continue;
|
|
252
|
+
emitted = evaluateWorkItemDomainActionAdmission(ctx, state, decision, admittedAtMs) || emitted;
|
|
347
253
|
}
|
|
348
|
-
|
|
349
|
-
return {
|
|
350
|
-
decisions: project(
|
|
351
|
-
graph,
|
|
352
|
-
runtime,
|
|
353
|
-
`${name}/decisions`,
|
|
354
|
-
"workItemDomainActionCapabilityGuardDecisions",
|
|
355
|
-
(fact) => fact.kind === "decision" ? fact.value : void 0
|
|
356
|
-
),
|
|
357
|
-
status: project(
|
|
358
|
-
graph,
|
|
359
|
-
runtime,
|
|
360
|
-
`${name}/status`,
|
|
361
|
-
"workItemDomainActionCapabilityGuardStatus",
|
|
362
|
-
(fact) => fact.kind === "status" ? fact.value : void 0
|
|
363
|
-
),
|
|
364
|
-
issues: project(
|
|
365
|
-
graph,
|
|
366
|
-
runtime,
|
|
367
|
-
`${name}/issues`,
|
|
368
|
-
"workItemDomainActionCapabilityGuardIssues",
|
|
369
|
-
(fact) => fact.kind === "issue" ? fact.value : void 0
|
|
370
|
-
),
|
|
371
|
-
audit: project(
|
|
372
|
-
graph,
|
|
373
|
-
runtime,
|
|
374
|
-
`${name}/audit`,
|
|
375
|
-
"workItemDomainActionCapabilityGuardAudit",
|
|
376
|
-
(fact) => fact.kind === "audit" ? fact.value : void 0
|
|
377
|
-
)
|
|
378
|
-
};
|
|
254
|
+
}
|
|
379
255
|
}
|
|
380
|
-
function
|
|
381
|
-
|
|
382
|
-
const
|
|
256
|
+
function evaluateWorkItemDomainActionAdmission(ctx, state, decision, admittedAtMs) {
|
|
257
|
+
const proposal = state.proposals.get(decision.proposalId);
|
|
258
|
+
const proposalRefs = workItemAdmissionDecisionRefs(decision, proposal);
|
|
259
|
+
const subjectId = proposal?.workItemId ?? decision.proposalId;
|
|
260
|
+
if (proposal === void 0) {
|
|
261
|
+
emitWorkItemAdmissionIssue(
|
|
262
|
+
ctx,
|
|
263
|
+
state,
|
|
264
|
+
`missing-admission-proposal:${decision.decisionId}:${decision.proposalId}`,
|
|
265
|
+
"missing-work-item-domain-action-admission-proposal",
|
|
266
|
+
`WorkItemDomainActionAdmissionDecision '${decision.decisionId}' references missing proposal '${decision.proposalId}'`,
|
|
267
|
+
subjectId,
|
|
268
|
+
proposalRefs
|
|
269
|
+
);
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
const staleProposalRef = (decision.sourceRefs ?? []).find(
|
|
273
|
+
(sourceRef) => sourceRef.kind === "work-item-domain-action-proposal" && sourceRef.id !== decision.proposalId && sourceRef.id !== decision.targetProposalId
|
|
274
|
+
);
|
|
275
|
+
if (staleProposalRef !== void 0) {
|
|
276
|
+
emitWorkItemAdmissionIssue(
|
|
277
|
+
ctx,
|
|
278
|
+
state,
|
|
279
|
+
`stale-admission-proposal-ref:${decision.decisionId}:${staleProposalRef.id}`,
|
|
280
|
+
"stale-work-item-domain-action-admission-proposal-ref",
|
|
281
|
+
`WorkItemDomainActionAdmissionDecision '${decision.decisionId}' carries stale proposal ref '${staleProposalRef.id}'`,
|
|
282
|
+
proposal.workItemId,
|
|
283
|
+
proposalRefs
|
|
284
|
+
);
|
|
285
|
+
state.terminalDecisionIds.add(decision.decisionId);
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
const policy = admissionPolicyForDecision(state, decision);
|
|
383
289
|
if (typeof policy === "string") {
|
|
384
|
-
|
|
290
|
+
emitWorkItemAdmissionIssue(
|
|
385
291
|
ctx,
|
|
386
292
|
state,
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
293
|
+
`${policy}:${decision.decisionId}:${decision.policyId ?? "missing"}`,
|
|
294
|
+
policy,
|
|
295
|
+
admissionPolicyIssueMessage(policy, decision),
|
|
296
|
+
proposal.workItemId,
|
|
297
|
+
proposalRefs
|
|
392
298
|
);
|
|
393
|
-
|
|
299
|
+
if (policy === "stale-work-item-domain-action-admission-policy-ref")
|
|
300
|
+
state.terminalDecisionIds.add(decision.decisionId);
|
|
301
|
+
return false;
|
|
394
302
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
303
|
+
if (policy !== void 0) {
|
|
304
|
+
const policyIssue = validateWorkItemAdmissionPolicy(policy, decision, proposal);
|
|
305
|
+
if (policyIssue !== void 0) {
|
|
306
|
+
emitWorkItemAdmissionIssue(
|
|
307
|
+
ctx,
|
|
308
|
+
state,
|
|
309
|
+
`${policyIssue.code}:${decision.decisionId}:${policy.policyId}`,
|
|
310
|
+
policyIssue.code,
|
|
311
|
+
policyIssue.message,
|
|
312
|
+
proposal.workItemId,
|
|
313
|
+
workItemAdmissionDecisionRefs(decision, proposal, policy)
|
|
314
|
+
);
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
402
317
|
}
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
const missingCapabilityRefs = policy.capabilityRefs.filter(
|
|
407
|
-
(_, index) => admissions[index] === void 0
|
|
408
|
-
);
|
|
409
|
-
const deferredCapabilityRefs = admissions.filter((admission) => admission !== void 0).filter((admission) => admission.state === "deferred").map((admission) => capabilityBoundaryRefId(admission.capability));
|
|
410
|
-
if (missingCapabilityRefs.length > 0 || deferredCapabilityRefs.length > 0) {
|
|
411
|
-
emitCapabilityGuardStatusOnce(
|
|
318
|
+
const mergeIssue = validateWorkItemAdmissionMergeTarget(state, decision);
|
|
319
|
+
if (mergeIssue !== void 0) {
|
|
320
|
+
emitWorkItemAdmissionIssue(
|
|
412
321
|
ctx,
|
|
413
322
|
state,
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
actionKind: proposal.actionKind,
|
|
420
|
-
policyId: policy.policyId,
|
|
421
|
-
capabilityRefs: policy.capabilityRefs,
|
|
422
|
-
admissionSubjectIds: policy.admissionSubjectIds,
|
|
423
|
-
message: "WorkItem domain action is waiting for capability admission",
|
|
424
|
-
sourceRefs: capabilityGuardRefs(proposal, policy, admissions),
|
|
425
|
-
metadata: {
|
|
426
|
-
missingCapabilityRefs: missingCapabilityRefs.map(capabilityBoundaryRefId),
|
|
427
|
-
deferredCapabilityRefs
|
|
428
|
-
}
|
|
429
|
-
}
|
|
323
|
+
`${mergeIssue.code}:${decision.decisionId}:${decision.targetProposalId ?? ""}:${decision.targetAdmissionId ?? ""}`,
|
|
324
|
+
mergeIssue.code,
|
|
325
|
+
mergeIssue.message,
|
|
326
|
+
proposal.workItemId,
|
|
327
|
+
proposalRefs
|
|
430
328
|
);
|
|
431
|
-
|
|
329
|
+
if (mergeIssue.code !== "unknown-work-item-domain-action-admission-merge-target")
|
|
330
|
+
state.terminalDecisionIds.add(decision.decisionId);
|
|
331
|
+
return false;
|
|
432
332
|
}
|
|
433
|
-
|
|
434
|
-
(
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
333
|
+
if (state.admissionsById.has(decision.admissionId)) {
|
|
334
|
+
emitWorkItemAdmissionIssue(
|
|
335
|
+
ctx,
|
|
336
|
+
state,
|
|
337
|
+
`duplicate-admission-id:${decision.admissionId}`,
|
|
338
|
+
"duplicate-work-item-domain-action-admission",
|
|
339
|
+
`WorkItemDomainActionAdmission '${decision.admissionId}' was already emitted`,
|
|
340
|
+
proposal.workItemId,
|
|
341
|
+
proposalRefs
|
|
342
|
+
);
|
|
343
|
+
state.terminalDecisionIds.add(decision.decisionId);
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
if (state.admissionsByProposal.has(decision.proposalId)) {
|
|
347
|
+
emitWorkItemAdmissionIssue(
|
|
348
|
+
ctx,
|
|
349
|
+
state,
|
|
350
|
+
`duplicate-admission-proposal-decision:${decision.proposalId}:${decision.decisionId}`,
|
|
351
|
+
"duplicate-work-item-domain-action-admission-proposal",
|
|
352
|
+
`WorkItemDomainActionProposal '${decision.proposalId}' already has an admission decision`,
|
|
353
|
+
proposal.workItemId,
|
|
354
|
+
proposalRefs
|
|
355
|
+
);
|
|
356
|
+
state.terminalDecisionIds.add(decision.decisionId);
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
const admissionState = admissionStateForOutcome(decision.outcome);
|
|
360
|
+
if (admissionState === void 0) {
|
|
361
|
+
emitWorkItemAdmissionIssue(
|
|
362
|
+
ctx,
|
|
363
|
+
state,
|
|
364
|
+
`unsupported-admission-outcome:${decision.decisionId}:${String(decision.outcome)}`,
|
|
365
|
+
"unsupported-work-item-domain-action-admission-outcome",
|
|
366
|
+
`WorkItemDomainActionAdmissionDecision '${decision.decisionId}' uses unsupported outcome '${String(decision.outcome)}'`,
|
|
367
|
+
proposal.workItemId,
|
|
368
|
+
proposalRefs
|
|
369
|
+
);
|
|
370
|
+
state.terminalDecisionIds.add(decision.decisionId);
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
const admission = {
|
|
374
|
+
kind: "work-item-domain-action-admission",
|
|
375
|
+
admissionId: decision.admissionId,
|
|
442
376
|
proposalId: proposal.proposalId,
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
377
|
+
workItemId: proposal.workItemId,
|
|
378
|
+
actionKind: proposal.actionKind,
|
|
379
|
+
state: admissionState,
|
|
380
|
+
decisionId: decision.decisionId,
|
|
381
|
+
policyId: decision.policyId,
|
|
382
|
+
reason: decision.reason,
|
|
383
|
+
targetProposalId: decision.targetProposalId,
|
|
384
|
+
targetAdmissionId: decision.targetAdmissionId,
|
|
385
|
+
sourceRefs: workItemAdmissionDecisionRefs(decision, proposal, policy),
|
|
386
|
+
admittedAtMs: decision.decidedAtMs ?? admittedAtMs,
|
|
447
387
|
metadata: {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
blockedCapabilityRefs: blocked.map(
|
|
452
|
-
(admission) => capabilityBoundaryRefId(admission.capability)
|
|
453
|
-
)
|
|
388
|
+
...proposal.metadata ?? {},
|
|
389
|
+
...policy?.metadata ?? {},
|
|
390
|
+
...decision.metadata ?? {}
|
|
454
391
|
}
|
|
455
392
|
};
|
|
456
|
-
state.
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
393
|
+
state.admissionsById.set(admission.admissionId, admission);
|
|
394
|
+
state.admissionsByProposal.set(admission.proposalId, admission);
|
|
395
|
+
state.terminalDecisionIds.add(decision.decisionId);
|
|
396
|
+
state.statusSeq += 1;
|
|
397
|
+
state.auditSeq += 1;
|
|
398
|
+
const statusState = workItemAdmissionStatusState(admission.state);
|
|
399
|
+
const status = {
|
|
400
|
+
kind: "work-item-status",
|
|
401
|
+
statusId: `${proposal.workItemId}:${statusState}:${state.statusSeq}`,
|
|
460
402
|
workItemId: proposal.workItemId,
|
|
403
|
+
state: statusState,
|
|
404
|
+
sourceRefs: admission.sourceRefs,
|
|
405
|
+
effectRunId: proposal.effectRunId,
|
|
406
|
+
evidenceId: proposal.evidenceId,
|
|
461
407
|
proposalId: proposal.proposalId,
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
408
|
+
metadata: {
|
|
409
|
+
actionKind: proposal.actionKind,
|
|
410
|
+
admissionId: admission.admissionId,
|
|
411
|
+
decisionId: decision.decisionId,
|
|
412
|
+
policyId: decision.policyId
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
const audit = {
|
|
416
|
+
id: `${proposal.workItemId}:${statusState}:${state.auditSeq}`,
|
|
417
|
+
kind: `work-item-domain-action-${admission.state}`,
|
|
418
|
+
subjectId: proposal.workItemId,
|
|
419
|
+
sourceRefs: admission.sourceRefs,
|
|
420
|
+
metadata: {
|
|
421
|
+
actionKind: proposal.actionKind,
|
|
422
|
+
admissionId: admission.admissionId,
|
|
423
|
+
decisionId: decision.decisionId,
|
|
424
|
+
policyId: decision.policyId,
|
|
425
|
+
proposalId: proposal.proposalId
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
ctx.down([
|
|
429
|
+
["DATA", { kind: "admission", admission }],
|
|
430
|
+
["DATA", { kind: "status", status }],
|
|
431
|
+
["DATA", { kind: "audit", audit }]
|
|
432
|
+
]);
|
|
433
|
+
return true;
|
|
470
434
|
}
|
|
471
|
-
function
|
|
472
|
-
if (
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
}
|
|
479
|
-
return void 0;
|
|
435
|
+
function admissionPolicyForDecision(state, decision) {
|
|
436
|
+
if (decision.policyId === void 0) return void 0;
|
|
437
|
+
const stalePolicyRef = (decision.sourceRefs ?? []).find(
|
|
438
|
+
(sourceRef) => sourceRef.kind === "work-item-domain-action-admission-policy" && sourceRef.id !== decision.policyId
|
|
439
|
+
);
|
|
440
|
+
if (stalePolicyRef !== void 0) return "stale-work-item-domain-action-admission-policy-ref";
|
|
441
|
+
return state.policies.get(decision.policyId) ?? "missing-work-item-domain-action-admission-policy";
|
|
480
442
|
}
|
|
481
|
-
function
|
|
482
|
-
if (
|
|
483
|
-
return
|
|
484
|
-
"malformed-work-item-domain-action-capability-guard-policy",
|
|
485
|
-
"WorkItem domain action capability guard policy requires policyId, capabilityRefs, and admissionSubjectIds",
|
|
486
|
-
{
|
|
487
|
-
subjectId: recordString(policy, "policyId")
|
|
488
|
-
}
|
|
489
|
-
);
|
|
443
|
+
function admissionPolicyIssueMessage(code, decision) {
|
|
444
|
+
if (code === "missing-work-item-domain-action-admission-policy") {
|
|
445
|
+
return `WorkItemDomainActionAdmissionPolicy '${decision.policyId}' was referenced for WorkItem action admission but not present`;
|
|
490
446
|
}
|
|
491
|
-
return
|
|
447
|
+
return `WorkItemDomainActionAdmissionDecision '${decision.decisionId}' carries a stale admission policy source ref`;
|
|
492
448
|
}
|
|
493
|
-
function
|
|
494
|
-
if (
|
|
495
|
-
return
|
|
496
|
-
"
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
449
|
+
function validateWorkItemAdmissionPolicy(policy, decision, proposal) {
|
|
450
|
+
if (policy.actionKinds !== void 0 && !policy.actionKinds.includes(proposal.actionKind)) {
|
|
451
|
+
return {
|
|
452
|
+
code: "work-item-domain-action-admission-policy-mismatch",
|
|
453
|
+
message: `WorkItemDomainActionAdmissionPolicy '${policy.policyId}' does not apply to actionKind '${proposal.actionKind}'`
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
if (policy.allowedOutcomes !== void 0 && !policy.allowedOutcomes.includes(decision.outcome)) {
|
|
457
|
+
return {
|
|
458
|
+
code: "unsupported-work-item-domain-action-admission-outcome",
|
|
459
|
+
message: `WorkItemDomainActionAdmissionPolicy '${policy.policyId}' does not allow outcome '${decision.outcome}'`
|
|
460
|
+
};
|
|
500
461
|
}
|
|
501
462
|
return void 0;
|
|
502
463
|
}
|
|
503
|
-
function
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
{
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
return dataIssue(
|
|
515
|
-
"malformed-capability-admission-status",
|
|
516
|
-
"WorkItem domain action capability guard requires valid CapabilityAdmissionStatus DATA facts",
|
|
517
|
-
{ subjectId: recordString(status, "subjectId") }
|
|
518
|
-
);
|
|
464
|
+
function validateWorkItemAdmissionMergeTarget(state, decision) {
|
|
465
|
+
const hasTargetProposal = decision.targetProposalId !== void 0;
|
|
466
|
+
const hasTargetAdmission = decision.targetAdmissionId !== void 0;
|
|
467
|
+
if (decision.outcome !== "merge") {
|
|
468
|
+
if (hasTargetProposal || hasTargetAdmission) {
|
|
469
|
+
return {
|
|
470
|
+
code: "ambiguous-work-item-domain-action-admission-merge-target",
|
|
471
|
+
message: `WorkItemDomainActionAdmissionDecision '${decision.decisionId}' carries a merge target for non-merge outcome '${decision.outcome}'`
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
return void 0;
|
|
519
475
|
}
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
return value === "auth" || value === "permission" || value === "config" || value === "resource";
|
|
544
|
-
}
|
|
545
|
-
function selectCapabilityGuardPolicy(state, proposal) {
|
|
546
|
-
const explicitId = stringMetadata(proposal.metadata, "capabilityGuardPolicyId") ?? proposal.sourceRefs?.find(
|
|
547
|
-
(sourceRef) => sourceRef.kind === "work-item-domain-action-capability-guard-policy"
|
|
548
|
-
)?.id;
|
|
549
|
-
if (explicitId !== void 0)
|
|
550
|
-
return state.policies.get(explicitId) ?? "unknown-work-item-domain-action-capability-guard-policy";
|
|
551
|
-
const matching = [...state.policies.values()].filter(
|
|
552
|
-
(policy) => policy.actionKinds === void 0 || policy.actionKinds.includes(proposal.actionKind)
|
|
553
|
-
);
|
|
554
|
-
if (matching.length === 0) return "missing-policy";
|
|
555
|
-
if (matching.length > 1) return "policy-mismatch";
|
|
556
|
-
return matching[0];
|
|
557
|
-
}
|
|
558
|
-
function latestCapabilityAdmission(state, policy, capabilityRef) {
|
|
559
|
-
const admissions = state.admissionsByCapability.get(capabilityBoundaryRefId(capabilityRef)) ?? [];
|
|
560
|
-
for (let index = admissions.length - 1; index >= 0; index -= 1) {
|
|
561
|
-
const admission = admissions[index];
|
|
562
|
-
if (admission !== void 0 && policy.admissionSubjectIds.includes(admission.subjectId))
|
|
563
|
-
return admission;
|
|
476
|
+
if (hasTargetProposal === hasTargetAdmission) {
|
|
477
|
+
return {
|
|
478
|
+
code: "ambiguous-work-item-domain-action-admission-merge-target",
|
|
479
|
+
message: `WorkItemDomainActionAdmissionDecision '${decision.decisionId}' must reference exactly one merge target`
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
if (decision.targetProposalId === decision.proposalId) {
|
|
483
|
+
return {
|
|
484
|
+
code: "ambiguous-work-item-domain-action-admission-merge-target",
|
|
485
|
+
message: `WorkItemDomainActionAdmissionDecision '${decision.decisionId}' cannot merge a proposal into itself`
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
if (decision.targetProposalId !== void 0 && !state.proposals.has(decision.targetProposalId)) {
|
|
489
|
+
return {
|
|
490
|
+
code: "unknown-work-item-domain-action-admission-merge-target",
|
|
491
|
+
message: `WorkItemDomainActionAdmissionDecision '${decision.decisionId}' references unknown target proposal '${decision.targetProposalId}'`
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
if (decision.targetAdmissionId !== void 0 && !state.admissionsById.has(decision.targetAdmissionId)) {
|
|
495
|
+
return {
|
|
496
|
+
code: "unknown-work-item-domain-action-admission-merge-target",
|
|
497
|
+
message: `WorkItemDomainActionAdmissionDecision '${decision.decisionId}' references unknown target admission '${decision.targetAdmissionId}'`
|
|
498
|
+
};
|
|
564
499
|
}
|
|
565
500
|
return void 0;
|
|
566
501
|
}
|
|
567
|
-
function
|
|
568
|
-
|
|
569
|
-
if (
|
|
570
|
-
if (
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
const capabilityKind = status.capabilityKind;
|
|
574
|
-
const statusCapabilityKey = capabilityId === void 0 || capabilityKind === void 0 ? void 0 : capabilityBoundaryRefId({ id: capabilityId, kind: capabilityKind });
|
|
575
|
-
if (statusCapabilityKey !== void 0 && !policy.capabilityRefs.some(
|
|
576
|
-
(capabilityRef) => capabilityBoundaryRefId(capabilityRef) === statusCapabilityKey
|
|
577
|
-
))
|
|
578
|
-
return;
|
|
579
|
-
const issue = status.issues?.[0] ?? dataIssue(
|
|
580
|
-
"capability-admission-status-issue",
|
|
581
|
-
"Capability admission status reported an issue",
|
|
582
|
-
{ subjectId: proposal.workItemId }
|
|
583
|
-
);
|
|
584
|
-
emitCapabilityGuardIssue(
|
|
585
|
-
ctx,
|
|
586
|
-
state,
|
|
587
|
-
`capability-status:${proposal.proposalId}:${status.statusId}`,
|
|
588
|
-
dataIssue(issue.code, issue.message, {
|
|
589
|
-
subjectId: proposal.workItemId,
|
|
590
|
-
refs: uniqueSourceRefs([
|
|
591
|
-
...capabilityGuardRefs(proposal, policy),
|
|
592
|
-
...status.sourceRefs ?? []
|
|
593
|
-
]),
|
|
594
|
-
metadata: {
|
|
595
|
-
...statusCapabilityKey === void 0 ? {} : { capabilityRef: statusCapabilityKey },
|
|
596
|
-
capabilityAdmissionStatusId: status.statusId
|
|
597
|
-
}
|
|
598
|
-
})
|
|
599
|
-
);
|
|
600
|
-
}
|
|
601
|
-
function capabilityGuardPolicyMessage(code, proposal) {
|
|
602
|
-
if (code === "missing-policy")
|
|
603
|
-
return `WorkItem domain action '${proposal.proposalId}' has no capability guard policy`;
|
|
604
|
-
if (code === "unknown-work-item-domain-action-capability-guard-policy")
|
|
605
|
-
return `WorkItem domain action '${proposal.proposalId}' references a missing capability guard policy`;
|
|
606
|
-
return `WorkItem domain action '${proposal.proposalId}' matches multiple capability guard policies`;
|
|
607
|
-
}
|
|
608
|
-
function capabilityGuardRefs(proposal, policy, admissions = []) {
|
|
609
|
-
return uniqueSourceRefs([
|
|
610
|
-
ref("work-item", proposal.workItemId),
|
|
611
|
-
ref("work-item-domain-action-proposal", proposal.proposalId),
|
|
612
|
-
ref("work-item-domain-action-capability-guard-policy", policy.policyId),
|
|
613
|
-
...admissions.filter((admission) => admission !== void 0).flatMap((admission) => [
|
|
614
|
-
ref("capability-admission", admission.admissionId),
|
|
615
|
-
ref("capability-admission-proposal", admission.proposalId),
|
|
616
|
-
ref("boundary-capability", capabilityBoundaryRefId(admission.capability)),
|
|
617
|
-
...admission.sourceRefs ?? []
|
|
618
|
-
]),
|
|
619
|
-
...proposal.sourceRefs ?? []
|
|
620
|
-
]);
|
|
502
|
+
function admissionStateForOutcome(outcome) {
|
|
503
|
+
if (outcome === "admit") return "admitted";
|
|
504
|
+
if (outcome === "reject") return "rejected";
|
|
505
|
+
if (outcome === "defer") return "deferred";
|
|
506
|
+
if (outcome === "merge") return "merged";
|
|
507
|
+
return void 0;
|
|
621
508
|
}
|
|
622
|
-
function
|
|
623
|
-
return
|
|
509
|
+
function workItemAdmissionStatusState(state) {
|
|
510
|
+
if (state === "admitted") return "domain-action-admitted";
|
|
511
|
+
if (state === "rejected") return "domain-action-rejected";
|
|
512
|
+
if (state === "deferred") return "domain-action-deferred";
|
|
513
|
+
return "domain-action-merged";
|
|
624
514
|
}
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
};
|
|
632
|
-
emitCapabilityGuard(ctx, "status", statusFact);
|
|
633
|
-
state.auditSeq += 1;
|
|
634
|
-
emitCapabilityGuard(ctx, "audit", {
|
|
635
|
-
id: `work-item-domain-action-capability-guard-status:${state.auditSeq}`,
|
|
636
|
-
kind: "work-item-domain-action-capability-guard-status",
|
|
515
|
+
|
|
516
|
+
// src/solutions/work-item/actions-shared.ts
|
|
517
|
+
function auditRecord(kind, seq, status) {
|
|
518
|
+
return {
|
|
519
|
+
id: `${kind}:${seq}`,
|
|
520
|
+
kind,
|
|
637
521
|
subjectId: status.workItemId,
|
|
638
522
|
message: status.message,
|
|
523
|
+
issueCode: status.code,
|
|
639
524
|
sourceRefs: status.sourceRefs,
|
|
640
525
|
metadata: {
|
|
641
|
-
statusId:
|
|
526
|
+
statusId: status.statusId,
|
|
642
527
|
state: status.state,
|
|
643
528
|
proposalId: status.proposalId,
|
|
644
|
-
|
|
529
|
+
admissionId: status.admissionId,
|
|
530
|
+
actionKind: status.actionKind,
|
|
645
531
|
...status.metadata ?? {}
|
|
646
532
|
}
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
function emitCapabilityGuardStatusOnce(ctx, state, key, status) {
|
|
650
|
-
if (state.emittedStatusKeys.has(key)) return;
|
|
651
|
-
state.emittedStatusKeys.add(key);
|
|
652
|
-
emitCapabilityGuardStatus(ctx, state, status);
|
|
653
|
-
}
|
|
654
|
-
function emitCapabilityGuardIssue(ctx, state, key, issue) {
|
|
655
|
-
if (state.issueKeys.has(key)) return;
|
|
656
|
-
state.issueKeys.add(key);
|
|
657
|
-
emitCapabilityGuard(ctx, "issue", issue);
|
|
658
|
-
emitCapabilityGuardStatus(ctx, state, {
|
|
659
|
-
state: "issue",
|
|
660
|
-
workItemId: issue.subjectId,
|
|
661
|
-
issues: [issue],
|
|
662
|
-
message: issue.message,
|
|
663
|
-
metadata: { issueCode: issue.code }
|
|
664
|
-
});
|
|
533
|
+
};
|
|
665
534
|
}
|
|
666
|
-
function
|
|
667
|
-
|
|
535
|
+
function project(graph, runtime, name, factory, select) {
|
|
536
|
+
return graph.node(
|
|
537
|
+
[runtime],
|
|
538
|
+
(ctx) => {
|
|
539
|
+
for (const raw of depBatch(ctx, 0) ?? []) {
|
|
540
|
+
const selected = select(raw);
|
|
541
|
+
if (selected !== void 0) ctx.down([["DATA", selected]]);
|
|
542
|
+
}
|
|
543
|
+
},
|
|
544
|
+
{ name, factory, partial: true, completeWhenDepsComplete: false, errorWhenDepsError: false }
|
|
545
|
+
);
|
|
668
546
|
}
|
|
669
|
-
function
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
}
|
|
681
|
-
if (state.proposals.has(proposal.proposalId)) {
|
|
682
|
-
emitIntakeStatus(ctx, state, {
|
|
683
|
-
state: "duplicate",
|
|
684
|
-
code: "duplicate-suppressed",
|
|
685
|
-
workItemId: proposal.workItemId,
|
|
686
|
-
proposalId: proposal.proposalId,
|
|
687
|
-
actionKind: proposal.actionKind,
|
|
688
|
-
message: `Duplicate WorkItem domain action proposal '${proposal.proposalId}' suppressed`
|
|
689
|
-
});
|
|
690
|
-
return;
|
|
691
|
-
}
|
|
692
|
-
state.proposals.add(proposal.proposalId);
|
|
693
|
-
emitIntake(ctx, "proposal", proposal);
|
|
694
|
-
emitIntakeStatus(ctx, state, {
|
|
695
|
-
state: "proposed",
|
|
696
|
-
workItemId: proposal.workItemId,
|
|
697
|
-
proposalId: proposal.proposalId,
|
|
698
|
-
actionKind: proposal.actionKind,
|
|
699
|
-
sourceRefs: proposal.sourceRefs
|
|
700
|
-
});
|
|
547
|
+
function dataIssue2(code, message, opts = {}) {
|
|
548
|
+
return {
|
|
549
|
+
kind: "issue",
|
|
550
|
+
code,
|
|
551
|
+
message,
|
|
552
|
+
severity: "error",
|
|
553
|
+
source: "work-item-actions",
|
|
554
|
+
subjectId: opts.subjectId,
|
|
555
|
+
refs: opts.refs?.map((sourceRef) => `${sourceRef.kind}:${sourceRef.id}`),
|
|
556
|
+
metadata: opts.metadata
|
|
557
|
+
};
|
|
701
558
|
}
|
|
702
|
-
function
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
if (typeof input.actionKind !== "string" || input.actionKind.trim() === "")
|
|
711
|
-
return "WorkItemDomainActionProposal.actionKind is required";
|
|
712
|
-
if (input.kind === "work-item-domain-action-proposal") {
|
|
713
|
-
if (typeof input.effectRunId !== "string")
|
|
714
|
-
return "WorkItemDomainActionProposal.effectRunId is required";
|
|
715
|
-
if (typeof input.effectRunResultId !== "string")
|
|
716
|
-
return "WorkItemDomainActionProposal.effectRunResultId is required";
|
|
717
|
-
if (typeof input.evidenceId !== "string")
|
|
718
|
-
return "WorkItemDomainActionProposal.evidenceId is required";
|
|
719
|
-
if (typeof input.policyId !== "string")
|
|
720
|
-
return "WorkItemDomainActionProposal.policyId is required";
|
|
721
|
-
return input;
|
|
559
|
+
function uniqueSourceRefs2(sourceRefs2) {
|
|
560
|
+
const seen = /* @__PURE__ */ new Set();
|
|
561
|
+
const out = [];
|
|
562
|
+
for (const sourceRef of sourceRefs2) {
|
|
563
|
+
const key = `${sourceRef.kind}:${sourceRef.id}:${JSON.stringify(sourceRef.metadata ?? {})}`;
|
|
564
|
+
if (seen.has(key)) continue;
|
|
565
|
+
seen.add(key);
|
|
566
|
+
out.push(sourceRef);
|
|
722
567
|
}
|
|
723
|
-
return
|
|
724
|
-
input.proposalId,
|
|
725
|
-
input.workItemId,
|
|
726
|
-
input.actionKind,
|
|
727
|
-
{
|
|
728
|
-
payload: input.payload,
|
|
729
|
-
reason: typeof input.reason === "string" ? input.reason : void 0,
|
|
730
|
-
policyId: typeof input.policyId === "string" ? input.policyId : void 0,
|
|
731
|
-
effectRunId: typeof input.effectRunId === "string" ? input.effectRunId : void 0,
|
|
732
|
-
effectRunResultId: typeof input.effectRunResultId === "string" ? input.effectRunResultId : void 0,
|
|
733
|
-
evidenceId: typeof input.evidenceId === "string" ? input.evidenceId : void 0,
|
|
734
|
-
proposedAtMs: typeof input.proposedAtMs === "number" ? input.proposedAtMs : proposedAtMs,
|
|
735
|
-
sourceRefs: sourceRefs(input.sourceRefs),
|
|
736
|
-
metadata: isRecord(input.metadata) ? input.metadata : void 0
|
|
737
|
-
}
|
|
738
|
-
);
|
|
568
|
+
return out;
|
|
739
569
|
}
|
|
740
|
-
function
|
|
570
|
+
function sourceRefs(value) {
|
|
571
|
+
if (!Array.isArray(value)) return void 0;
|
|
572
|
+
return value.filter(
|
|
573
|
+
(item) => isRecord(item) && typeof item.kind === "string" && typeof item.id === "string"
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
function numberMetadata(metadata, key) {
|
|
577
|
+
const value = metadata?.[key];
|
|
578
|
+
return typeof value === "number" ? value : void 0;
|
|
579
|
+
}
|
|
580
|
+
function stringMetadata(metadata, key) {
|
|
581
|
+
const value = metadata?.[key];
|
|
582
|
+
return typeof value === "string" ? value : void 0;
|
|
583
|
+
}
|
|
584
|
+
function ref2(kind, id) {
|
|
585
|
+
return { kind, id };
|
|
586
|
+
}
|
|
587
|
+
function recordString(value, key) {
|
|
588
|
+
return isRecord(value) && typeof value[key] === "string" ? value[key] : void 0;
|
|
589
|
+
}
|
|
590
|
+
function isRecord(value) {
|
|
591
|
+
return value !== null && typeof value === "object";
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// src/solutions/work-item/actions-types.ts
|
|
595
|
+
var patchFieldKeys = /* @__PURE__ */ new Set([
|
|
596
|
+
"summary",
|
|
597
|
+
"detail",
|
|
598
|
+
"detailRefs",
|
|
599
|
+
"kind",
|
|
600
|
+
"priority",
|
|
601
|
+
"tags",
|
|
602
|
+
"owner",
|
|
603
|
+
"assignee",
|
|
604
|
+
"deadlineMs",
|
|
605
|
+
"customFields",
|
|
606
|
+
"sourceRefs",
|
|
607
|
+
"metadata"
|
|
608
|
+
]);
|
|
609
|
+
|
|
610
|
+
// src/solutions/work-item/actions-application.ts
|
|
611
|
+
function workItemDomainActionApplicationProjector(graph, opts) {
|
|
612
|
+
const name = opts.name ?? "workItemDomainActionApplication";
|
|
613
|
+
const deps = opts.applyPolicies === void 0 ? [opts.proposals, opts.admissions, opts.workItems] : [opts.proposals, opts.admissions, opts.workItems, opts.applyPolicies];
|
|
614
|
+
const runtime = graph.node(
|
|
615
|
+
deps,
|
|
616
|
+
(ctx) => {
|
|
617
|
+
const state = ctx.state.get() ?? {
|
|
618
|
+
proposals: /* @__PURE__ */ new Map(),
|
|
619
|
+
admissions: /* @__PURE__ */ new Map(),
|
|
620
|
+
workItems: /* @__PURE__ */ new Map(),
|
|
621
|
+
policies: /* @__PURE__ */ new Map(),
|
|
622
|
+
appliedAdmissions: /* @__PURE__ */ new Set(),
|
|
623
|
+
emittedFactIds: /* @__PURE__ */ new Set(),
|
|
624
|
+
issueKeys: /* @__PURE__ */ new Set(),
|
|
625
|
+
statusSeq: 0,
|
|
626
|
+
auditSeq: 0
|
|
627
|
+
};
|
|
628
|
+
for (const raw of depBatch(ctx, 0) ?? []) {
|
|
629
|
+
const proposal = raw;
|
|
630
|
+
if (state.proposals.has(proposal.proposalId)) {
|
|
631
|
+
emitApplicationIssue(
|
|
632
|
+
ctx,
|
|
633
|
+
state,
|
|
634
|
+
`duplicate-proposal:${proposal.proposalId}`,
|
|
635
|
+
"duplicate-suppressed",
|
|
636
|
+
`Duplicate WorkItem domain action proposal '${proposal.proposalId}' suppressed`,
|
|
637
|
+
proposal.workItemId,
|
|
638
|
+
proposal.sourceRefs
|
|
639
|
+
);
|
|
640
|
+
continue;
|
|
641
|
+
}
|
|
642
|
+
state.proposals.set(proposal.proposalId, proposal);
|
|
643
|
+
}
|
|
644
|
+
for (const raw of depBatch(ctx, 2) ?? []) {
|
|
645
|
+
const workItem = raw;
|
|
646
|
+
state.workItems.set(workItem.workItemId, workItem);
|
|
647
|
+
}
|
|
648
|
+
if (opts.applyPolicies !== void 0) {
|
|
649
|
+
for (const raw of depBatch(ctx, 3) ?? []) {
|
|
650
|
+
const policy = raw;
|
|
651
|
+
state.policies.set(policy.policyId, policy);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
for (const raw of depBatch(ctx, 1) ?? []) {
|
|
655
|
+
const admission = raw;
|
|
656
|
+
if (state.admissions.has(admission.admissionId)) {
|
|
657
|
+
emitApplicationIssue(
|
|
658
|
+
ctx,
|
|
659
|
+
state,
|
|
660
|
+
`duplicate-admission:${admission.admissionId}`,
|
|
661
|
+
"duplicate-suppressed",
|
|
662
|
+
`Duplicate WorkItem domain action admission '${admission.admissionId}' suppressed`,
|
|
663
|
+
admission.workItemId,
|
|
664
|
+
admissionRefs(admission)
|
|
665
|
+
);
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
state.admissions.set(admission.admissionId, admission);
|
|
669
|
+
}
|
|
670
|
+
for (const admission of state.admissions.values()) applyAdmission(ctx, state, admission);
|
|
671
|
+
ctx.state.set(state);
|
|
672
|
+
},
|
|
673
|
+
{
|
|
674
|
+
name: `${name}/runtime`,
|
|
675
|
+
factory: "workItemDomainActionApplicationProjector",
|
|
676
|
+
partial: true,
|
|
677
|
+
completeWhenDepsComplete: false,
|
|
678
|
+
errorWhenDepsError: false
|
|
679
|
+
}
|
|
680
|
+
);
|
|
681
|
+
return {
|
|
682
|
+
authoringFacts: project(
|
|
683
|
+
graph,
|
|
684
|
+
runtime,
|
|
685
|
+
`${name}/authoringFacts`,
|
|
686
|
+
"workItemDomainActionAuthoringFacts",
|
|
687
|
+
(fact) => fact.kind === "authoring-fact" ? fact.value : void 0
|
|
688
|
+
),
|
|
689
|
+
applications: project(
|
|
690
|
+
graph,
|
|
691
|
+
runtime,
|
|
692
|
+
`${name}/applications`,
|
|
693
|
+
"workItemDomainActionApplications",
|
|
694
|
+
(fact) => fact.kind === "application" ? fact.value : void 0
|
|
695
|
+
),
|
|
696
|
+
status: project(
|
|
697
|
+
graph,
|
|
698
|
+
runtime,
|
|
699
|
+
`${name}/status`,
|
|
700
|
+
"workItemDomainActionStatus",
|
|
701
|
+
(fact) => fact.kind === "status" ? fact.value : void 0
|
|
702
|
+
),
|
|
703
|
+
issues: project(
|
|
704
|
+
graph,
|
|
705
|
+
runtime,
|
|
706
|
+
`${name}/issues`,
|
|
707
|
+
"workItemDomainActionIssues",
|
|
708
|
+
(fact) => fact.kind === "issue" ? fact.value : void 0
|
|
709
|
+
),
|
|
710
|
+
audit: project(
|
|
711
|
+
graph,
|
|
712
|
+
runtime,
|
|
713
|
+
`${name}/audit`,
|
|
714
|
+
"workItemDomainActionAudit",
|
|
715
|
+
(fact) => fact.kind === "audit" ? fact.value : void 0
|
|
716
|
+
)
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
function applyAdmission(ctx, state, admission) {
|
|
741
720
|
if (state.appliedAdmissions.has(admission.admissionId)) return;
|
|
742
721
|
const proposal = state.proposals.get(admission.proposalId);
|
|
743
722
|
if (proposal === void 0) {
|
|
@@ -1073,24 +1052,136 @@ function lowerSpawnAction(ctx, state, proposal, admission, policy) {
|
|
|
1073
1052
|
message: "Spawn action remains proposal/admission only without explicit create policy"
|
|
1074
1053
|
};
|
|
1075
1054
|
}
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1055
|
+
const policyIssue = validateSpawnCreatePolicy(policy, proposal);
|
|
1056
|
+
if (policyIssue !== void 0) {
|
|
1057
|
+
emitApplicationIssue(
|
|
1058
|
+
ctx,
|
|
1059
|
+
state,
|
|
1060
|
+
`spawn-create-policy:${admission.admissionId}:${policy.policyId}:${policyIssue.field}`,
|
|
1061
|
+
"policy-mismatch",
|
|
1062
|
+
policyIssue.message,
|
|
1063
|
+
proposal.workItemId,
|
|
1064
|
+
admissionRefs(admission, proposal, policy),
|
|
1065
|
+
{ field: policyIssue.field }
|
|
1066
|
+
);
|
|
1067
|
+
return {
|
|
1068
|
+
state: "policy-rejected",
|
|
1069
|
+
facts: [],
|
|
1070
|
+
code: "policy-mismatch",
|
|
1071
|
+
message: policyIssue.message
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
const payload = normalizeSpawnPayload(proposal.payload);
|
|
1075
|
+
if (typeof payload === "string") {
|
|
1076
|
+
emitApplicationIssue(
|
|
1077
|
+
ctx,
|
|
1078
|
+
state,
|
|
1079
|
+
`invalid-spawn-payload:${admission.admissionId}`,
|
|
1080
|
+
"invalid-patch",
|
|
1081
|
+
payload,
|
|
1082
|
+
proposal.workItemId,
|
|
1083
|
+
admissionRefs(admission, proposal, policy)
|
|
1084
|
+
);
|
|
1085
|
+
return { state: "rejected", facts: [], code: "invalid-patch", message: payload };
|
|
1086
|
+
}
|
|
1087
|
+
const childWorkItemId = payload.childWorkItemId ?? payload.workItemId ?? policy.spawn.childWorkItemId ?? (policy.spawn.childIdPrefix === void 0 ? void 0 : `${policy.spawn.childIdPrefix}${admission.admissionId}`);
|
|
1088
|
+
if (childWorkItemId === void 0 || childWorkItemId.trim() === "") {
|
|
1089
|
+
const message = "Spawn create action requires a childWorkItemId or create policy child id";
|
|
1090
|
+
emitApplicationIssue(
|
|
1091
|
+
ctx,
|
|
1092
|
+
state,
|
|
1093
|
+
`missing-spawn-child-id:${admission.admissionId}`,
|
|
1094
|
+
"missing-required-field",
|
|
1095
|
+
message,
|
|
1096
|
+
proposal.workItemId,
|
|
1097
|
+
admissionRefs(admission, proposal, policy)
|
|
1098
|
+
);
|
|
1099
|
+
return { state: "rejected", facts: [], code: "missing-required-field", message };
|
|
1100
|
+
}
|
|
1101
|
+
if (state.workItems.has(childWorkItemId)) {
|
|
1102
|
+
const message = `Spawn create action would duplicate WorkItem '${childWorkItemId}'`;
|
|
1103
|
+
emitApplicationIssue(
|
|
1104
|
+
ctx,
|
|
1105
|
+
state,
|
|
1106
|
+
`duplicate-spawn-child:${admission.admissionId}:${childWorkItemId}`,
|
|
1107
|
+
"duplicate-id",
|
|
1108
|
+
message,
|
|
1109
|
+
childWorkItemId,
|
|
1110
|
+
admissionRefs(admission, proposal, policy)
|
|
1111
|
+
);
|
|
1112
|
+
return { state: "duplicate", facts: [], code: "duplicate-id", message };
|
|
1113
|
+
}
|
|
1114
|
+
if (payload.draft === void 0) {
|
|
1115
|
+
const message = "Spawn create action requires a draft payload";
|
|
1116
|
+
emitApplicationIssue(
|
|
1117
|
+
ctx,
|
|
1118
|
+
state,
|
|
1119
|
+
`missing-spawn-draft:${admission.admissionId}`,
|
|
1120
|
+
"missing-required-field",
|
|
1121
|
+
message,
|
|
1122
|
+
childWorkItemId,
|
|
1123
|
+
admissionRefs(admission, proposal, policy)
|
|
1124
|
+
);
|
|
1125
|
+
return { state: "rejected", facts: [], code: "missing-required-field", message };
|
|
1126
|
+
}
|
|
1127
|
+
const issues = validateWorkItemDraft(payload.draft, { workItemId: childWorkItemId });
|
|
1128
|
+
if (issues.length > 0) {
|
|
1129
|
+
for (const item of issues) emitApplicationFact(ctx, "issue", item);
|
|
1130
|
+
return {
|
|
1131
|
+
state: "rejected",
|
|
1132
|
+
facts: [],
|
|
1133
|
+
code: issues[0]?.code,
|
|
1134
|
+
message: issues[0]?.message
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
const metadata = {
|
|
1138
|
+
...payload.metadata ?? {},
|
|
1139
|
+
actionKind: proposal.actionKind,
|
|
1140
|
+
parentWorkItemId: policy.spawn.linkParent === true ? proposal.workItemId : void 0,
|
|
1141
|
+
sourceProposalId: proposal.proposalId,
|
|
1142
|
+
idempotencyKey: payload.idempotencyKey ?? policy.spawn.idempotencyKey
|
|
1143
|
+
};
|
|
1144
|
+
const created = {
|
|
1145
|
+
kind: "work-item-created",
|
|
1146
|
+
eventId: payload.eventId ?? `${admission.admissionId}:work-item-created:${childWorkItemId}`,
|
|
1147
|
+
workItemId: childWorkItemId,
|
|
1148
|
+
draft: payload.draft,
|
|
1149
|
+
authorId: payload.authorId,
|
|
1150
|
+
sourceRefs: actionSourceRefs(admission, proposal, policy),
|
|
1151
|
+
metadata
|
|
1093
1152
|
};
|
|
1153
|
+
return { state: "applied", facts: [created] };
|
|
1154
|
+
}
|
|
1155
|
+
function validateSpawnCreatePolicy(policy, proposal) {
|
|
1156
|
+
if (policy.spawn?.idempotencyKey === void 0 || policy.spawn.idempotencyKey.trim() === "") {
|
|
1157
|
+
return {
|
|
1158
|
+
field: "idempotencyKey",
|
|
1159
|
+
message: `Spawn create policy '${policy.policyId}' requires an idempotencyKey`
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1162
|
+
if (policy.spawn.maxChildrenPerAdmission === void 0 || policy.spawn.maxChildrenPerAdmission < 1) {
|
|
1163
|
+
return {
|
|
1164
|
+
field: "maxChildrenPerAdmission",
|
|
1165
|
+
message: `Spawn create policy '${policy.policyId}' requires maxChildrenPerAdmission >= 1`
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
if (proposal.actionKind === "spawn-child" && policy.spawn.linkParent !== true) {
|
|
1169
|
+
return {
|
|
1170
|
+
field: "linkParent",
|
|
1171
|
+
message: `Spawn child create policy '${policy.policyId}' requires linkParent: true`
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
return void 0;
|
|
1175
|
+
}
|
|
1176
|
+
function normalizeSpawnPayload(payload) {
|
|
1177
|
+
if (!isRecord(payload)) return "Spawn action payload must be an object";
|
|
1178
|
+
if ("draft" in payload && payload.draft !== void 0 && !isRecord(payload.draft)) {
|
|
1179
|
+
return "Spawn action payload.draft must be an object";
|
|
1180
|
+
}
|
|
1181
|
+
if ("metadata" in payload && payload.metadata !== void 0 && !isRecord(payload.metadata)) {
|
|
1182
|
+
return "Spawn action payload.metadata must be an object";
|
|
1183
|
+
}
|
|
1184
|
+
return payload;
|
|
1094
1185
|
}
|
|
1095
1186
|
function normalizePatchPayload(payload) {
|
|
1096
1187
|
if (!isRecord(payload)) return "Patch action payload must be an object";
|
|
@@ -1109,338 +1200,871 @@ function normalizePatchPayload(payload) {
|
|
|
1109
1200
|
patch: Object.keys(patch).length === 0 ? void 0 : patch
|
|
1110
1201
|
};
|
|
1111
1202
|
}
|
|
1112
|
-
return { patch: payload };
|
|
1203
|
+
return { patch: payload };
|
|
1204
|
+
}
|
|
1205
|
+
function inlinePatchFields(payload) {
|
|
1206
|
+
const patch = {};
|
|
1207
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
1208
|
+
if (key === "metadata") continue;
|
|
1209
|
+
if (patchFieldKeys.has(key)) patch[key] = value;
|
|
1210
|
+
}
|
|
1211
|
+
return patch;
|
|
1212
|
+
}
|
|
1213
|
+
function splitDedicatedPatchFields(payload, workItem) {
|
|
1214
|
+
if (!isRecord(payload.patch)) return payload;
|
|
1215
|
+
const patchRecord = payload.patch;
|
|
1216
|
+
const patch = {};
|
|
1217
|
+
for (const [key, value] of Object.entries(patchRecord)) {
|
|
1218
|
+
if (key === "acceptanceCriteria" || key === "verificationPlan" || key === "verificationSteps")
|
|
1219
|
+
continue;
|
|
1220
|
+
patch[key] = value;
|
|
1221
|
+
}
|
|
1222
|
+
const acceptanceCriteria = payload.acceptanceCriteria ?? (Array.isArray(patchRecord.acceptanceCriteria) ? patchRecord.acceptanceCriteria : void 0);
|
|
1223
|
+
const verificationPlan = payload.verificationPlan ?? (isRecord(patchRecord.verificationPlan) ? patchRecord.verificationPlan : Array.isArray(patchRecord.verificationSteps) ? {
|
|
1224
|
+
...workItem.verificationPlan ?? { planId: "default" },
|
|
1225
|
+
steps: patchRecord.verificationSteps
|
|
1226
|
+
} : void 0);
|
|
1227
|
+
return {
|
|
1228
|
+
...payload,
|
|
1229
|
+
patch: Object.keys(patch).length === 0 ? void 0 : patch,
|
|
1230
|
+
acceptanceCriteria,
|
|
1231
|
+
verificationPlan
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
function invalidPatchKeys(patch, policy) {
|
|
1235
|
+
const allowed = new Set(policy.patch?.allowedFields ?? Array.from(patchFieldKeys));
|
|
1236
|
+
return Object.keys(patch).filter((key) => !patchFieldKeys.has(key) || !allowed.has(key));
|
|
1237
|
+
}
|
|
1238
|
+
function allowsAcceptanceCriteria(policy) {
|
|
1239
|
+
if (policy.patch?.allowAcceptanceCriteria === false) return false;
|
|
1240
|
+
const allowed = policy.patch?.allowedFields;
|
|
1241
|
+
return allowed === void 0 || allowed.includes("acceptanceCriteria") || policy.patch?.allowAcceptanceCriteria === true;
|
|
1242
|
+
}
|
|
1243
|
+
function allowsVerificationPlan(policy) {
|
|
1244
|
+
if (policy.patch?.allowVerificationPlan === false) return false;
|
|
1245
|
+
const allowed = policy.patch?.allowedFields;
|
|
1246
|
+
return allowed === void 0 || allowed.includes("verificationPlan") || allowed.includes("verificationSteps") || policy.patch?.allowVerificationPlan === true;
|
|
1247
|
+
}
|
|
1248
|
+
function selectApplyPolicy(state, proposal, admission) {
|
|
1249
|
+
const explicitId = explicitApplyPolicyId(proposal, admission);
|
|
1250
|
+
if (explicitId !== void 0)
|
|
1251
|
+
return state.policies.get(explicitId) ?? "unknown-work-item-domain-action-apply-policy";
|
|
1252
|
+
const matching = Array.from(state.policies.values()).filter(
|
|
1253
|
+
(policy) => policyAllowsAction(policy, proposal.actionKind)
|
|
1254
|
+
);
|
|
1255
|
+
if (matching.length === 0) return "missing-policy";
|
|
1256
|
+
if (matching.length > 1) return "policy-mismatch";
|
|
1257
|
+
return matching[0];
|
|
1258
|
+
}
|
|
1259
|
+
function explicitApplyPolicyId(proposal, admission) {
|
|
1260
|
+
const metadataPolicy = stringMetadata(admission.metadata, "applyPolicyId") ?? stringMetadata(proposal.metadata, "applyPolicyId");
|
|
1261
|
+
if (metadataPolicy !== void 0) return metadataPolicy;
|
|
1262
|
+
return [...admission.sourceRefs ?? [], ...proposal.sourceRefs ?? []].find(
|
|
1263
|
+
(sourceRef) => sourceRef.kind === "work-item-domain-action-apply-policy"
|
|
1264
|
+
)?.id;
|
|
1265
|
+
}
|
|
1266
|
+
function policyAllowsAction(policy, actionKind) {
|
|
1267
|
+
return policy.actionKinds === void 0 || policy.actionKinds.includes(actionKind);
|
|
1268
|
+
}
|
|
1269
|
+
function admissionProposalMismatch(admission, proposal) {
|
|
1270
|
+
if (admission.workItemId !== proposal.workItemId)
|
|
1271
|
+
return {
|
|
1272
|
+
field: "workItemId",
|
|
1273
|
+
admissionValue: admission.workItemId,
|
|
1274
|
+
proposalValue: proposal.workItemId,
|
|
1275
|
+
message: `Admission '${admission.admissionId}' workItemId does not match proposal '${proposal.proposalId}'`
|
|
1276
|
+
};
|
|
1277
|
+
if (admission.actionKind !== proposal.actionKind)
|
|
1278
|
+
return {
|
|
1279
|
+
field: "actionKind",
|
|
1280
|
+
admissionValue: admission.actionKind,
|
|
1281
|
+
proposalValue: proposal.actionKind,
|
|
1282
|
+
message: `Admission '${admission.admissionId}' actionKind does not match proposal '${proposal.proposalId}'`
|
|
1283
|
+
};
|
|
1284
|
+
return void 0;
|
|
1285
|
+
}
|
|
1286
|
+
function conflictingApplicationMetadata(proposal, admission) {
|
|
1287
|
+
const authoringRevision = conflictingMetadataNumber(proposal, admission, "authoringRevision");
|
|
1288
|
+
if (authoringRevision !== void 0)
|
|
1289
|
+
return {
|
|
1290
|
+
field: "authoringRevision",
|
|
1291
|
+
code: "stale-revision",
|
|
1292
|
+
...authoringRevision,
|
|
1293
|
+
message: `Admission '${admission.admissionId}' carries conflicting authoringRevision metadata`
|
|
1294
|
+
};
|
|
1295
|
+
const executionInputRevision = conflictingMetadataNumber(
|
|
1296
|
+
proposal,
|
|
1297
|
+
admission,
|
|
1298
|
+
"executionInputRevision"
|
|
1299
|
+
);
|
|
1300
|
+
if (executionInputRevision !== void 0)
|
|
1301
|
+
return {
|
|
1302
|
+
field: "executionInputRevision",
|
|
1303
|
+
code: "stale-execution-input",
|
|
1304
|
+
...executionInputRevision,
|
|
1305
|
+
message: `Admission '${admission.admissionId}' carries conflicting executionInputRevision metadata`
|
|
1306
|
+
};
|
|
1307
|
+
const applyPolicyId = conflictingMetadataString(proposal, admission, "applyPolicyId");
|
|
1308
|
+
if (applyPolicyId !== void 0)
|
|
1309
|
+
return {
|
|
1310
|
+
field: "applyPolicyId",
|
|
1311
|
+
code: "policy-mismatch",
|
|
1312
|
+
...applyPolicyId,
|
|
1313
|
+
message: `Admission '${admission.admissionId}' carries conflicting applyPolicyId metadata`
|
|
1314
|
+
};
|
|
1315
|
+
return void 0;
|
|
1316
|
+
}
|
|
1317
|
+
function conflictingMetadataNumber(proposal, admission, key) {
|
|
1318
|
+
const proposalValue = numberMetadata(proposal.metadata, key);
|
|
1319
|
+
const admissionValue = numberMetadata(admission.metadata, key);
|
|
1320
|
+
if (proposalValue === void 0 || admissionValue === void 0 || proposalValue === admissionValue)
|
|
1321
|
+
return void 0;
|
|
1322
|
+
return { proposalValue, admissionValue };
|
|
1323
|
+
}
|
|
1324
|
+
function conflictingMetadataString(proposal, admission, key) {
|
|
1325
|
+
const proposalValue = stringMetadata(proposal.metadata, key);
|
|
1326
|
+
const admissionValue = stringMetadata(admission.metadata, key);
|
|
1327
|
+
if (proposalValue === void 0 || admissionValue === void 0 || proposalValue === admissionValue)
|
|
1328
|
+
return void 0;
|
|
1329
|
+
return { proposalValue, admissionValue };
|
|
1330
|
+
}
|
|
1331
|
+
function staleRevision(proposal, admission, workItem) {
|
|
1332
|
+
const authoringRevision = numberMetadata(admission.metadata, "authoringRevision") ?? numberMetadata(proposal.metadata, "authoringRevision");
|
|
1333
|
+
if (authoringRevision !== void 0 && authoringRevision !== workItem.authoringRevision)
|
|
1334
|
+
return {
|
|
1335
|
+
kind: "stale-revision",
|
|
1336
|
+
expected: authoringRevision,
|
|
1337
|
+
current: workItem.authoringRevision
|
|
1338
|
+
};
|
|
1339
|
+
const executionInputRevision = numberMetadata(admission.metadata, "executionInputRevision") ?? numberMetadata(proposal.metadata, "executionInputRevision");
|
|
1340
|
+
if (executionInputRevision !== void 0 && executionInputRevision !== workItem.executionInputRevision)
|
|
1341
|
+
return {
|
|
1342
|
+
kind: "stale-execution-input",
|
|
1343
|
+
expected: executionInputRevision,
|
|
1344
|
+
current: workItem.executionInputRevision
|
|
1345
|
+
};
|
|
1346
|
+
return void 0;
|
|
1347
|
+
}
|
|
1348
|
+
function applyPolicyMessage(code, proposal, admission) {
|
|
1349
|
+
if (code === "missing-policy")
|
|
1350
|
+
return `Admitted action '${admission.admissionId}' requires an explicit apply policy`;
|
|
1351
|
+
if (code === "unknown-work-item-domain-action-apply-policy")
|
|
1352
|
+
return `Admitted action '${admission.admissionId}' references a missing apply policy`;
|
|
1353
|
+
return `Admitted action '${proposal.proposalId}' matches multiple apply policies`;
|
|
1354
|
+
}
|
|
1355
|
+
function emitApplication(ctx, state, admission, proposal, applicationState, facts, opts = {}) {
|
|
1356
|
+
const producedFactIds = facts.map((fact) => fact.eventId);
|
|
1357
|
+
const application = {
|
|
1358
|
+
kind: "work-item-domain-action-application",
|
|
1359
|
+
applicationId: `${admission.admissionId}:application`,
|
|
1360
|
+
admissionId: admission.admissionId,
|
|
1361
|
+
proposalId: proposal.proposalId,
|
|
1362
|
+
workItemId: proposal.workItemId,
|
|
1363
|
+
actionKind: proposal.actionKind,
|
|
1364
|
+
state: applicationState,
|
|
1365
|
+
producedFactIds,
|
|
1366
|
+
reason: admission.reason ?? proposal.reason,
|
|
1367
|
+
sourceRefs: admissionRefs(admission, proposal),
|
|
1368
|
+
metadata: { policyId: opts.policyId, code: opts.code }
|
|
1369
|
+
};
|
|
1370
|
+
emitApplicationFact(ctx, "application", application);
|
|
1371
|
+
emitApplicationStatus(ctx, state, {
|
|
1372
|
+
state: applicationState,
|
|
1373
|
+
code: opts.code,
|
|
1374
|
+
message: opts.message,
|
|
1375
|
+
workItemId: proposal.workItemId,
|
|
1376
|
+
proposalId: proposal.proposalId,
|
|
1377
|
+
admissionId: admission.admissionId,
|
|
1378
|
+
actionKind: proposal.actionKind,
|
|
1379
|
+
sourceRefs: application.sourceRefs,
|
|
1380
|
+
metadata: { producedFactIds, policyId: opts.policyId }
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1383
|
+
function emitApplicationIssue(ctx, state, key, code, message, workItemId, sourceRefs2, metadata) {
|
|
1384
|
+
if (state.issueKeys.has(key)) return;
|
|
1385
|
+
state.issueKeys.add(key);
|
|
1386
|
+
const item = dataIssue2(code, message, { subjectId: workItemId, refs: sourceRefs2, metadata });
|
|
1387
|
+
emitApplicationFact(ctx, "issue", item);
|
|
1388
|
+
emitApplicationStatus(ctx, state, {
|
|
1389
|
+
state: code === "missing-policy" ? "missing-policy" : "rejected",
|
|
1390
|
+
code,
|
|
1391
|
+
message,
|
|
1392
|
+
workItemId,
|
|
1393
|
+
sourceRefs: sourceRefs2,
|
|
1394
|
+
metadata
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
function emitApplicationStatus(ctx, state, status) {
|
|
1398
|
+
state.statusSeq += 1;
|
|
1399
|
+
const statusFact = {
|
|
1400
|
+
kind: "work-item-domain-action-status",
|
|
1401
|
+
statusId: `work-item-domain-action-application-status:${state.statusSeq}`,
|
|
1402
|
+
...status
|
|
1403
|
+
};
|
|
1404
|
+
emitApplicationFact(ctx, "status", statusFact);
|
|
1405
|
+
emitApplicationAudit(ctx, state, "work-item-domain-action-application-status", statusFact);
|
|
1406
|
+
}
|
|
1407
|
+
function emitApplicationAudit(ctx, state, kind, status) {
|
|
1408
|
+
state.auditSeq += 1;
|
|
1409
|
+
emitApplicationFact(ctx, "audit", auditRecord(kind, state.auditSeq, status));
|
|
1410
|
+
}
|
|
1411
|
+
function emitApplicationFact(ctx, kind, value) {
|
|
1412
|
+
ctx.down([["DATA", { kind, value }]]);
|
|
1413
|
+
}
|
|
1414
|
+
function firstDuplicateFactId(state, facts) {
|
|
1415
|
+
const pending = /* @__PURE__ */ new Set();
|
|
1416
|
+
for (const fact of facts) {
|
|
1417
|
+
if (state.emittedFactIds.has(fact.eventId) || pending.has(fact.eventId)) return fact.eventId;
|
|
1418
|
+
pending.add(fact.eventId);
|
|
1419
|
+
}
|
|
1420
|
+
return void 0;
|
|
1421
|
+
}
|
|
1422
|
+
function claimFactId(state, factId) {
|
|
1423
|
+
if (state.emittedFactIds.has(factId)) return false;
|
|
1424
|
+
state.emittedFactIds.add(factId);
|
|
1425
|
+
return true;
|
|
1426
|
+
}
|
|
1427
|
+
function admissionRefs(admission, proposal, policy) {
|
|
1428
|
+
return uniqueSourceRefs2([
|
|
1429
|
+
ref2("work-item-domain-action-admission", admission.admissionId),
|
|
1430
|
+
ref2("work-item-domain-action-proposal", admission.proposalId),
|
|
1431
|
+
...proposal === void 0 ? [] : [
|
|
1432
|
+
ref2("work-item", proposal.workItemId),
|
|
1433
|
+
ref2("work-item-evidence", proposal.evidenceId),
|
|
1434
|
+
...proposal.sourceRefs ?? []
|
|
1435
|
+
],
|
|
1436
|
+
...policy === void 0 ? [] : [ref2("work-item-domain-action-apply-policy", policy.policyId)],
|
|
1437
|
+
...admission.sourceRefs ?? []
|
|
1438
|
+
]);
|
|
1439
|
+
}
|
|
1440
|
+
function actionSourceRefs(admission, proposal, policy) {
|
|
1441
|
+
return admissionRefs(admission, proposal, policy);
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
// src/solutions/work-item/actions-builders.ts
|
|
1445
|
+
function workItemDomainActionProposal(proposalId, workItemId, actionKind, opts = {}) {
|
|
1446
|
+
return {
|
|
1447
|
+
kind: "work-item-domain-action-proposal",
|
|
1448
|
+
proposalId,
|
|
1449
|
+
workItemId,
|
|
1450
|
+
actionKind,
|
|
1451
|
+
effectRunId: opts.effectRunId ?? `domain-action:${proposalId}:effect-run`,
|
|
1452
|
+
effectRunResultId: opts.effectRunResultId ?? `domain-action:${proposalId}:effect-run-result`,
|
|
1453
|
+
evidenceId: opts.evidenceId ?? `domain-action:${proposalId}:evidence`,
|
|
1454
|
+
policyId: opts.policyId ?? "domain-action-proposal",
|
|
1455
|
+
payload: opts.payload,
|
|
1456
|
+
reason: opts.reason,
|
|
1457
|
+
sourceRefs: opts.sourceRefs,
|
|
1458
|
+
proposedAtMs: opts.proposedAtMs,
|
|
1459
|
+
metadata: opts.metadata
|
|
1460
|
+
};
|
|
1461
|
+
}
|
|
1462
|
+
function workItemDomainActionProposalIntake(proposalId, workItemId, actionKind, opts = {}) {
|
|
1463
|
+
return {
|
|
1464
|
+
kind: "work-item-domain-action-proposal-intake",
|
|
1465
|
+
proposalId,
|
|
1466
|
+
workItemId,
|
|
1467
|
+
actionKind,
|
|
1468
|
+
payload: opts.payload,
|
|
1469
|
+
reason: opts.reason,
|
|
1470
|
+
policyId: opts.policyId,
|
|
1471
|
+
effectRunId: opts.effectRunId,
|
|
1472
|
+
effectRunResultId: opts.effectRunResultId,
|
|
1473
|
+
evidenceId: opts.evidenceId,
|
|
1474
|
+
proposedAtMs: opts.proposedAtMs,
|
|
1475
|
+
sourceRefs: opts.sourceRefs,
|
|
1476
|
+
metadata: opts.metadata
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1479
|
+
function workItemDomainActionApplyPolicy(policyId, opts = {}) {
|
|
1480
|
+
return {
|
|
1481
|
+
kind: "work-item-domain-action-apply-policy",
|
|
1482
|
+
policyId,
|
|
1483
|
+
actionKinds: opts.actionKinds,
|
|
1484
|
+
patch: opts.patch,
|
|
1485
|
+
spawn: opts.spawn,
|
|
1486
|
+
metadata: opts.metadata
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
// src/solutions/work-item/actions-capability-guard.ts
|
|
1491
|
+
function workItemDomainActionCapabilityGuardProjector(graph, opts) {
|
|
1492
|
+
const name = opts.name ?? "workItemDomainActionCapabilityGuard";
|
|
1493
|
+
const deps = opts.capabilityAdmissionStatus === void 0 ? [opts.proposals, opts.capabilityAdmissions, opts.guardPolicies] : [
|
|
1494
|
+
opts.proposals,
|
|
1495
|
+
opts.capabilityAdmissions,
|
|
1496
|
+
opts.guardPolicies,
|
|
1497
|
+
opts.capabilityAdmissionStatus
|
|
1498
|
+
];
|
|
1499
|
+
const statusIndex = opts.capabilityAdmissionStatus === void 0 ? -1 : 3;
|
|
1500
|
+
const now = opts.now ?? Date.now;
|
|
1501
|
+
const runtime = graph.node(
|
|
1502
|
+
deps,
|
|
1503
|
+
(ctx) => {
|
|
1504
|
+
const state = ctx.state.get() ?? {
|
|
1505
|
+
proposals: /* @__PURE__ */ new Map(),
|
|
1506
|
+
admissionsByCapability: /* @__PURE__ */ new Map(),
|
|
1507
|
+
issueStatusesByCapability: /* @__PURE__ */ new Map(),
|
|
1508
|
+
issueStatusesWithoutCapability: [],
|
|
1509
|
+
policies: /* @__PURE__ */ new Map(),
|
|
1510
|
+
emittedProposalDecisions: /* @__PURE__ */ new Set(),
|
|
1511
|
+
emittedStatusKeys: /* @__PURE__ */ new Set(),
|
|
1512
|
+
issueKeys: /* @__PURE__ */ new Set(),
|
|
1513
|
+
statusSeq: 0,
|
|
1514
|
+
auditSeq: 0
|
|
1515
|
+
};
|
|
1516
|
+
for (const raw of depBatch(ctx, 2) ?? []) {
|
|
1517
|
+
const policyIssue = capabilityGuardPolicyIssue(raw);
|
|
1518
|
+
if (policyIssue !== void 0) {
|
|
1519
|
+
emitCapabilityGuardIssue(
|
|
1520
|
+
ctx,
|
|
1521
|
+
state,
|
|
1522
|
+
`malformed-policy:${recordString(raw, "policyId") ?? "unknown"}`,
|
|
1523
|
+
policyIssue
|
|
1524
|
+
);
|
|
1525
|
+
continue;
|
|
1526
|
+
}
|
|
1527
|
+
const policy = raw;
|
|
1528
|
+
state.policies.set(policy.policyId, policy);
|
|
1529
|
+
}
|
|
1530
|
+
for (const raw of depBatch(ctx, 1) ?? []) {
|
|
1531
|
+
const admissionIssue = capabilityAdmissionIssue(raw);
|
|
1532
|
+
if (admissionIssue !== void 0) {
|
|
1533
|
+
emitCapabilityGuardIssue(
|
|
1534
|
+
ctx,
|
|
1535
|
+
state,
|
|
1536
|
+
`malformed-admission:${recordString(raw, "admissionId") ?? "unknown"}`,
|
|
1537
|
+
admissionIssue
|
|
1538
|
+
);
|
|
1539
|
+
continue;
|
|
1540
|
+
}
|
|
1541
|
+
const admission = raw;
|
|
1542
|
+
const capabilityKey = capabilityBoundaryRefId(admission.capability);
|
|
1543
|
+
const byCapability = state.admissionsByCapability.get(capabilityKey) ?? [];
|
|
1544
|
+
if (!byCapability.some((item) => item.admissionId === admission.admissionId)) {
|
|
1545
|
+
state.admissionsByCapability.set(capabilityKey, [...byCapability, admission]);
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
if (statusIndex >= 0) {
|
|
1549
|
+
for (const raw of depBatch(ctx, statusIndex) ?? []) {
|
|
1550
|
+
const statusIssue = capabilityAdmissionStatusIssue(raw);
|
|
1551
|
+
if (statusIssue !== void 0) {
|
|
1552
|
+
emitCapabilityGuardIssue(
|
|
1553
|
+
ctx,
|
|
1554
|
+
state,
|
|
1555
|
+
`malformed-capability-status:${recordString(raw, "statusId") ?? "unknown"}`,
|
|
1556
|
+
statusIssue
|
|
1557
|
+
);
|
|
1558
|
+
continue;
|
|
1559
|
+
}
|
|
1560
|
+
const status = raw;
|
|
1561
|
+
if (status.state !== "capability-admission-issue") continue;
|
|
1562
|
+
const capabilityId = status.capabilityId;
|
|
1563
|
+
const capabilityKind = status.capabilityKind;
|
|
1564
|
+
if (capabilityId !== void 0 && capabilityKind !== void 0) {
|
|
1565
|
+
const capabilityKey = capabilityBoundaryRefId({
|
|
1566
|
+
id: capabilityId,
|
|
1567
|
+
kind: capabilityKind
|
|
1568
|
+
});
|
|
1569
|
+
const statuses = state.issueStatusesByCapability.get(capabilityKey) ?? [];
|
|
1570
|
+
if (!statuses.some((item) => item.statusId === status.statusId)) {
|
|
1571
|
+
state.issueStatusesByCapability.set(capabilityKey, [...statuses, status]);
|
|
1572
|
+
}
|
|
1573
|
+
} else if (!state.issueStatusesWithoutCapability.some((item) => item.statusId === status.statusId)) {
|
|
1574
|
+
state.issueStatusesWithoutCapability.push(status);
|
|
1575
|
+
}
|
|
1576
|
+
for (const proposal of state.proposals.values())
|
|
1577
|
+
emitCapabilityStatusIssueForProposal(ctx, state, proposal, status);
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
for (const raw of depBatch(ctx, 0) ?? []) {
|
|
1581
|
+
const proposalIssue = capabilityGuardProposalIssue(raw);
|
|
1582
|
+
if (proposalIssue !== void 0) {
|
|
1583
|
+
emitCapabilityGuardIssue(
|
|
1584
|
+
ctx,
|
|
1585
|
+
state,
|
|
1586
|
+
`malformed-proposal:${recordString(raw, "proposalId") ?? "unknown"}`,
|
|
1587
|
+
proposalIssue
|
|
1588
|
+
);
|
|
1589
|
+
continue;
|
|
1590
|
+
}
|
|
1591
|
+
const proposal = raw;
|
|
1592
|
+
state.proposals.set(proposal.proposalId, proposal);
|
|
1593
|
+
}
|
|
1594
|
+
for (const proposal of state.proposals.values())
|
|
1595
|
+
evaluateCapabilityGuardProposal(ctx, state, proposal, now());
|
|
1596
|
+
ctx.state.set(state);
|
|
1597
|
+
},
|
|
1598
|
+
{
|
|
1599
|
+
name: `${name}/runtime`,
|
|
1600
|
+
factory: "workItemDomainActionCapabilityGuardProjector",
|
|
1601
|
+
partial: true,
|
|
1602
|
+
completeWhenDepsComplete: false,
|
|
1603
|
+
errorWhenDepsError: false
|
|
1604
|
+
}
|
|
1605
|
+
);
|
|
1606
|
+
return {
|
|
1607
|
+
decisions: project(
|
|
1608
|
+
graph,
|
|
1609
|
+
runtime,
|
|
1610
|
+
`${name}/decisions`,
|
|
1611
|
+
"workItemDomainActionCapabilityGuardDecisions",
|
|
1612
|
+
(fact) => fact.kind === "decision" ? fact.value : void 0
|
|
1613
|
+
),
|
|
1614
|
+
status: project(
|
|
1615
|
+
graph,
|
|
1616
|
+
runtime,
|
|
1617
|
+
`${name}/status`,
|
|
1618
|
+
"workItemDomainActionCapabilityGuardStatus",
|
|
1619
|
+
(fact) => fact.kind === "status" ? fact.value : void 0
|
|
1620
|
+
),
|
|
1621
|
+
issues: project(
|
|
1622
|
+
graph,
|
|
1623
|
+
runtime,
|
|
1624
|
+
`${name}/issues`,
|
|
1625
|
+
"workItemDomainActionCapabilityGuardIssues",
|
|
1626
|
+
(fact) => fact.kind === "issue" ? fact.value : void 0
|
|
1627
|
+
),
|
|
1628
|
+
audit: project(
|
|
1629
|
+
graph,
|
|
1630
|
+
runtime,
|
|
1631
|
+
`${name}/audit`,
|
|
1632
|
+
"workItemDomainActionCapabilityGuardAudit",
|
|
1633
|
+
(fact) => fact.kind === "audit" ? fact.value : void 0
|
|
1634
|
+
)
|
|
1635
|
+
};
|
|
1636
|
+
}
|
|
1637
|
+
function evaluateCapabilityGuardProposal(ctx, state, proposal, decidedAtMs) {
|
|
1638
|
+
if (state.emittedProposalDecisions.has(proposal.proposalId)) return;
|
|
1639
|
+
const policy = selectCapabilityGuardPolicy(state, proposal);
|
|
1640
|
+
if (typeof policy === "string") {
|
|
1641
|
+
emitCapabilityGuardIssue(
|
|
1642
|
+
ctx,
|
|
1643
|
+
state,
|
|
1644
|
+
`policy:${proposal.proposalId}:${policy}`,
|
|
1645
|
+
dataIssue2(policy, capabilityGuardPolicyMessage(policy, proposal), {
|
|
1646
|
+
subjectId: proposal.workItemId,
|
|
1647
|
+
refs: [ref2("work-item-domain-action-proposal", proposal.proposalId)]
|
|
1648
|
+
})
|
|
1649
|
+
);
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
for (const status of state.issueStatusesWithoutCapability)
|
|
1653
|
+
emitCapabilityStatusIssueForProposal(ctx, state, proposal, status);
|
|
1654
|
+
for (const capabilityRef of policy.capabilityRefs) {
|
|
1655
|
+
for (const status of state.issueStatusesByCapability.get(
|
|
1656
|
+
capabilityBoundaryRefId(capabilityRef)
|
|
1657
|
+
) ?? [])
|
|
1658
|
+
emitCapabilityStatusIssueForProposal(ctx, state, proposal, status);
|
|
1659
|
+
}
|
|
1660
|
+
const admissions = policy.capabilityRefs.map(
|
|
1661
|
+
(capabilityRef) => latestCapabilityAdmission(state, policy, capabilityRef)
|
|
1662
|
+
);
|
|
1663
|
+
const missingCapabilityRefs = policy.capabilityRefs.filter(
|
|
1664
|
+
(_, index) => admissions[index] === void 0
|
|
1665
|
+
);
|
|
1666
|
+
const deferredCapabilityRefs = admissions.filter((admission) => admission !== void 0).filter((admission) => admission.state === "deferred").map((admission) => capabilityBoundaryRefId(admission.capability));
|
|
1667
|
+
if (missingCapabilityRefs.length > 0 || deferredCapabilityRefs.length > 0) {
|
|
1668
|
+
emitCapabilityGuardStatusOnce(
|
|
1669
|
+
ctx,
|
|
1670
|
+
state,
|
|
1671
|
+
`deferred:${proposal.proposalId}:${policy.policyId}:missing=${missingCapabilityRefs.map(capabilityBoundaryRefId).join(",")}:deferred=${deferredCapabilityRefs.join(",")}`,
|
|
1672
|
+
{
|
|
1673
|
+
state: "deferred",
|
|
1674
|
+
workItemId: proposal.workItemId,
|
|
1675
|
+
proposalId: proposal.proposalId,
|
|
1676
|
+
actionKind: proposal.actionKind,
|
|
1677
|
+
policyId: policy.policyId,
|
|
1678
|
+
capabilityRefs: policy.capabilityRefs,
|
|
1679
|
+
admissionSubjectIds: policy.admissionSubjectIds,
|
|
1680
|
+
message: "WorkItem domain action is waiting for capability admission",
|
|
1681
|
+
sourceRefs: capabilityGuardRefs(proposal, policy, admissions),
|
|
1682
|
+
metadata: {
|
|
1683
|
+
missingCapabilityRefs: missingCapabilityRefs.map(capabilityBoundaryRefId),
|
|
1684
|
+
deferredCapabilityRefs
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
);
|
|
1688
|
+
return;
|
|
1689
|
+
}
|
|
1690
|
+
const presentAdmissions = admissions.filter(
|
|
1691
|
+
(admission) => admission !== void 0
|
|
1692
|
+
);
|
|
1693
|
+
const blocked = presentAdmissions.filter((admission) => admission.state === "blocked");
|
|
1694
|
+
const outcome = blocked.length === 0 ? "admit" : "reject";
|
|
1695
|
+
const decision = {
|
|
1696
|
+
kind: "work-item-domain-action-admission-decision",
|
|
1697
|
+
decisionId: `capability-guard:${proposal.proposalId}:decision`,
|
|
1698
|
+
admissionId: `capability-guard:${proposal.proposalId}:admission`,
|
|
1699
|
+
proposalId: proposal.proposalId,
|
|
1700
|
+
outcome,
|
|
1701
|
+
reason: blocked.length === 0 ? "Capability guard admitted required capabilities" : "Capability guard rejected blocked capabilities",
|
|
1702
|
+
decidedAtMs,
|
|
1703
|
+
sourceRefs: capabilityGuardRefs(proposal, policy, presentAdmissions),
|
|
1704
|
+
metadata: {
|
|
1705
|
+
capabilityGuardPolicyId: policy.policyId,
|
|
1706
|
+
capabilityRefs: policy.capabilityRefs.map(capabilityBoundaryRefId),
|
|
1707
|
+
admissionSubjectIds: policy.admissionSubjectIds,
|
|
1708
|
+
blockedCapabilityRefs: blocked.map(
|
|
1709
|
+
(admission) => capabilityBoundaryRefId(admission.capability)
|
|
1710
|
+
)
|
|
1711
|
+
}
|
|
1712
|
+
};
|
|
1713
|
+
state.emittedProposalDecisions.add(proposal.proposalId);
|
|
1714
|
+
emitCapabilityGuard(ctx, "decision", decision);
|
|
1715
|
+
emitCapabilityGuardStatus(ctx, state, {
|
|
1716
|
+
state: outcome === "admit" ? "admitted" : "rejected",
|
|
1717
|
+
workItemId: proposal.workItemId,
|
|
1718
|
+
proposalId: proposal.proposalId,
|
|
1719
|
+
actionKind: proposal.actionKind,
|
|
1720
|
+
policyId: policy.policyId,
|
|
1721
|
+
capabilityRefs: policy.capabilityRefs,
|
|
1722
|
+
admissionSubjectIds: policy.admissionSubjectIds,
|
|
1723
|
+
admissionIds: presentAdmissions.map((admission) => admission.admissionId),
|
|
1724
|
+
sourceRefs: decision.sourceRefs,
|
|
1725
|
+
metadata: { decisionId: decision.decisionId, admissionId: decision.admissionId }
|
|
1726
|
+
});
|
|
1727
|
+
}
|
|
1728
|
+
function capabilityGuardProposalIssue(proposal) {
|
|
1729
|
+
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)) {
|
|
1730
|
+
return dataIssue2(
|
|
1731
|
+
"malformed-work-item-domain-action-capability-guard-proposal",
|
|
1732
|
+
"WorkItem domain action capability guard requires valid proposal DATA facts",
|
|
1733
|
+
{ subjectId: recordString(proposal, "workItemId") }
|
|
1734
|
+
);
|
|
1735
|
+
}
|
|
1736
|
+
return void 0;
|
|
1737
|
+
}
|
|
1738
|
+
function capabilityGuardPolicyIssue(policy) {
|
|
1739
|
+
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))) {
|
|
1740
|
+
return dataIssue2(
|
|
1741
|
+
"malformed-work-item-domain-action-capability-guard-policy",
|
|
1742
|
+
"WorkItem domain action capability guard policy requires policyId, capabilityRefs, and admissionSubjectIds",
|
|
1743
|
+
{
|
|
1744
|
+
subjectId: recordString(policy, "policyId")
|
|
1745
|
+
}
|
|
1746
|
+
);
|
|
1747
|
+
}
|
|
1748
|
+
return void 0;
|
|
1749
|
+
}
|
|
1750
|
+
function capabilityAdmissionIssue(admission) {
|
|
1751
|
+
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)) {
|
|
1752
|
+
return dataIssue2(
|
|
1753
|
+
"malformed-capability-admission",
|
|
1754
|
+
"WorkItem domain action capability guard requires valid CapabilityAdmission DATA facts",
|
|
1755
|
+
{ subjectId: recordString(admission, "subjectId") }
|
|
1756
|
+
);
|
|
1757
|
+
}
|
|
1758
|
+
return void 0;
|
|
1759
|
+
}
|
|
1760
|
+
function capabilityAdmissionStatusIssue(status) {
|
|
1761
|
+
if (!isRecord(status) || status.kind !== "capability-admission-status" || typeof status.statusId !== "string" || status.statusId.length === 0 || !isCapabilityAdmissionStatusState(status.state)) {
|
|
1762
|
+
return dataIssue2(
|
|
1763
|
+
"malformed-capability-admission-status",
|
|
1764
|
+
"WorkItem domain action capability guard requires valid CapabilityAdmissionStatus DATA facts",
|
|
1765
|
+
{ subjectId: recordString(status, "subjectId") }
|
|
1766
|
+
);
|
|
1767
|
+
}
|
|
1768
|
+
const hasCapabilityId = status.capabilityId !== void 0;
|
|
1769
|
+
const hasCapabilityKind = status.capabilityKind !== void 0;
|
|
1770
|
+
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)) {
|
|
1771
|
+
return dataIssue2(
|
|
1772
|
+
"malformed-capability-admission-status",
|
|
1773
|
+
"WorkItem domain action capability guard requires valid CapabilityAdmissionStatus DATA facts",
|
|
1774
|
+
{ subjectId: recordString(status, "subjectId") }
|
|
1775
|
+
);
|
|
1776
|
+
}
|
|
1777
|
+
return void 0;
|
|
1113
1778
|
}
|
|
1114
|
-
function
|
|
1115
|
-
|
|
1116
|
-
for (const [key, value] of Object.entries(payload)) {
|
|
1117
|
-
if (key === "metadata") continue;
|
|
1118
|
-
if (patchFieldKeys.has(key)) patch[key] = value;
|
|
1119
|
-
}
|
|
1120
|
-
return patch;
|
|
1779
|
+
function isCapabilityAdmissionState(value) {
|
|
1780
|
+
return value === "allowed" || value === "blocked" || value === "deferred";
|
|
1121
1781
|
}
|
|
1122
|
-
function
|
|
1123
|
-
|
|
1124
|
-
const patchRecord = payload.patch;
|
|
1125
|
-
const patch = {};
|
|
1126
|
-
for (const [key, value] of Object.entries(patchRecord)) {
|
|
1127
|
-
if (key === "acceptanceCriteria" || key === "verificationPlan" || key === "verificationSteps")
|
|
1128
|
-
continue;
|
|
1129
|
-
patch[key] = value;
|
|
1130
|
-
}
|
|
1131
|
-
const acceptanceCriteria = payload.acceptanceCriteria ?? (Array.isArray(patchRecord.acceptanceCriteria) ? patchRecord.acceptanceCriteria : void 0);
|
|
1132
|
-
const verificationPlan = payload.verificationPlan ?? (isRecord(patchRecord.verificationPlan) ? patchRecord.verificationPlan : Array.isArray(patchRecord.verificationSteps) ? {
|
|
1133
|
-
...workItem.verificationPlan ?? { planId: "default" },
|
|
1134
|
-
steps: patchRecord.verificationSteps
|
|
1135
|
-
} : void 0);
|
|
1136
|
-
return {
|
|
1137
|
-
...payload,
|
|
1138
|
-
patch: Object.keys(patch).length === 0 ? void 0 : patch,
|
|
1139
|
-
acceptanceCriteria,
|
|
1140
|
-
verificationPlan
|
|
1141
|
-
};
|
|
1782
|
+
function isCapabilityAdmissionStatusState(value) {
|
|
1783
|
+
return value === "capability-admission-allowed" || value === "capability-admission-blocked" || value === "capability-admission-deferred" || value === "capability-admission-issue";
|
|
1142
1784
|
}
|
|
1143
|
-
function
|
|
1144
|
-
|
|
1145
|
-
|
|
1785
|
+
function isSourceRefArray(value) {
|
|
1786
|
+
return Array.isArray(value) && value.every(
|
|
1787
|
+
(item) => isRecord(item) && typeof item.kind === "string" && typeof item.id === "string"
|
|
1788
|
+
);
|
|
1146
1789
|
}
|
|
1147
|
-
function
|
|
1148
|
-
|
|
1149
|
-
const allowed = policy.patch?.allowedFields;
|
|
1150
|
-
return allowed === void 0 || allowed.includes("acceptanceCriteria") || policy.patch?.allowAcceptanceCriteria === true;
|
|
1790
|
+
function isDataIssueArray(value) {
|
|
1791
|
+
return Array.isArray(value) && value.every(isDataIssue);
|
|
1151
1792
|
}
|
|
1152
|
-
function
|
|
1153
|
-
|
|
1154
|
-
const allowed = policy.patch?.allowedFields;
|
|
1155
|
-
return allowed === void 0 || allowed.includes("verificationPlan") || allowed.includes("verificationSteps") || policy.patch?.allowVerificationPlan === true;
|
|
1793
|
+
function isDataIssue(value) {
|
|
1794
|
+
return isRecord(value) && value.kind === "issue" && typeof value.code === "string" && value.code.length > 0 && typeof value.message === "string" && value.message.length > 0;
|
|
1156
1795
|
}
|
|
1157
|
-
function
|
|
1158
|
-
|
|
1796
|
+
function isBoundaryCapabilityRef(value) {
|
|
1797
|
+
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"));
|
|
1798
|
+
}
|
|
1799
|
+
function isBoundaryCapabilityKind(value) {
|
|
1800
|
+
return value === "auth" || value === "permission" || value === "config" || value === "resource";
|
|
1801
|
+
}
|
|
1802
|
+
function selectCapabilityGuardPolicy(state, proposal) {
|
|
1803
|
+
const explicitId = stringMetadata(proposal.metadata, "capabilityGuardPolicyId") ?? proposal.sourceRefs?.find(
|
|
1804
|
+
(sourceRef) => sourceRef.kind === "work-item-domain-action-capability-guard-policy"
|
|
1805
|
+
)?.id;
|
|
1159
1806
|
if (explicitId !== void 0)
|
|
1160
|
-
return state.policies.get(explicitId) ?? "unknown-work-item-domain-action-
|
|
1161
|
-
const matching =
|
|
1162
|
-
(policy) =>
|
|
1807
|
+
return state.policies.get(explicitId) ?? "unknown-work-item-domain-action-capability-guard-policy";
|
|
1808
|
+
const matching = [...state.policies.values()].filter(
|
|
1809
|
+
(policy) => policy.actionKinds === void 0 || policy.actionKinds.includes(proposal.actionKind)
|
|
1163
1810
|
);
|
|
1164
1811
|
if (matching.length === 0) return "missing-policy";
|
|
1165
1812
|
if (matching.length > 1) return "policy-mismatch";
|
|
1166
1813
|
return matching[0];
|
|
1167
1814
|
}
|
|
1168
|
-
function
|
|
1169
|
-
const
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
(
|
|
1173
|
-
|
|
1174
|
-
}
|
|
1175
|
-
function policyAllowsAction(policy, actionKind) {
|
|
1176
|
-
return policy.actionKinds === void 0 || policy.actionKinds.includes(actionKind);
|
|
1177
|
-
}
|
|
1178
|
-
function admissionProposalMismatch(admission, proposal) {
|
|
1179
|
-
if (admission.workItemId !== proposal.workItemId)
|
|
1180
|
-
return {
|
|
1181
|
-
field: "workItemId",
|
|
1182
|
-
admissionValue: admission.workItemId,
|
|
1183
|
-
proposalValue: proposal.workItemId,
|
|
1184
|
-
message: `Admission '${admission.admissionId}' workItemId does not match proposal '${proposal.proposalId}'`
|
|
1185
|
-
};
|
|
1186
|
-
if (admission.actionKind !== proposal.actionKind)
|
|
1187
|
-
return {
|
|
1188
|
-
field: "actionKind",
|
|
1189
|
-
admissionValue: admission.actionKind,
|
|
1190
|
-
proposalValue: proposal.actionKind,
|
|
1191
|
-
message: `Admission '${admission.admissionId}' actionKind does not match proposal '${proposal.proposalId}'`
|
|
1192
|
-
};
|
|
1815
|
+
function latestCapabilityAdmission(state, policy, capabilityRef) {
|
|
1816
|
+
const admissions = state.admissionsByCapability.get(capabilityBoundaryRefId(capabilityRef)) ?? [];
|
|
1817
|
+
for (let index = admissions.length - 1; index >= 0; index -= 1) {
|
|
1818
|
+
const admission = admissions[index];
|
|
1819
|
+
if (admission !== void 0 && policy.admissionSubjectIds.includes(admission.subjectId))
|
|
1820
|
+
return admission;
|
|
1821
|
+
}
|
|
1193
1822
|
return void 0;
|
|
1194
1823
|
}
|
|
1195
|
-
function
|
|
1196
|
-
const
|
|
1197
|
-
if (
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1824
|
+
function emitCapabilityStatusIssueForProposal(ctx, state, proposal, status) {
|
|
1825
|
+
const policy = selectCapabilityGuardPolicy(state, proposal);
|
|
1826
|
+
if (typeof policy === "string") return;
|
|
1827
|
+
if (status.subjectId !== void 0 && !policy.admissionSubjectIds.includes(status.subjectId))
|
|
1828
|
+
return;
|
|
1829
|
+
const capabilityId = status.capabilityId;
|
|
1830
|
+
const capabilityKind = status.capabilityKind;
|
|
1831
|
+
const statusCapabilityKey = capabilityId === void 0 || capabilityKind === void 0 ? void 0 : capabilityBoundaryRefId({ id: capabilityId, kind: capabilityKind });
|
|
1832
|
+
if (statusCapabilityKey !== void 0 && !policy.capabilityRefs.some(
|
|
1833
|
+
(capabilityRef) => capabilityBoundaryRefId(capabilityRef) === statusCapabilityKey
|
|
1834
|
+
))
|
|
1835
|
+
return;
|
|
1836
|
+
const issue = status.issues?.[0] ?? dataIssue2(
|
|
1837
|
+
"capability-admission-status-issue",
|
|
1838
|
+
"Capability admission status reported an issue",
|
|
1839
|
+
{ subjectId: proposal.workItemId }
|
|
1840
|
+
);
|
|
1841
|
+
emitCapabilityGuardIssue(
|
|
1842
|
+
ctx,
|
|
1843
|
+
state,
|
|
1844
|
+
`capability-status:${proposal.proposalId}:${status.statusId}`,
|
|
1845
|
+
dataIssue2(issue.code, issue.message, {
|
|
1846
|
+
subjectId: proposal.workItemId,
|
|
1847
|
+
refs: uniqueSourceRefs2([
|
|
1848
|
+
...capabilityGuardRefs(proposal, policy),
|
|
1849
|
+
...status.sourceRefs ?? []
|
|
1850
|
+
]),
|
|
1851
|
+
metadata: {
|
|
1852
|
+
...statusCapabilityKey === void 0 ? {} : { capabilityRef: statusCapabilityKey },
|
|
1853
|
+
capabilityAdmissionStatusId: status.statusId
|
|
1854
|
+
}
|
|
1855
|
+
})
|
|
1208
1856
|
);
|
|
1209
|
-
if (executionInputRevision !== void 0)
|
|
1210
|
-
return {
|
|
1211
|
-
field: "executionInputRevision",
|
|
1212
|
-
code: "stale-execution-input",
|
|
1213
|
-
...executionInputRevision,
|
|
1214
|
-
message: `Admission '${admission.admissionId}' carries conflicting executionInputRevision metadata`
|
|
1215
|
-
};
|
|
1216
|
-
const applyPolicyId = conflictingMetadataString(proposal, admission, "applyPolicyId");
|
|
1217
|
-
if (applyPolicyId !== void 0)
|
|
1218
|
-
return {
|
|
1219
|
-
field: "applyPolicyId",
|
|
1220
|
-
code: "policy-mismatch",
|
|
1221
|
-
...applyPolicyId,
|
|
1222
|
-
message: `Admission '${admission.admissionId}' carries conflicting applyPolicyId metadata`
|
|
1223
|
-
};
|
|
1224
|
-
return void 0;
|
|
1225
|
-
}
|
|
1226
|
-
function conflictingMetadataNumber(proposal, admission, key) {
|
|
1227
|
-
const proposalValue = numberMetadata(proposal.metadata, key);
|
|
1228
|
-
const admissionValue = numberMetadata(admission.metadata, key);
|
|
1229
|
-
if (proposalValue === void 0 || admissionValue === void 0 || proposalValue === admissionValue)
|
|
1230
|
-
return void 0;
|
|
1231
|
-
return { proposalValue, admissionValue };
|
|
1232
|
-
}
|
|
1233
|
-
function conflictingMetadataString(proposal, admission, key) {
|
|
1234
|
-
const proposalValue = stringMetadata(proposal.metadata, key);
|
|
1235
|
-
const admissionValue = stringMetadata(admission.metadata, key);
|
|
1236
|
-
if (proposalValue === void 0 || admissionValue === void 0 || proposalValue === admissionValue)
|
|
1237
|
-
return void 0;
|
|
1238
|
-
return { proposalValue, admissionValue };
|
|
1239
|
-
}
|
|
1240
|
-
function staleRevision(proposal, admission, workItem) {
|
|
1241
|
-
const authoringRevision = numberMetadata(admission.metadata, "authoringRevision") ?? numberMetadata(proposal.metadata, "authoringRevision");
|
|
1242
|
-
if (authoringRevision !== void 0 && authoringRevision !== workItem.authoringRevision)
|
|
1243
|
-
return {
|
|
1244
|
-
kind: "stale-revision",
|
|
1245
|
-
expected: authoringRevision,
|
|
1246
|
-
current: workItem.authoringRevision
|
|
1247
|
-
};
|
|
1248
|
-
const executionInputRevision = numberMetadata(admission.metadata, "executionInputRevision") ?? numberMetadata(proposal.metadata, "executionInputRevision");
|
|
1249
|
-
if (executionInputRevision !== void 0 && executionInputRevision !== workItem.executionInputRevision)
|
|
1250
|
-
return {
|
|
1251
|
-
kind: "stale-execution-input",
|
|
1252
|
-
expected: executionInputRevision,
|
|
1253
|
-
current: workItem.executionInputRevision
|
|
1254
|
-
};
|
|
1255
|
-
return void 0;
|
|
1256
1857
|
}
|
|
1257
|
-
function
|
|
1858
|
+
function capabilityGuardPolicyMessage(code, proposal) {
|
|
1258
1859
|
if (code === "missing-policy")
|
|
1259
|
-
return `
|
|
1260
|
-
if (code === "unknown-work-item-domain-action-
|
|
1261
|
-
return `
|
|
1262
|
-
return `
|
|
1263
|
-
}
|
|
1264
|
-
function emitApplication(ctx, state, admission, proposal, applicationState, facts, opts = {}) {
|
|
1265
|
-
const producedFactIds = facts.map((fact) => fact.eventId);
|
|
1266
|
-
const application = {
|
|
1267
|
-
kind: "work-item-domain-action-application",
|
|
1268
|
-
applicationId: `${admission.admissionId}:application`,
|
|
1269
|
-
admissionId: admission.admissionId,
|
|
1270
|
-
proposalId: proposal.proposalId,
|
|
1271
|
-
workItemId: proposal.workItemId,
|
|
1272
|
-
actionKind: proposal.actionKind,
|
|
1273
|
-
state: applicationState,
|
|
1274
|
-
producedFactIds,
|
|
1275
|
-
reason: admission.reason ?? proposal.reason,
|
|
1276
|
-
sourceRefs: admissionRefs(admission, proposal),
|
|
1277
|
-
metadata: { policyId: opts.policyId, code: opts.code }
|
|
1278
|
-
};
|
|
1279
|
-
emitApplicationFact(ctx, "application", application);
|
|
1280
|
-
emitApplicationStatus(ctx, state, {
|
|
1281
|
-
state: applicationState,
|
|
1282
|
-
code: opts.code,
|
|
1283
|
-
message: opts.message,
|
|
1284
|
-
workItemId: proposal.workItemId,
|
|
1285
|
-
proposalId: proposal.proposalId,
|
|
1286
|
-
admissionId: admission.admissionId,
|
|
1287
|
-
actionKind: proposal.actionKind,
|
|
1288
|
-
sourceRefs: application.sourceRefs,
|
|
1289
|
-
metadata: { producedFactIds, policyId: opts.policyId }
|
|
1290
|
-
});
|
|
1291
|
-
}
|
|
1292
|
-
function emitApplicationIssue(ctx, state, key, code, message, workItemId, sourceRefs2, metadata) {
|
|
1293
|
-
if (state.issueKeys.has(key)) return;
|
|
1294
|
-
state.issueKeys.add(key);
|
|
1295
|
-
const item = dataIssue(code, message, { subjectId: workItemId, refs: sourceRefs2, metadata });
|
|
1296
|
-
emitApplicationFact(ctx, "issue", item);
|
|
1297
|
-
emitApplicationStatus(ctx, state, {
|
|
1298
|
-
state: code === "missing-policy" ? "missing-policy" : "rejected",
|
|
1299
|
-
code,
|
|
1300
|
-
message,
|
|
1301
|
-
workItemId,
|
|
1302
|
-
sourceRefs: sourceRefs2,
|
|
1303
|
-
metadata
|
|
1304
|
-
});
|
|
1860
|
+
return `WorkItem domain action '${proposal.proposalId}' has no capability guard policy`;
|
|
1861
|
+
if (code === "unknown-work-item-domain-action-capability-guard-policy")
|
|
1862
|
+
return `WorkItem domain action '${proposal.proposalId}' references a missing capability guard policy`;
|
|
1863
|
+
return `WorkItem domain action '${proposal.proposalId}' matches multiple capability guard policies`;
|
|
1305
1864
|
}
|
|
1306
|
-
function
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
...
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1865
|
+
function capabilityGuardRefs(proposal, policy, admissions = []) {
|
|
1866
|
+
return uniqueSourceRefs2([
|
|
1867
|
+
ref2("work-item", proposal.workItemId),
|
|
1868
|
+
ref2("work-item-domain-action-proposal", proposal.proposalId),
|
|
1869
|
+
ref2("work-item-domain-action-capability-guard-policy", policy.policyId),
|
|
1870
|
+
...admissions.filter((admission) => admission !== void 0).flatMap((admission) => [
|
|
1871
|
+
ref2("capability-admission", admission.admissionId),
|
|
1872
|
+
ref2("capability-admission-proposal", admission.proposalId),
|
|
1873
|
+
ref2("boundary-capability", capabilityBoundaryRefId(admission.capability)),
|
|
1874
|
+
...admission.sourceRefs ?? []
|
|
1875
|
+
]),
|
|
1876
|
+
...proposal.sourceRefs ?? []
|
|
1877
|
+
]);
|
|
1315
1878
|
}
|
|
1316
|
-
function
|
|
1879
|
+
function capabilityBoundaryRefId(capability) {
|
|
1880
|
+
return `${capability.kind}:${capability.id}`;
|
|
1881
|
+
}
|
|
1882
|
+
function emitCapabilityGuardStatus(ctx, state, status) {
|
|
1317
1883
|
state.statusSeq += 1;
|
|
1318
1884
|
const statusFact = {
|
|
1319
|
-
kind: "work-item-domain-action-status",
|
|
1320
|
-
statusId: `work-item-domain-action-
|
|
1885
|
+
kind: "work-item-domain-action-capability-guard-status",
|
|
1886
|
+
statusId: `work-item-domain-action-capability-guard-status:${state.statusSeq}`,
|
|
1321
1887
|
...status
|
|
1322
1888
|
};
|
|
1323
|
-
|
|
1324
|
-
emitApplicationAudit(ctx, state, "work-item-domain-action-application-status", statusFact);
|
|
1325
|
-
}
|
|
1326
|
-
function emitIntakeAudit(ctx, state, kind, status) {
|
|
1327
|
-
state.auditSeq += 1;
|
|
1328
|
-
emitIntake(ctx, "audit", auditRecord(kind, state.auditSeq, status));
|
|
1329
|
-
}
|
|
1330
|
-
function emitApplicationAudit(ctx, state, kind, status) {
|
|
1889
|
+
emitCapabilityGuard(ctx, "status", statusFact);
|
|
1331
1890
|
state.auditSeq += 1;
|
|
1332
|
-
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
return {
|
|
1336
|
-
id: `${kind}:${seq}`,
|
|
1337
|
-
kind,
|
|
1891
|
+
emitCapabilityGuard(ctx, "audit", {
|
|
1892
|
+
id: `work-item-domain-action-capability-guard-status:${state.auditSeq}`,
|
|
1893
|
+
kind: "work-item-domain-action-capability-guard-status",
|
|
1338
1894
|
subjectId: status.workItemId,
|
|
1339
1895
|
message: status.message,
|
|
1340
|
-
issueCode: status.code,
|
|
1341
1896
|
sourceRefs: status.sourceRefs,
|
|
1342
1897
|
metadata: {
|
|
1343
|
-
statusId:
|
|
1898
|
+
statusId: statusFact.statusId,
|
|
1344
1899
|
state: status.state,
|
|
1345
1900
|
proposalId: status.proposalId,
|
|
1346
|
-
|
|
1347
|
-
actionKind: status.actionKind,
|
|
1901
|
+
policyId: status.policyId,
|
|
1348
1902
|
...status.metadata ?? {}
|
|
1349
1903
|
}
|
|
1350
|
-
};
|
|
1904
|
+
});
|
|
1351
1905
|
}
|
|
1352
|
-
function
|
|
1353
|
-
|
|
1906
|
+
function emitCapabilityGuardStatusOnce(ctx, state, key, status) {
|
|
1907
|
+
if (state.emittedStatusKeys.has(key)) return;
|
|
1908
|
+
state.emittedStatusKeys.add(key);
|
|
1909
|
+
emitCapabilityGuardStatus(ctx, state, status);
|
|
1354
1910
|
}
|
|
1355
|
-
function
|
|
1911
|
+
function emitCapabilityGuardIssue(ctx, state, key, issue) {
|
|
1912
|
+
if (state.issueKeys.has(key)) return;
|
|
1913
|
+
state.issueKeys.add(key);
|
|
1914
|
+
emitCapabilityGuard(ctx, "issue", issue);
|
|
1915
|
+
emitCapabilityGuardStatus(ctx, state, {
|
|
1916
|
+
state: "issue",
|
|
1917
|
+
workItemId: issue.subjectId,
|
|
1918
|
+
issues: [issue],
|
|
1919
|
+
message: issue.message,
|
|
1920
|
+
metadata: { issueCode: issue.code }
|
|
1921
|
+
});
|
|
1922
|
+
}
|
|
1923
|
+
function emitCapabilityGuard(ctx, kind, value) {
|
|
1356
1924
|
ctx.down([["DATA", { kind, value }]]);
|
|
1357
1925
|
}
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1926
|
+
|
|
1927
|
+
// src/solutions/work-item/actions-intake.ts
|
|
1928
|
+
function workItemDomainActionProposalIntakeProjector(graph, opts) {
|
|
1929
|
+
const name = opts.name ?? "workItemDomainActionIntake";
|
|
1930
|
+
const now = opts.now ?? Date.now;
|
|
1931
|
+
const runtime = graph.node(
|
|
1932
|
+
[opts.proposals],
|
|
1361
1933
|
(ctx) => {
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1934
|
+
const state = ctx.state.get() ?? {
|
|
1935
|
+
proposals: /* @__PURE__ */ new Set(),
|
|
1936
|
+
statusSeq: 0,
|
|
1937
|
+
auditSeq: 0
|
|
1938
|
+
};
|
|
1939
|
+
for (const raw of depBatch(ctx, 0) ?? []) reduceProposalIntake(ctx, state, raw, now());
|
|
1940
|
+
ctx.state.set(state);
|
|
1366
1941
|
},
|
|
1367
|
-
{
|
|
1942
|
+
{
|
|
1943
|
+
name: `${name}/runtime`,
|
|
1944
|
+
factory: "workItemDomainActionProposalIntakeProjector",
|
|
1945
|
+
partial: true,
|
|
1946
|
+
completeWhenDepsComplete: false,
|
|
1947
|
+
errorWhenDepsError: false
|
|
1948
|
+
}
|
|
1368
1949
|
);
|
|
1369
|
-
}
|
|
1370
|
-
function firstDuplicateFactId(state, facts) {
|
|
1371
|
-
const pending = /* @__PURE__ */ new Set();
|
|
1372
|
-
for (const fact of facts) {
|
|
1373
|
-
if (state.emittedFactIds.has(fact.eventId) || pending.has(fact.eventId)) return fact.eventId;
|
|
1374
|
-
pending.add(fact.eventId);
|
|
1375
|
-
}
|
|
1376
|
-
return void 0;
|
|
1377
|
-
}
|
|
1378
|
-
function claimFactId(state, factId) {
|
|
1379
|
-
if (state.emittedFactIds.has(factId)) return false;
|
|
1380
|
-
state.emittedFactIds.add(factId);
|
|
1381
|
-
return true;
|
|
1382
|
-
}
|
|
1383
|
-
function admissionRefs(admission, proposal, policy) {
|
|
1384
|
-
return uniqueSourceRefs([
|
|
1385
|
-
ref("work-item-domain-action-admission", admission.admissionId),
|
|
1386
|
-
ref("work-item-domain-action-proposal", admission.proposalId),
|
|
1387
|
-
...proposal === void 0 ? [] : [
|
|
1388
|
-
ref("work-item", proposal.workItemId),
|
|
1389
|
-
ref("work-item-evidence", proposal.evidenceId),
|
|
1390
|
-
...proposal.sourceRefs ?? []
|
|
1391
|
-
],
|
|
1392
|
-
...policy === void 0 ? [] : [ref("work-item-domain-action-apply-policy", policy.policyId)],
|
|
1393
|
-
...admission.sourceRefs ?? []
|
|
1394
|
-
]);
|
|
1395
|
-
}
|
|
1396
|
-
function actionSourceRefs(admission, proposal, policy) {
|
|
1397
|
-
return admissionRefs(admission, proposal, policy);
|
|
1398
|
-
}
|
|
1399
|
-
function dataIssue(code, message, opts = {}) {
|
|
1400
1950
|
return {
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1951
|
+
proposals: project(
|
|
1952
|
+
graph,
|
|
1953
|
+
runtime,
|
|
1954
|
+
`${name}/proposals`,
|
|
1955
|
+
"workItemDomainActionProposals",
|
|
1956
|
+
(fact) => fact.kind === "proposal" ? fact.value : void 0
|
|
1957
|
+
),
|
|
1958
|
+
status: project(
|
|
1959
|
+
graph,
|
|
1960
|
+
runtime,
|
|
1961
|
+
`${name}/status`,
|
|
1962
|
+
"workItemDomainActionStatus",
|
|
1963
|
+
(fact) => fact.kind === "status" ? fact.value : void 0
|
|
1964
|
+
),
|
|
1965
|
+
issues: project(
|
|
1966
|
+
graph,
|
|
1967
|
+
runtime,
|
|
1968
|
+
`${name}/issues`,
|
|
1969
|
+
"workItemDomainActionIssues",
|
|
1970
|
+
(fact) => fact.kind === "issue" ? fact.value : void 0
|
|
1971
|
+
),
|
|
1972
|
+
audit: project(
|
|
1973
|
+
graph,
|
|
1974
|
+
runtime,
|
|
1975
|
+
`${name}/audit`,
|
|
1976
|
+
"workItemDomainActionAudit",
|
|
1977
|
+
(fact) => fact.kind === "audit" ? fact.value : void 0
|
|
1978
|
+
)
|
|
1409
1979
|
};
|
|
1410
1980
|
}
|
|
1411
|
-
function
|
|
1412
|
-
const
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1981
|
+
function reduceProposalIntake(ctx, state, input, proposedAtMs) {
|
|
1982
|
+
const proposal = normalizeProposal(input, proposedAtMs);
|
|
1983
|
+
if (typeof proposal === "string") {
|
|
1984
|
+
const issue = dataIssue2("malformed-domain-action-proposal", proposal);
|
|
1985
|
+
emitIntake(ctx, "issue", issue);
|
|
1986
|
+
emitIntakeStatus(ctx, state, {
|
|
1987
|
+
state: "rejected",
|
|
1988
|
+
code: issue.code,
|
|
1989
|
+
message: issue.message
|
|
1990
|
+
});
|
|
1991
|
+
return;
|
|
1419
1992
|
}
|
|
1420
|
-
|
|
1993
|
+
if (state.proposals.has(proposal.proposalId)) {
|
|
1994
|
+
emitIntakeStatus(ctx, state, {
|
|
1995
|
+
state: "duplicate",
|
|
1996
|
+
code: "duplicate-suppressed",
|
|
1997
|
+
workItemId: proposal.workItemId,
|
|
1998
|
+
proposalId: proposal.proposalId,
|
|
1999
|
+
actionKind: proposal.actionKind,
|
|
2000
|
+
message: `Duplicate WorkItem domain action proposal '${proposal.proposalId}' suppressed`
|
|
2001
|
+
});
|
|
2002
|
+
return;
|
|
2003
|
+
}
|
|
2004
|
+
state.proposals.add(proposal.proposalId);
|
|
2005
|
+
emitIntake(ctx, "proposal", proposal);
|
|
2006
|
+
emitIntakeStatus(ctx, state, {
|
|
2007
|
+
state: "proposed",
|
|
2008
|
+
workItemId: proposal.workItemId,
|
|
2009
|
+
proposalId: proposal.proposalId,
|
|
2010
|
+
actionKind: proposal.actionKind,
|
|
2011
|
+
sourceRefs: proposal.sourceRefs
|
|
2012
|
+
});
|
|
1421
2013
|
}
|
|
1422
|
-
function
|
|
1423
|
-
if (!
|
|
1424
|
-
|
|
1425
|
-
|
|
2014
|
+
function normalizeProposal(input, proposedAtMs) {
|
|
2015
|
+
if (!isRecord(input)) return "WorkItemDomainActionProposal intake must be an object";
|
|
2016
|
+
if (input.kind !== "work-item-domain-action-proposal" && input.kind !== "work-item-domain-action-proposal-intake")
|
|
2017
|
+
return "WorkItemDomainActionProposal intake has unsupported kind";
|
|
2018
|
+
if (typeof input.proposalId !== "string" || input.proposalId.trim() === "")
|
|
2019
|
+
return "WorkItemDomainActionProposal.proposalId is required";
|
|
2020
|
+
if (typeof input.workItemId !== "string" || input.workItemId.trim() === "")
|
|
2021
|
+
return "WorkItemDomainActionProposal.workItemId is required";
|
|
2022
|
+
if (typeof input.actionKind !== "string" || input.actionKind.trim() === "")
|
|
2023
|
+
return "WorkItemDomainActionProposal.actionKind is required";
|
|
2024
|
+
if (input.kind === "work-item-domain-action-proposal") {
|
|
2025
|
+
if (typeof input.effectRunId !== "string")
|
|
2026
|
+
return "WorkItemDomainActionProposal.effectRunId is required";
|
|
2027
|
+
if (typeof input.effectRunResultId !== "string")
|
|
2028
|
+
return "WorkItemDomainActionProposal.effectRunResultId is required";
|
|
2029
|
+
if (typeof input.evidenceId !== "string")
|
|
2030
|
+
return "WorkItemDomainActionProposal.evidenceId is required";
|
|
2031
|
+
if (typeof input.policyId !== "string")
|
|
2032
|
+
return "WorkItemDomainActionProposal.policyId is required";
|
|
2033
|
+
return input;
|
|
2034
|
+
}
|
|
2035
|
+
return workItemDomainActionProposal(
|
|
2036
|
+
input.proposalId,
|
|
2037
|
+
input.workItemId,
|
|
2038
|
+
input.actionKind,
|
|
2039
|
+
{
|
|
2040
|
+
payload: input.payload,
|
|
2041
|
+
reason: typeof input.reason === "string" ? input.reason : void 0,
|
|
2042
|
+
policyId: typeof input.policyId === "string" ? input.policyId : void 0,
|
|
2043
|
+
effectRunId: typeof input.effectRunId === "string" ? input.effectRunId : void 0,
|
|
2044
|
+
effectRunResultId: typeof input.effectRunResultId === "string" ? input.effectRunResultId : void 0,
|
|
2045
|
+
evidenceId: typeof input.evidenceId === "string" ? input.evidenceId : void 0,
|
|
2046
|
+
proposedAtMs: typeof input.proposedAtMs === "number" ? input.proposedAtMs : proposedAtMs,
|
|
2047
|
+
sourceRefs: sourceRefs(input.sourceRefs),
|
|
2048
|
+
metadata: isRecord(input.metadata) ? input.metadata : void 0
|
|
2049
|
+
}
|
|
1426
2050
|
);
|
|
1427
2051
|
}
|
|
1428
|
-
function
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
return { kind, id };
|
|
2052
|
+
function emitIntakeStatus(ctx, state, status) {
|
|
2053
|
+
state.statusSeq += 1;
|
|
2054
|
+
const statusFact = {
|
|
2055
|
+
kind: "work-item-domain-action-status",
|
|
2056
|
+
statusId: `work-item-domain-action-intake-status:${state.statusSeq}`,
|
|
2057
|
+
...status
|
|
2058
|
+
};
|
|
2059
|
+
emitIntake(ctx, "status", statusFact);
|
|
2060
|
+
emitIntakeAudit(ctx, state, "work-item-domain-action-intake-status", statusFact);
|
|
1438
2061
|
}
|
|
1439
|
-
function
|
|
1440
|
-
|
|
2062
|
+
function emitIntakeAudit(ctx, state, kind, status) {
|
|
2063
|
+
state.auditSeq += 1;
|
|
2064
|
+
emitIntake(ctx, "audit", auditRecord(kind, state.auditSeq, status));
|
|
1441
2065
|
}
|
|
1442
|
-
function
|
|
1443
|
-
|
|
2066
|
+
function emitIntake(ctx, kind, value) {
|
|
2067
|
+
ctx.down([["DATA", { kind, value }]]);
|
|
1444
2068
|
}
|
|
1445
2069
|
export {
|
|
1446
2070
|
workItemDomainActionAdmissionProjector,
|