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