@uipath/case-tool 1.205.0-preview.281 → 1.206.0-preview.282

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/dist/THIRD-PARTY-NOTICES.md +68 -7
  2. package/dist/applicationinsights-web-8m26566f.js +17209 -0
  3. package/dist/{authoring-kqh34zzm.js → authoring-vg90pqrn.js} +488 -213
  4. package/dist/bindings-v2-1da35xsc.js +27 -0
  5. package/dist/{browser-strategy-6atc7v8s.js → browser-strategy-627rrg7z.js} +6 -7
  6. package/dist/{business-rule-export-ey588rv7.js → business-rule-export-jv60r9jf.js} +3 -7
  7. package/dist/carry-forward-sfdnta6q.js +52 -0
  8. package/dist/{case-api-sme7esce.js → case-api-by95rf40.js} +38 -37
  9. package/dist/{case-bindings-service-cht20gqt.js → case-bindings-service-pp3c9g1e.js} +93 -45
  10. package/dist/{case-debug-solution-projects-qqd3h10v.js → case-debug-solution-projects-je7h4hmq.js} +3 -7
  11. package/dist/{case-debug-studio-web-service-nqzmwbr5.js → case-debug-studio-web-service-d83h3atd.js} +48 -45
  12. package/dist/{case-described-io-service-dx1f4nxe.js → case-described-io-service-tsm8r189.js} +33 -15
  13. package/dist/case-event-trigger-bindings-qbs8ywf5.js +17 -0
  14. package/dist/{case-format-service-6aew6tqc.js → case-format-service-3sab2297.js} +9 -10
  15. package/dist/{case-pack-service-pqq8wnfd.js → case-pack-service-txnv711n.js} +47 -42
  16. package/dist/case-schema-version-cw0h2nrv.js +18 -0
  17. package/dist/case-shape-reader-xaxmrd40.js +14 -0
  18. package/dist/{case-splice-service-s5gccctq.js → case-splice-service-6y35w83e.js} +139 -51
  19. package/dist/{case-validate-service-vq38va33.js → case-validate-service-hz0j90we.js} +234 -170
  20. package/dist/connector-enrichment-service-sjjmb9dy.js +56 -0
  21. package/dist/connector-library-1yw5r47w.js +99 -0
  22. package/dist/connector-lookup-service-yerhg9cd.js +20 -0
  23. package/dist/connector-task-shape-bgywpbqc.js +13 -0
  24. package/dist/{de-WLW6KWOJ-2qnsqdtw.js → de-2BEKRUMY-x4dx0nn9.js} +190 -10
  25. package/dist/{de-UZI7FJDO-mwvc4cxe.js → de-AI7ML6JM-km47f8k4.js} +20 -5
  26. package/dist/debug-service-h4jxbn1r.js +56 -0
  27. package/dist/{embedded-file-reader-r66242ey.js → embedded-file-reader-0228j82a.js} +1 -4
  28. package/dist/en-LAVDF7IQ-yzy20n6d.js +254 -0
  29. package/dist/en-X6T4SP2Y-xm47mf18.js +2752 -0
  30. package/dist/{enrichment-service-hv7ff88p.js → enrichment-service-wpprec7k.js} +36 -37
  31. package/dist/{es-5THCNRFN-tqwyrdgy.js → es-7ZTQTGKN-11sj0yjx.js} +190 -10
  32. package/dist/{es-MX-5RELIXKI-9jhqtddc.js → es-MX-7KJZPEQN-1fq0a9jr.js} +20 -5
  33. package/dist/{es-MX-TWZBRAXK-gqd9swzw.js → es-MX-IHGRIEWH-g63p0gb4.js} +191 -11
  34. package/dist/{es-4UBUTCQA-m1ytpyx2.js → es-SPNKTD5Y-vw5cbgv5.js} +20 -5
  35. package/dist/filter-engine-dh9s90gb.js +10 -0
  36. package/dist/first-party-service-ahcc6xc6.js +8 -0
  37. package/dist/{fr-CERONSRC-edpxhbmb.js → fr-7ZDL4MVO-pd27vwh6.js} +191 -11
  38. package/dist/{fr-VBM5WRC2-63z7w4mh.js → fr-AXJGUT53-hdg3pmd4.js} +20 -5
  39. package/dist/index-21kns9qg.js +10 -0
  40. package/dist/{index-fyc0k5dd.js → index-5pzp2qeb.js} +5350 -3271
  41. package/dist/{connector-task-spec-pzfzmfr5.js → index-9f8naxbk.js} +320 -144
  42. package/dist/{index-5dxh9z60.js → index-a7y7etmy.js} +6 -5
  43. package/dist/{index-ekwtgx7y.js → index-ec115v05.js} +309 -287
  44. package/dist/index-j9t2k52h.js +35 -0
  45. package/dist/index-myf7b4a5.js +26 -0
  46. package/dist/index-nj3axvya.js +159 -0
  47. package/dist/{index-h8cph72k.js → index-nzr4mx5p.js} +2 -22
  48. package/dist/index-pg9wmyrf.js +36 -0
  49. package/dist/index-z2qk2qn6.js +24 -0
  50. package/dist/index.js +19 -25
  51. package/dist/index.umd.min-ttg1qc0x.js +7814 -0
  52. package/dist/{init-entry-points-hhpv3e3f.js → init-entry-points-pc128f7z.js} +22 -35
  53. package/dist/input-reader-9ym8mkbc.js +10 -0
  54. package/dist/{ja-43QL7CGO-c3q8gre9.js → ja-C7GZV6YP-hvtbngr5.js} +20 -5
  55. package/dist/{ja-U2MCNHGT-gz0ap1jk.js → ja-LGAM5HKR-sgrtjqjh.js} +190 -10
  56. package/dist/{jmespath-e4a59c8w.js → jmespath-wwym30dp.js} +1 -1
  57. package/dist/{js-yaml-87cdx6bm.js → js-yaml-a2bk27p6.js} +1 -3
  58. package/dist/known-types-gak9by1w.js +11 -0
  59. package/dist/{ko-XAUYQWDR-zp81gjp4.js → ko-7B4SUZB7-c8wr20sc.js} +190 -10
  60. package/dist/{ko-TLL6Z6CD-wrt3n8a9.js → ko-Q24HSSL4-5vn55pte.js} +20 -5
  61. package/dist/localSolutionDiscovery-48d1bnkj.js +15 -0
  62. package/dist/{migration-diagnosis-ns5cw4e3.js → migration-diagnosis-myqqgwj0.js} +1 -4
  63. package/dist/{node-strategy-v442hwgd.js → node-strategy-2mqzg36t.js} +15 -18
  64. package/dist/node-sync-service-y8b5w1rd.js +56 -0
  65. package/dist/{out-target-wjgmna0r.js → out-target-eqsttq1k.js} +18 -22
  66. package/dist/{packager-tool-f8vwy92w.js → packager-tool-0te1tyhf.js} +186 -16
  67. package/dist/{packager-tool-ydyvtknp.js → packager-tool-0w4fsdzv.js} +44 -20
  68. package/dist/{packager-tool-8ftat1vn.js → packager-tool-17b466mr.js} +59158 -59666
  69. package/dist/packager-tool-1nfdj0zm.js +425 -0
  70. package/dist/packager-tool-1wgav5ma.js +139 -0
  71. package/dist/{packager-tool-b1nw50ev.js → packager-tool-265h946t.js} +256 -486
  72. package/dist/{packager-tool-nj4seavy.js → packager-tool-35c9npcy.js} +26 -7
  73. package/dist/{packager-tool-r0x3qh59.js → packager-tool-388vyja1.js} +185 -29
  74. package/dist/packager-tool-3m8482sw.js +78 -0
  75. package/dist/packager-tool-3r23qsca.js +4832 -0
  76. package/dist/packager-tool-3xehbdyd.js +1302 -0
  77. package/dist/packager-tool-4avchhz8.js +539 -0
  78. package/dist/{packager-tool-9kvkzc05.js → packager-tool-5w0gjcck.js} +4 -4
  79. package/dist/packager-tool-68648y3j.js +6 -0
  80. package/dist/{packager-tool-1yh8g550.js → packager-tool-6an893wm.js} +12 -3
  81. package/dist/{packager-tool-aagnvyba.js → packager-tool-76zhn5pa.js} +131 -432
  82. package/dist/packager-tool-7x1tmb8a.js +46 -0
  83. package/dist/{packager-tool-kyrm4e5m.js → packager-tool-9ar5829n.js} +781 -741
  84. package/dist/{packager-tool-4p11fvf4.js → packager-tool-b2q8m9pq.js} +10 -13
  85. package/dist/packager-tool-bg3tdpva.js +27 -0
  86. package/dist/{packager-tool-wr12ndwk.js → packager-tool-ccejfs0r.js} +252 -468
  87. package/dist/{packager-tool-yn33pw57.js → packager-tool-dn9d4ywm.js} +246 -225
  88. package/dist/{packager-tool-kdhey8gz.js → packager-tool-e02jkmrq.js} +41 -29
  89. package/dist/{packager-tool-sq931c23.js → packager-tool-e1tx5mqg.js} +1 -1
  90. package/dist/{packager-tool-qn3zkrrc.js → packager-tool-ehjw672b.js} +32 -15
  91. package/dist/packager-tool-gy4yjqhf.js +178 -0
  92. package/dist/packager-tool-h2ar8xf8.js +222 -0
  93. package/dist/{packager-tool-v2kcgjbf.js → packager-tool-j6nfje7t.js} +5 -4
  94. package/dist/packager-tool-k0ar6cn7.js +295 -0
  95. package/dist/packager-tool-kz7zz79p.js +71 -0
  96. package/dist/{packager-tool-1gwbq864.js → packager-tool-m5384a1s.js} +66 -82
  97. package/dist/{packager-tool-9e59z4d6.js → packager-tool-me8gd22q.js} +82 -83
  98. package/dist/packager-tool-mk9fmzdy.js +20 -0
  99. package/dist/{packager-tool-yfmby4dd.js → packager-tool-mkt1njk2.js} +630 -689
  100. package/dist/{packager-tool-gdtpsdn7.js → packager-tool-mm1x18d6.js} +223 -28
  101. package/dist/{packager-tool-mqy97y5f.js → packager-tool-p5gs9xqy.js} +14 -14
  102. package/dist/{packager-tool-83v804p7.js → packager-tool-psmb758n.js} +3108 -2627
  103. package/dist/packager-tool-q26hncjr.js +13 -0
  104. package/dist/packager-tool-q9fnx278.js +77 -0
  105. package/dist/{packager-tool-ezds6t5e.js → packager-tool-s12s1dp6.js} +11 -11
  106. package/dist/packager-tool-scy94jyb.js +5424 -0
  107. package/dist/{packager-tool-yn7r7zq2.js → packager-tool-snesn0bt.js} +177 -96
  108. package/dist/{packager-tool-d8e2mrkq.js → packager-tool-tgehy324.js} +56 -56
  109. package/dist/{packager-tool-tqn0j6f9.js → packager-tool-v11fx4dk.js} +7 -7
  110. package/dist/{packager-tool-67vy1fz3.js → packager-tool-v6rkp2ay.js} +24 -92
  111. package/dist/{packager-tool-9sd483vs.js → packager-tool-vs7gqa4r.js} +15 -15
  112. package/dist/packager-tool-vyavrbym.js +488 -0
  113. package/dist/{packager-tool-b098y4a2.js → packager-tool-wbksf242.js} +97 -97
  114. package/dist/{packager-tool-2sn7d8fd.js → packager-tool-x74mmjkw.js} +9 -13
  115. package/dist/{packager-tool-7hw344bt.js → packager-tool-xb16e3tz.js} +5 -5
  116. package/dist/{packager-tool-a17g0pj1.js → packager-tool-xd1wbvay.js} +10 -8
  117. package/dist/{packager-tool-1yfw674x.js → packager-tool-xehhk3c3.js} +10 -5
  118. package/dist/packager-tool-yh8mmj3p.js +123 -0
  119. package/dist/{packager-tool-hdxtgckw.js → packager-tool-z070caqq.js} +56 -12
  120. package/dist/{packager-tool-b250k6w4.js → packager-tool-z1fw9a8v.js} +41 -9
  121. package/dist/{packager-tool-0gctz400.js → packager-tool-z9fmqtm6.js} +4 -4
  122. package/dist/{packager-tool-mzf4gmph.js → packager-tool-zbx189j3.js} +11 -8
  123. package/dist/{packager-tool-zf8f9pmm.js → packager-tool-zrbnms9b.js} +3 -4
  124. package/dist/packager-tool.js +10 -11
  125. package/dist/prepare-uip-ad7mfs8t.js +92 -0
  126. package/dist/{project-artifacts-bgyyb0dm.js → project-artifacts-q0c0zghw.js} +2 -6
  127. package/dist/{pt-BR-USP2T7GD-pedn77m1.js → pt-BR-BIUJXIPU-2sqzhr8y.js} +20 -5
  128. package/dist/{pt-BR-GNQNAAZ2-xadharv4.js → pt-BR-PHLFHMXZ-n6de4s7z.js} +191 -11
  129. package/dist/{pt-RMIAKZB4-67enhx3m.js → pt-MHX6JGAX-xfgvdqwg.js} +20 -5
  130. package/dist/{pt-TO2DARY2-ftcce1bv.js → pt-N7ZTJT4W-d6c67xcn.js} +190 -10
  131. package/dist/{resolved-check-aqw7568y.js → resolved-check-dz8eqrf6.js} +2 -3
  132. package/dist/{resourcesSyncService-8ac6ncq2.js → resourcesSyncService-yrhhh62m.js} +81 -141
  133. package/dist/{ro-GHOBYW2S-mhnqsgaa.js → ro-3RKJNGJZ-xqj14e3d.js} +20 -5
  134. package/dist/{ro-WUYJV6OW-a8d9qy1n.js → ro-FLNXVYJI-ytrwabg2.js} +190 -10
  135. package/dist/ru-GSCT4ARZ-c2cfq4h1.js +7 -0
  136. package/dist/ru-WBLQ5B2U-bf20yr5y.js +7 -0
  137. package/dist/{rule-group-members-zje7n3kf.js → rule-group-members-p0nv1rmj.js} +1 -3
  138. package/dist/rule-type-declaration-s2nxpcfy.js +77 -0
  139. package/dist/{sdd-convert-service-8jpew7vz.js → sdd-convert-service-ffbx00k6.js} +481 -504
  140. package/dist/{sdd-lookup-fe900sdb.js → sdd-lookup-sah7wj5j.js} +8 -9
  141. package/dist/{sdd-resolve-service-qdtmxw9b.js → sdd-resolve-service-6nt10vhq.js} +28 -20
  142. package/dist/{sdd-resolve-siblings-8gf9zjwf.js → sdd-resolve-siblings-ap8j1c8h.js} +11 -14
  143. package/dist/solution-auth-a1p22hq1.js +11 -0
  144. package/dist/solution-discovery-r1y550sz.js +33 -0
  145. package/dist/{strict-case-rules-4tjcaznr.js → strict-case-rules-045kbjvv.js} +16 -28
  146. package/dist/{strict-entry-points-g2pd4mf4.js → strict-entry-points-40ha9afz.js} +15 -14
  147. package/dist/strict-sdd-audit-bp7fq2tg.js +26 -0
  148. package/dist/{task-selector-8bjn35dv.js → task-selector-jt60hdbt.js} +1 -3
  149. package/dist/{task-type-declaration-7a8fqdvs.js → task-type-declaration-yhczvj05.js} +9 -9
  150. package/dist/tool.js +18 -24
  151. package/dist/{tr-M3F5A6AU-bb0tg39m.js → tr-7AIZZWZ6-pdzp9n0p.js} +190 -10
  152. package/dist/{tr-GDTLECMM-k5fhmq13.js → tr-PQLUUHPC-qsb7btxn.js} +20 -5
  153. package/dist/{trigger-enrichment-service-agvnhdf7.js → trigger-enrichment-service-z8kxy6qr.js} +53 -57
  154. package/dist/trigger-entry-point-id-751v48qd.js +14 -0
  155. package/dist/{zh-CN-C5G4PC7C-ds9gkptg.js → zh-CN-CN2ARPFS-e0p2qrxf.js} +190 -10
  156. package/dist/{zh-CN-I5D7WHBD-sr5h0a98.js → zh-CN-OHNTHZRI-b0hjkfqn.js} +20 -5
  157. package/dist/{zh-TW-62HZIG6O-787kdwex.js → zh-TW-7RUFEVFK-qd7myw31.js} +190 -10
  158. package/dist/{zh-TW-2AVLM723-288pkc1z.js → zh-TW-E4D3Y6ZR-tn3bmzz5.js} +20 -5
  159. package/package.json +2 -2
  160. package/dist/canvas-notes-86fxx1t8.js +0 -19
  161. package/dist/case-event-trigger-bindings-g9ftnm4x.js +0 -16
  162. package/dist/case-schema-version-h820ne16.js +0 -20
  163. package/dist/case-shape-reader-dsbraetf.js +0 -13
  164. package/dist/connector-enrichment-service-6kskfevq.js +0 -65
  165. package/dist/connector-lookup-service-yyn8kkw7.js +0 -22
  166. package/dist/connector-task-shape-mpazkf7s.js +0 -14
  167. package/dist/debug-service-7q98j726.js +0 -59
  168. package/dist/en-EJCBA3LK-dtpdghgj.js +0 -146
  169. package/dist/en-M7GQYBV2-5nfmpnfz.js +0 -2018
  170. package/dist/filter-engine-7ep90g7f.js +0 -11
  171. package/dist/first-party-service-srxaftkn.js +0 -27
  172. package/dist/index-0d7qc2gs.js +0 -282
  173. package/dist/index-jmv5by9z.js +0 -666
  174. package/dist/index-kmp9fmhb.js +0 -25
  175. package/dist/index-n9qvnx4x.js +0 -29
  176. package/dist/index-ngs0a2he.js +0 -15
  177. package/dist/index-xq6ajhgq.js +0 -157
  178. package/dist/index.umd.min-fqefd9kn.js +0 -7814
  179. package/dist/input-reader-c7wz8ra2.js +0 -11
  180. package/dist/known-types-5b6yrsqn.js +0 -16
  181. package/dist/localSolutionDiscovery-yyx234ak.js +0 -13
  182. package/dist/node-sync-service-2ezr3akv.js +0 -57
  183. package/dist/packager-tool-53w8skv5.js +0 -262
  184. package/dist/packager-tool-6z1hsm2h.js +0 -143
  185. package/dist/packager-tool-93mdeqay.js +0 -18
  186. package/dist/packager-tool-9kf3fqtr.js +0 -23
  187. package/dist/packager-tool-bfgdz1zh.js +0 -73
  188. package/dist/packager-tool-e15kqpyh.js +0 -266
  189. package/dist/packager-tool-gmykn1ef.js +0 -5365
  190. package/dist/packager-tool-jysvpt8p.js +0 -11
  191. package/dist/packager-tool-pcvhcn2k.js +0 -2019
  192. package/dist/packager-tool-rk25wp3c.js +0 -28
  193. package/dist/packager-tool-sc961w0q.js +0 -16
  194. package/dist/packager-tool-wekqkmgn.js +0 -265
  195. package/dist/packager-tool-yc4kw997.js +0 -13
  196. package/dist/packager-tool-zgpsnxkd.js +0 -13189
  197. package/dist/ru-GSCT4ARZ-1sa9wbr8.js +0 -10
  198. package/dist/ru-WBLQ5B2U-s4em88p0.js +0 -10
  199. package/dist/solution-auth-30yycqtv.js +0 -14
  200. package/dist/solution-discovery-e9mamp6w.js +0 -38
  201. package/dist/strict-sdd-audit-q7tb9yj7.js +0 -25
  202. package/dist/trigger-entry-point-id-yhqwnkx9.js +0 -15
  203. /package/dist/{packager-tool-5qp9xza1.js → packager-tool-1c30ttpm.js} +0 -0
  204. /package/dist/{packager-tool-mknccck5.js → packager-tool-1h7abxw6.js} +0 -0
  205. /package/dist/{packager-tool-9y1w2wds.js → packager-tool-fcektt1g.js} +0 -0
  206. /package/dist/{packager-tool-k6jbedyc.js → packager-tool-hcdnc420.js} +0 -0
  207. /package/dist/{packager-tool-7nxppa3d.js → packager-tool-jv5byaww.js} +0 -0
  208. /package/dist/{packager-tool-qa9gftnj.js → packager-tool-mxgybnmt.js} +0 -0
  209. /package/dist/{packager-tool-87y6xw3a.js → packager-tool-nfhtwa72.js} +0 -0
  210. /package/dist/{packager-tool-cv05dwac.js → packager-tool-p0rdmmvk.js} +0 -0
  211. /package/dist/{packager-tool-21qfsbxn.js → packager-tool-yaertxsw.js} +0 -0
@@ -1,49 +1,68 @@
1
1
  import {
2
- ledgerMissFor
3
- } from "./packager-tool-b250k6w4.js";
2
+ catchError2
3
+ } from "./packager-tool-psmb758n.js";
4
4
  import {
5
- dedupedId,
6
- exactKey,
7
- indexKey,
8
- lookupOutput,
9
- modelKey,
10
- stageSpellings
11
- } from "./packager-tool-9kvkzc05.js";
12
- import {
13
- outputSourceFor
14
- } from "./packager-tool-ydyvtknp.js";
15
- import {
16
- comparableFolder
17
- } from "./packager-tool-21qfsbxn.js";
18
- import {
19
- cellAt,
20
- columnField,
21
- columnIndex,
22
- fenceOf,
5
+ withoutTrailingSlug,
23
6
  field,
24
- firstTable,
25
- isBlank,
26
7
  isDigit,
27
8
  isLetter,
28
9
  isLowerLetter,
29
- isSeparatorCell,
30
- namedValue,
10
+ trimQuotes,
31
11
  norm,
32
- pipeTables,
33
- splitRowCells,
34
12
  stripTicks,
35
- trimQuotes,
36
- withoutAnnotation
37
- } from "./packager-tool-9y1w2wds.js";
13
+ isBlank,
14
+ namedValue,
15
+ splitRowCells,
16
+ isSeparatorCell,
17
+ firstTable,
18
+ fenceOf,
19
+ pipeTables,
20
+ columnIndex,
21
+ cellAt,
22
+ withoutAnnotation,
23
+ columnField
24
+ } from "./packager-tool-fcektt1g.js";
25
+ import {
26
+ CASE_APP_VERSIONS_WITH_ROLES,
27
+ roleActionKeyOf,
28
+ readCaseRoles
29
+ } from "./packager-tool-1wgav5ma.js";
30
+ import {
31
+ resolvePersonas
32
+ } from "./packager-tool-yh8mmj3p.js";
33
+ import {
34
+ CONNECTOR_CALL_TASK_TYPES
35
+ } from "./packager-tool-7x1tmb8a.js";
36
+ import {
37
+ outputSourceFor
38
+ } from "./packager-tool-0w4fsdzv.js";
39
+ import {
40
+ STAGE_SELECTOR_RULES,
41
+ TASK_SELECTOR_RULES,
42
+ parseIfCell,
43
+ outputTypeCell,
44
+ canonicalExpression
45
+ } from "./packager-tool-1nfdj0zm.js";
38
46
  import {
39
- isAnyTriggerNode,
40
- isStageNode
41
- } from "./packager-tool-nj4seavy.js";
47
+ ledgerMissFor,
48
+ ledgerFoundFor
49
+ } from "./packager-tool-z1fw9a8v.js";
42
50
  import {
43
- catchError
44
- } from "./packager-tool-83v804p7.js";
51
+ comparableFolder
52
+ } from "./packager-tool-yaertxsw.js";
53
+ import {
54
+ stageSpellings
55
+ } from "./packager-tool-5w0gjcck.js";
56
+ import {
57
+ isStageNode,
58
+ isAnyTriggerNode
59
+ } from "./packager-tool-35c9npcy.js";
45
60
 
46
61
  // src/services/sdd-normalizer.ts
62
+ var ownBodyOf = (section) => {
63
+ const first = section.children[0];
64
+ return first === undefined ? section.body : section.body.slice(0, first.start - section.contentStart);
65
+ };
47
66
  var readSddHeading = (line) => {
48
67
  let level = 0;
49
68
  while (level < line.length && line[level] === "#")
@@ -93,8 +112,10 @@ var readSddSections = (source) => {
93
112
  });
94
113
  const stack = [];
95
114
  for (const section of sections) {
96
- while (stack.length > 0 && stack.at(-1)?.level >= section.level) {
115
+ let top = stack.at(-1);
116
+ while (top !== undefined && top.level >= section.level) {
97
117
  stack.pop();
118
+ top = stack.at(-1);
98
119
  }
99
120
  const parent = stack.at(-1);
100
121
  if (parent !== undefined)
@@ -235,7 +256,14 @@ var SLA_RESPONSE_MAP_HEADINGS = ["SLA Response Map"];
235
256
  var CASE_METADATA_HEADINGS = ["Case Metadata"];
236
257
  var CASE_SLA_PROPERTY = "Case-Level SLA";
237
258
  var CASE_VARIABLE_HEADINGS = ["Case Variables"];
259
+ var PERSONA_HEADINGS = [
260
+ "Personas",
261
+ "Case Roles",
262
+ "Case Roles and Permissions",
263
+ "Roles and Permissions"
264
+ ];
238
265
  var CANVAS_NOTE_HEADINGS = ["Canvas Notes", "Sticky Notes"];
266
+ var BUSINESS_EVENT_HEADINGS = ["Business Events"];
239
267
  var STAGE_ENTRY_HEADINGS = ["Stage Entry Conditions"];
240
268
  var STAGE_EXIT_HEADINGS = [
241
269
  "Stage Exit Conditions",
@@ -254,12 +282,12 @@ var CASE_CONDITIONAL_SLA_HEADINGS = [
254
282
  ];
255
283
  var STAGE_TASK_HEADINGS = ["Tasks"];
256
284
  var VARIABLE_GROUPS = ["inputs", "outputs", "inputOutputs"];
257
- var VARIABLE_PLACEMENT = new Map([
285
+ var VARIABLE_PLACEMENT2 = new Map([
258
286
  ["in", ["inputs", "inputOutputs"]],
259
287
  ["out", ["outputs", "inputOutputs"]],
260
288
  ["variable", ["inputOutputs"]]
261
289
  ]);
262
- var DEFAULT_GROUP = new Map([
290
+ var DEFAULT_GROUP2 = new Map([
263
291
  ["in", "inputs"],
264
292
  ["out", "inputOutputs"],
265
293
  ["variable", "inputOutputs"]
@@ -303,8 +331,8 @@ var bindingId = (expression) => {
303
331
  if (!text.startsWith(prefix))
304
332
  return;
305
333
  const id = text.slice(prefix.length);
306
- const isWordChar2 = (ch) => isDigit(ch) || ch === "_" || isLowerLetter(ch.toLowerCase());
307
- return id !== "" && [...id].every(isWordChar2) ? id : undefined;
334
+ const isWordChar = (ch) => isDigit(ch) || ch === "_" || isLowerLetter(ch.toLowerCase());
335
+ return id !== "" && [...id].every(isWordChar) ? id : undefined;
308
336
  };
309
337
  var bindingDefault = (bindings, expression) => {
310
338
  const id = bindingId(expression);
@@ -372,7 +400,11 @@ var parseCaseplan = (doc) => {
372
400
  bindings,
373
401
  caseExitConditions: asArray(metadata.caseExitRules),
374
402
  caseSla: asArray(metadata.slaRules).length > 0,
375
- caseSlaRules: asArray(metadata.slaRules)
403
+ caseSlaRules: asArray(metadata.slaRules),
404
+ caseRoles: readCaseRoles(doc),
405
+ ...typeof metadata.caseAppVersion === "string" && {
406
+ caseAppVersion: metadata.caseAppVersion
407
+ }
376
408
  };
377
409
  for (const node of asArray(doc.nodes)) {
378
410
  const record = asRecord(node);
@@ -488,6 +520,7 @@ var customRowsOf = (header, rows) => {
488
520
  const bindingAt = columnIndex(header, ...INPUT_VALUE_COLUMNS);
489
521
  if (bindingAt === undefined)
490
522
  return [];
523
+ const typeAt = columnIndex(header, "Type");
491
524
  const found = [];
492
525
  for (const row of rows) {
493
526
  const binding = unwrapCodeSpan(cellAt(row, bindingAt));
@@ -501,7 +534,12 @@ var customRowsOf = (header, rows) => {
501
534
  const emptyLiteral = /^(""|'')$/.test(expression);
502
535
  if (!emptyLiteral && (!expression || isBlank(expression)))
503
536
  continue;
504
- found.push({ variable, expression });
537
+ const type = typeAt === undefined ? "" : cellAt(row, typeAt).trim();
538
+ found.push({
539
+ variable,
540
+ expression,
541
+ ...type !== "" && !isBlank(type) && { type }
542
+ });
505
543
  }
506
544
  return found;
507
545
  };
@@ -615,6 +653,7 @@ var xrefArguments = (text) => {
615
653
  return value.trim();
616
654
  }).filter((part) => part !== "");
617
655
  };
656
+ var IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
618
657
  var producerSegments = (raw) => {
619
658
  if (raw.includes(XREF_OPEN))
620
659
  return xrefArguments(raw);
@@ -625,9 +664,13 @@ var producerSegments = (raw) => {
625
664
  };
626
665
  var readProducer = (raw) => {
627
666
  const segments = producerSegments(raw);
628
- if (segments.length !== 3)
667
+ if (segments.length < 3)
629
668
  return;
630
- const [stage, task, output] = segments;
669
+ if (segments.length > 3 && (raw.includes(XREF_OPEN) || !segments.slice(2).every((part) => IDENTIFIER.test(part)))) {
670
+ return;
671
+ }
672
+ const [stage, task, ...path] = segments;
673
+ const output = path.join(".");
631
674
  return stage && task && output ? { stage, task, output } : undefined;
632
675
  };
633
676
  var resourceOutputsByTask = (document) => {
@@ -711,19 +754,24 @@ var detailProducerInputs = (block) => {
711
754
  };
712
755
 
713
756
  // src/services/strict-sdd-audit/references.ts
714
- var OPEN_QUOTES = new Set(['"', "“", "‘", "'"]);
715
- var CLOSE_QUOTES = new Set(['"', "”", "’", "'"]);
757
+ var QUOTE_PAIRS = new Map([
758
+ ['"', '"'],
759
+ ["“", "”"],
760
+ ["‘", "’"],
761
+ ["'", "'"]
762
+ ]);
716
763
  var quotedArgs = (argumentList) => {
717
764
  const args = [];
718
765
  let at = 0;
719
766
  while (at < argumentList.length) {
720
- if (!OPEN_QUOTES.has(argumentList[at])) {
767
+ const close = QUOTE_PAIRS.get(argumentList[at]);
768
+ if (close === undefined) {
721
769
  at += 1;
722
770
  continue;
723
771
  }
724
772
  const from = at + 1;
725
773
  let end = from;
726
- while (end < argumentList.length && !CLOSE_QUOTES.has(argumentList[end])) {
774
+ while (end < argumentList.length && argumentList[end] !== close) {
727
775
  end += 1;
728
776
  }
729
777
  if (end >= argumentList.length)
@@ -831,7 +879,7 @@ var pairLeftovers = (sddKeys, planKeys) => {
831
879
  return pairs.sort(([a], [b]) => a - b);
832
880
  };
833
881
  var pairByEvidence = (sddCount, planCount, related) => {
834
- const candidates = Array.from({ length: sddCount }, (_, s) => Array.from({ length: planCount }, (_2, p) => p).filter((p) => related(s, p)));
882
+ const candidates = Array.from({ length: sddCount }, (_, s) => Array.from({ length: planCount }, (_, p) => p).filter((p) => related(s, p)));
835
883
  const pairs = [];
836
884
  candidates.forEach((planAts, s) => {
837
885
  if (planAts.length !== 1)
@@ -939,19 +987,22 @@ var conditionNameFindings = (declared, planConditions, path, label, once) => {
939
987
  return findings;
940
988
  };
941
989
 
990
+ // src/services/strict-sdd-audit/input-lookup.ts
991
+ var inputNamed = (inputs, name) => inputs.find((i) => norm(i.name) === norm(name)) ?? inputs.find((i) => typeof i.displayName === "string" && norm(i.displayName) === norm(name));
992
+
942
993
  // src/services/strict-sdd-audit/wiring.ts
943
- var parsesAsJson = (text) => catchError(() => JSON.parse(text))[0] === undefined;
994
+ var parsesAsJson = (text) => catchError2(() => JSON.parse(text))[0] === undefined;
944
995
  var sameLiteral = (declared, actual) => {
945
996
  if (declared === actual)
946
997
  return true;
947
- const parse = (text) => catchError(() => JSON.parse(text))[1];
998
+ const parse = (text) => catchError2(() => JSON.parse(text))[1];
948
999
  const canonical = (value) => {
949
1000
  if (Array.isArray(value)) {
950
1001
  return `[${value.map(canonical).join(",")}]`;
951
1002
  }
952
1003
  const record = asRecord(value);
953
1004
  if (record) {
954
- return `{${Object.keys(record).sort((a2, b2) => a2.localeCompare(b2)).map((key) => `${JSON.stringify(key)}:${canonical(record[key])}`).join(",")}}`;
1005
+ return `{${Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => `${JSON.stringify(key)}:${canonical(record[key])}`).join(",")}}`;
955
1006
  }
956
1007
  return JSON.stringify(value) ?? "null";
957
1008
  };
@@ -1113,7 +1164,7 @@ var wiringFindings = (task, found, path, label) => {
1113
1164
  const findings = [];
1114
1165
  const inputs = asArray(found.data.inputs).map(asRecord).filter((i) => i !== undefined);
1115
1166
  for (const declared of task.expressionInputs) {
1116
- const input = inputs.find((i) => norm(i.name) === norm(declared.name));
1167
+ const input = inputNamed(inputs, declared.name);
1117
1168
  if (!input)
1118
1169
  continue;
1119
1170
  const actual = input.value;
@@ -1149,6 +1200,21 @@ var referenceTokensIn = (text) => {
1149
1200
  return tokens;
1150
1201
  };
1151
1202
  var isReferenceToken = (ch) => ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch >= "0" && ch <= "9" || ch === "_";
1203
+ var RESPONSE_PREFIX = "response.";
1204
+ var outputLabels = (output) => {
1205
+ const labels = [output.name, output.var];
1206
+ const source = output.source;
1207
+ if (typeof source !== "string" || !source.startsWith("="))
1208
+ return labels;
1209
+ const path = source.slice(1).trim();
1210
+ if (!path.split(".").every((part) => IDENTIFIER.test(part)))
1211
+ return labels;
1212
+ labels.push(path);
1213
+ if (path.startsWith(RESPONSE_PREFIX)) {
1214
+ labels.push(path.slice(RESPONSE_PREFIX.length));
1215
+ }
1216
+ return labels;
1217
+ };
1152
1218
  var planOutputFor = (plan, producer) => {
1153
1219
  for (const stage of plan.stages) {
1154
1220
  if (norm(stage.name) !== norm(producer.stage))
@@ -1160,7 +1226,7 @@ var planOutputFor = (plan, producer) => {
1160
1226
  const output = asRecord(entry);
1161
1227
  if (output === undefined)
1162
1228
  continue;
1163
- for (const label of [output.name, output.var]) {
1229
+ for (const label of outputLabels(output)) {
1164
1230
  if (typeof label === "string" && norm(label) === norm(producer.output)) {
1165
1231
  return output;
1166
1232
  }
@@ -1229,7 +1295,7 @@ var producerIndex = (plan) => {
1229
1295
  }
1230
1296
  if (tokens.size === 0)
1231
1297
  continue;
1232
- for (const label of [output.name, output.var]) {
1298
+ for (const label of outputLabels(output)) {
1233
1299
  if (typeof label !== "string" || label === "")
1234
1300
  continue;
1235
1301
  const key = `${norm(stage.name)}|${norm(planTask.name)}|${norm(label)}`;
@@ -1385,8 +1451,8 @@ var conditionExpressionFindings = (declared, planConditions, plan, path, label)
1385
1451
  if (resolved === undefined)
1386
1452
  continue;
1387
1453
  const expected = comparableExpression(resolved.expected);
1388
- const comparable = (actual2) => {
1389
- let normalised = actual2;
1454
+ const comparable = (actual) => {
1455
+ let normalised = actual;
1390
1456
  for (const token of resolved.tokens) {
1391
1457
  normalised = normalised.replaceAll(`${VARS_PREFIX}${token}`, VARIABLE_PLACEHOLDER);
1392
1458
  }
@@ -1423,7 +1489,7 @@ var expressionShapeFindings = (task, found, plan, path, label) => {
1423
1489
  const resolved = resolveXrefExpression(declared.expression, index);
1424
1490
  if (resolved === undefined)
1425
1491
  continue;
1426
- const input = inputs.find((i) => norm(i.name) === norm(declared.name));
1492
+ const input = inputNamed(inputs, declared.name);
1427
1493
  const actual = input?.value;
1428
1494
  if (typeof actual !== "string" || actual.trim() === "")
1429
1495
  continue;
@@ -1439,20 +1505,213 @@ var expressionShapeFindings = (task, found, plan, path, label) => {
1439
1505
  return findings;
1440
1506
  };
1441
1507
 
1508
+ // src/services/strict-sdd-audit/personas.ts
1509
+ var personaFindings = (sdd, plan) => {
1510
+ if (sdd.personas.length === 0)
1511
+ return [];
1512
+ const planStageId = new Map;
1513
+ for (const stage of plan.stages) {
1514
+ if (stage.name === undefined || stage.id === undefined)
1515
+ continue;
1516
+ planStageId.set(norm(withoutTrailingSlug(stage.name)), stage.id);
1517
+ }
1518
+ const declared = new Set(sdd.stages.map((stage) => norm(withoutTrailingSlug(stage.name))));
1519
+ const { roles, problems } = resolvePersonas(sdd.personas, (name) => {
1520
+ const key = norm(withoutTrailingSlug(name));
1521
+ if (!declared.has(key))
1522
+ return;
1523
+ return planStageId.get(key) ?? `${UNPLANNED_STAGE}${key}`;
1524
+ });
1525
+ const strictFor = new Map(sdd.personas.map((persona) => [
1526
+ persona.name.toLowerCase(),
1527
+ persona.templateShape
1528
+ ]));
1529
+ const finding = (personaName, code, path, message) => (strictFor.get(personaName.toLowerCase()) ? error : warning)(code, path, message);
1530
+ const issues = problems.map((problem) => {
1531
+ const code = problemCode(problem.code);
1532
+ const path = `metadata.caseRoles[${problem.persona}]`;
1533
+ const message = `Persona "${problem.persona}": ${problem.detail}.`;
1534
+ return problem.code === "stages-on-case" || problem.code === "permission-gated" ? warning(code, path, message) : finding(problem.persona, code, path, message);
1535
+ });
1536
+ if (roles.length === 0)
1537
+ return issues;
1538
+ if (plan.caseAppVersion === undefined || !CASE_APP_VERSIONS_WITH_ROLES.includes(plan.caseAppVersion)) {
1539
+ issues.push((roles.some((role) => strictFor.get(role.name.toLowerCase())) ? error : warning)("STRICT_SDD_PERSONA_APP_VERSION", "metadata.caseAppVersion", `The SDD declares ${roles.length} persona(s) and caseplan metadata.caseAppVersion is "${plan.caseAppVersion ?? ""}"; roles are honoured only under "v2" or "custom", so the deployed case would carry none.`));
1540
+ }
1541
+ const byName = new Map(plan.caseRoles.map((role) => [role.name.toLowerCase(), role]));
1542
+ const matched = new Set;
1543
+ for (const persona of roles) {
1544
+ const role = byName.get(persona.name.toLowerCase());
1545
+ if (role === undefined) {
1546
+ issues.push(finding(persona.name, "STRICT_SDD_PERSONA_MISSING", `metadata.caseRoles[${persona.name}]`, `Persona "${persona.name}" has no entry in caseplan metadata.caseRoles, so nobody can be assigned it.`));
1547
+ continue;
1548
+ }
1549
+ matched.add(role);
1550
+ issues.push(...roleFindings(persona, role, (code, path, message) => finding(persona.name, code, path, message)));
1551
+ }
1552
+ for (const role of plan.caseRoles) {
1553
+ if (matched.has(role))
1554
+ continue;
1555
+ issues.push(warning("STRICT_SDD_PERSONA_EXTRA", `metadata.caseRoles[${role.name}]`, `Role "${role.name}" is in caseplan metadata.caseRoles with no matching SDD persona.`));
1556
+ }
1557
+ return issues;
1558
+ };
1559
+ var UNPLANNED_STAGE = "sdd:";
1560
+ var problemCode = (code) => `STRICT_SDD_PERSONA_${code.toUpperCase().replace(/-/g, "_")}`;
1561
+ var sameSet = (left, right) => left.length === right.length && left.every((item) => right.includes(item));
1562
+ var keysOf = (actions) => actions.map((action) => roleActionKeyOf(action) ?? action).join(", ");
1563
+ var roleFindings = (persona, role, report) => {
1564
+ const path = `metadata.caseRoles[${persona.name}]`;
1565
+ const issues = [];
1566
+ if (role.scope !== persona.scope) {
1567
+ issues.push(report("STRICT_SDD_PERSONA_SCOPE_MISMATCH", `${path}.scope`, `Persona "${persona.name}": the SDD scopes it to ${persona.scope === "case" ? "the whole case" : "stages"} and the caseplan role has scope "${role.scope}".`));
1568
+ return issues;
1569
+ }
1570
+ if (persona.scope === "stages" && persona.stageIds.every((id) => !id.startsWith(UNPLANNED_STAGE)) && !sameSet(persona.stageIds, role.stageIds ?? [])) {
1571
+ issues.push(report("STRICT_SDD_PERSONA_STAGES_MISMATCH", `${path}.stageIds`, `Persona "${persona.name}": the SDD scopes it to stage(s) ${persona.stageNames.join(", ")} and the caseplan role's stageIds select different stages.`));
1572
+ }
1573
+ if (!sameSet(persona.actions, role.actions)) {
1574
+ issues.push(report("STRICT_SDD_PERSONA_PERMISSIONS_MISMATCH", `${path}.actions`, `Persona "${persona.name}": the SDD grants ${keysOf(persona.actions)} and the caseplan role grants ${keysOf(role.actions) || "nothing"}.`));
1575
+ }
1576
+ return issues;
1577
+ };
1578
+
1579
+ // src/services/strict-sdd-audit/inputs.ts
1580
+ var CONNECTOR_ENVELOPE_INPUTS = new Set([
1581
+ "pathParameters",
1582
+ "queryParameters",
1583
+ "body"
1584
+ ]);
1585
+ var resolveEnvelopeKey = (body, name) => {
1586
+ const named = (record, part) => Object.entries(record).find(([key]) => norm(key) === norm(part));
1587
+ const direct = named(body, name);
1588
+ if (direct !== undefined)
1589
+ return { value: direct[1] };
1590
+ const parts = name.split(".").filter((part) => part !== "");
1591
+ if (parts.length < 2)
1592
+ return;
1593
+ let current = body;
1594
+ for (const part of parts) {
1595
+ const record = asRecord(current);
1596
+ if (record === undefined)
1597
+ return;
1598
+ const hit = named(record, part);
1599
+ if (hit === undefined)
1600
+ return;
1601
+ current = hit[1];
1602
+ }
1603
+ return { value: current };
1604
+ };
1605
+ var carriedInput = (found, name) => {
1606
+ const absent = { present: false, value: undefined, valueless: false };
1607
+ const inputs = asArray(found.data.inputs).map(asRecord).filter((i) => i !== undefined);
1608
+ const direct = inputNamed(inputs, name);
1609
+ if (direct !== undefined) {
1610
+ return {
1611
+ present: true,
1612
+ value: direct.value,
1613
+ valueless: !("value" in direct)
1614
+ };
1615
+ }
1616
+ if (typeof found.type !== "string" || !CONNECTOR_CALL_TASK_TYPES.has(found.type)) {
1617
+ return absent;
1618
+ }
1619
+ for (const entry of inputs) {
1620
+ const entryName = entry.name;
1621
+ if (typeof entryName !== "string" || !CONNECTOR_ENVELOPE_INPUTS.has(entryName)) {
1622
+ continue;
1623
+ }
1624
+ const body = asRecord(entry.body);
1625
+ if (body === undefined)
1626
+ continue;
1627
+ const carried = resolveEnvelopeKey(body, name) ?? (entryName === "body" && found.type === "wait-for-connector" ? ["parameters", "queryParams"].map((key) => asRecord(body[key])).filter((nested) => nested !== undefined).map((nested) => resolveEnvelopeKey(nested, name)).find((key) => key !== undefined) : undefined);
1628
+ if (carried !== undefined) {
1629
+ return { present: true, value: carried.value, valueless: false };
1630
+ }
1631
+ }
1632
+ return absent;
1633
+ };
1634
+ var declaredInputNames = (task) => {
1635
+ const names = [];
1636
+ const add = (name) => {
1637
+ if (!names.some((existing) => norm(existing) === norm(name))) {
1638
+ names.push(name);
1639
+ }
1640
+ };
1641
+ for (const entry of task.literalInputs)
1642
+ add(entry.name);
1643
+ for (const entry of task.expressionInputs)
1644
+ add(entry.name);
1645
+ for (const entry of task.producerInputs)
1646
+ add(entry.name);
1647
+ return names;
1648
+ };
1649
+ var boundInputNames = (task) => task.boundInputs.map((entry) => entry.name);
1650
+ var emptyLiteralInputNames = (task) => task.literalInputs.filter((entry) => isBlank(entry.value)).map((entry) => entry.name);
1651
+ var envelopeFindings = (task, found, path, label) => {
1652
+ const findings = [];
1653
+ const columns = [
1654
+ {
1655
+ declared: task.required,
1656
+ actual: found.isRequired,
1657
+ column: "Required",
1658
+ field: "isRequired"
1659
+ },
1660
+ {
1661
+ declared: task.runOnlyOnce,
1662
+ actual: found.shouldRunOnlyOnce,
1663
+ column: "Run Only Once",
1664
+ field: "shouldRunOnlyOnce"
1665
+ }
1666
+ ];
1667
+ for (const { declared, actual, column, field } of columns) {
1668
+ if (declared === undefined)
1669
+ continue;
1670
+ if (typeof actual === "boolean" && actual === declared)
1671
+ continue;
1672
+ findings.push(error("STRICT_SDD_TASK_ENVELOPE_MISMATCH", `${path}.${field}`, `${label}: the SDD Tasks table declares a "${column}" value for this task and its \`${field}\` ${typeof actual === "boolean" ? "disagrees" : "is absent"}. Set it from that column.`));
1673
+ }
1674
+ return findings;
1675
+ };
1676
+ var emptyInputFindings = (task, found, path, label) => {
1677
+ const declared = declaredInputNames(task);
1678
+ if (declared.length === 0)
1679
+ return [];
1680
+ if (Object.keys(found.data).length === 0)
1681
+ return [];
1682
+ const bound = boundInputNames(task);
1683
+ const emptyLiteral = emptyLiteralInputNames(task);
1684
+ const findings = [];
1685
+ const names = (list, name) => list.some((entry) => norm(entry) === norm(name));
1686
+ for (const name of declared) {
1687
+ const carried = carriedInput(found, name);
1688
+ const value = carried.value;
1689
+ const missing = !carried.present;
1690
+ if (!missing && !carried.valueless && (typeof value !== "string" || value.trim() !== "")) {
1691
+ continue;
1692
+ }
1693
+ if (!missing && (names(bound, name) || names(emptyLiteral, name))) {
1694
+ continue;
1695
+ }
1696
+ const declares = names(emptyLiteral, name) ? "the empty literal" : "a value";
1697
+ findings.push(error("STRICT_SDD_INPUT_EMPTY", `${path}.data.inputs[${name}]`, `${label}: the SDD Inputs row for "${name}" declares ${declares}, and the caseplan input ${missing ? "is absent" : carried.valueless ? "carries no `value` at all" : "is empty"}. Nothing reaches the task for it, and none of the value comparisons can run against an input that carries nothing. ${missing ? "The row exists, so the input has to exist too — add it, carrying that row's value." : carried.valueless ? "Its value has to live under `value` — data under `body` is the connector envelope, which does not apply to this task type." : "Re-read that row and set the input from it."}`));
1698
+ }
1699
+ return findings;
1700
+ };
1701
+
1442
1702
  // src/services/strict-sdd-audit/producer-findings.ts
1443
1703
  var producerFindings = (task, found, plan, path, label) => {
1444
1704
  if (task.producerInputs.length === 0)
1445
1705
  return [];
1446
1706
  const findings = [];
1447
1707
  const index = producerIndex(plan);
1448
- const inputs = asArray(found.data.inputs).map(asRecord).filter((i) => i !== undefined);
1449
1708
  for (const declared of task.producerInputs) {
1450
1709
  const { stage, task: producerTask, output } = declared.producer;
1451
1710
  const candidates = index.get(`${norm(stage)}|${norm(producerTask)}|${norm(output)}`);
1452
1711
  if (candidates === undefined || candidates.length !== 1)
1453
1712
  continue;
1454
- const input = inputs.find((i) => norm(i.name) === norm(declared.name));
1455
- if (input === undefined)
1713
+ const input = carriedInput(found, declared.name);
1714
+ if (!input.present)
1456
1715
  continue;
1457
1716
  const value = input.value;
1458
1717
  if (typeof value !== "string" || value.trim() === "")
@@ -1562,289 +1821,212 @@ var capitalize = (text) => text.charAt(0).toUpperCase() + text.slice(1);
1562
1821
  var noCategoryColumnNote = (title, header) => `section "${title}": the table has rows but no Category column ` + `(header: ${header.join(" | ") || "<none>"}) — no row can be checked ` + "for `variables` group placement, so a formal argument missing half its " + "pair passes";
1563
1822
  var unrecognizedHeadingNote = (title, level) => `heading "${title}" (level ${level}) reads like an audited section but ` + "matches no accepted spelling — rename it to one of the canonical " + "headings, or the checks it feeds are skipped";
1564
1823
 
1565
- // src/services/sdd-convert/xrefs.ts
1566
- var QUOTED = String.raw`(?:'((?:\\.|[^'\\])*)'|"((?:\\.|[^"\\])*)"|` + "`((?:\\\\.|[^`\\\\])*)`" + String.raw`|\u201c([^\u201d]*)\u201d)`;
1567
- var XREF_CALL = new RegExp(String.raw`vars\.\$xref\(\s*${QUOTED}\s*,\s*${QUOTED}\s*,\s*${QUOTED}\s*\)`, "g");
1568
- var argumentOf = (groups) => groups.find((value) => value !== undefined) ?? "";
1569
- var unresolvedXrefDetail = (whole, found) => {
1570
- if (found === undefined) {
1571
- return `\`${whole}\` names an output this SDD does not declare, so the marker is left in place rather than pointed at a guess`;
1572
- }
1573
- const spellings = new Set(found.names);
1574
- const listed = [...spellings].map((name) => `\`${name}\``).join(", ");
1575
- if (spellings.size < found.names.length) {
1576
- return `\`${whole}\` names a task that ${found.names.length} tasks in that stage answer to (${listed}), so the marker is left in place rather than pointed at one of them. Task names are unique across the case, so rename all but one`;
1577
- }
1578
- return `\`${whole}\` names its task in a spelling that ${found.names.length} tasks answer to (${listed}), so the marker is left in place rather than pointed at one of them — write the task's name as its heading spells it`;
1579
- };
1580
- var DOTTED_READ = /^\.[A-Za-z_$]/;
1581
- var deferredOutputs = new WeakMap;
1582
- var markDeferredOutputs = (index, ids) => {
1583
- deferredOutputs.set(index, ids);
1584
- };
1585
- var expandXrefs = (text, context) => text.replace(XREF_CALL, (whole, ...rest) => {
1586
- const offset = rest[rest.length - 2];
1587
- const captured = rest.slice(0, -2);
1588
- const stage = argumentOf(captured.slice(0, 4));
1589
- const task = argumentOf(captured.slice(4, 8));
1590
- const output = argumentOf(captured.slice(8, 12));
1591
- const found = lookupOutput(context.outputs, `${indexKey(stage)}|${indexKey(task)}|${indexKey(output)}`, task);
1592
- if (found !== undefined && "id" in found) {
1593
- if (deferredOutputs.get(context.outputs)?.has(found.id)) {
1594
- context.unresolved.push({
1595
- kind: "placeholder",
1596
- where: context.where,
1597
- detail: `\`${whole}\` reads an output of a task the resolved file defers, so \`vars.${found.id}\` resolves to nothing until that task is filled`
1598
- });
1599
- }
1600
- const after = text.slice(offset + whole.length);
1601
- return DOTTED_READ.test(after) ? `vars.${found.id}?` : `vars.${found.id}`;
1602
- }
1603
- context.unresolved.push({
1604
- kind: "producer-reference",
1605
- where: context.where,
1606
- detail: unresolvedXrefDetail(whole, found)
1607
- });
1608
- return whole;
1609
- });
1610
- var expandOutputMarkers = (output, context) => {
1611
- const expand = (value2) => typeof value2 === "string" && value2.includes("$xref") ? expandXrefs(value2, context) : value2;
1612
- const value = expand(output.value);
1613
- const source = expand(output.source);
1614
- return value === output.value && source === output.source ? output : { ...output, value, source };
1824
+ // src/services/strict-sdd-audit/selector-targets.ts
1825
+ var idsIn = (value) => {
1826
+ const values = Array.isArray(value) ? value : [value];
1827
+ return values.filter((entry) => typeof entry === "string" && entry.trim() !== "");
1828
+ };
1829
+ var selectedIds = (rule) => Object.entries(rule ?? {}).filter(([key]) => key.startsWith("selected")).flatMap(([, value]) => idsIn(value));
1830
+ var hasSelector = (rule) => selectedIds(rule).length > 0 || idsIn(rule?.slaId).length > 0;
1831
+ var ruleTargets = (rule, resolve, same) => {
1832
+ const named = selectedIds(rule).map((id) => resolve.stage(id) ?? resolve.task(id)).filter((name) => name !== undefined);
1833
+ const owners = idsIn(rule?.slaId).map((id) => resolve.sla?.(id)).filter((name) => name !== undefined);
1834
+ if (named.length === 0)
1835
+ return { names: owners };
1836
+ const conflict = owners.find((owner) => !named.some((name) => same(name, owner)));
1837
+ return conflict === undefined ? { names: named } : { names: named, conflict };
1615
1838
  };
1616
1839
 
1617
- // src/services/sdd-convert/io.ts
1618
- var taskMatcher = (model, taskName) => {
1619
- const mine = exactKey(taskName);
1620
- const folded = indexKey(taskName);
1621
- const filed = modelKey(taskName);
1622
- let unique = true;
1623
- let filedUnique = true;
1624
- for (const stage of model.stages) {
1625
- for (const other of stage.tasks) {
1626
- const name = exactKey(String(other.name ?? ""));
1627
- if (name === mine)
1628
- continue;
1629
- if (indexKey(name) === folded)
1630
- unique = false;
1631
- if (modelKey(name) === filed)
1632
- filedUnique = false;
1633
- }
1840
+ // src/services/strict-sdd-audit/selectors.ts
1841
+ var sameLabel = (a, b) => norm(a) === norm(b) || normAnnotationless(a) === normAnnotationless(b);
1842
+ var leadingSegment = (name) => {
1843
+ for (const dash of [" - ", " – ", " — "]) {
1844
+ const at = name.indexOf(dash);
1845
+ if (at > 0)
1846
+ return name.slice(0, at);
1634
1847
  }
1635
- return {
1636
- names: (spelled) => exactKey(spelled) === mine || unique && indexKey(spelled) === folded,
1637
- unique,
1638
- filedUnique
1639
- };
1848
+ return name;
1640
1849
  };
1641
- var declaredOutputs = (model, stageName, position, task) => {
1642
- const taskName = String(task.name ?? "");
1643
- const { names: own, slug, ordinal } = stageSpellings(stageName, position);
1644
- const names = [...own, ...slug === undefined ? [] : [slug], ordinal].map(indexKey);
1645
- const matcher = taskMatcher(model, taskName);
1646
- const mine = (producer) => names.includes(indexKey(producer.stage)) && matcher.names(producer.task);
1647
- const rows = (task.outputRows ?? []).map((row) => ({ row, aliases: [] }));
1648
- const sameOutput = (a, b) => indexKey(a) === indexKey(b) || (!a.includes(".") || !b.includes(".")) && indexKey(emittedName(a)) === indexKey(emittedName(b));
1649
- const dotted = (one) => [one.row, ...one.aliases].find((spelling) => spelling.includes("."));
1650
- const takes = (one, name) => {
1651
- if (!sameOutput(one.row, name))
1652
- return false;
1653
- if (!name.includes("."))
1654
- return true;
1655
- const held = dotted(one);
1656
- return held === undefined || indexKey(held) === indexKey(name);
1657
- };
1658
- const add = (name) => {
1659
- if (name === "")
1660
- return;
1661
- const found = rows.find((one) => takes(one, name));
1662
- if (found === undefined)
1663
- rows.push({ row: name, aliases: [] });
1664
- else if (found.row !== name && !found.aliases.includes(name)) {
1665
- found.aliases.push(name);
1850
+ var targetsOf = (rule, selectors) => norm(rule) === norm("sla-status-change") ? selectors.slice(0, 1) : norm(rule) === norm("api-event") ? [] : selectors;
1851
+ var undeclaredGateFinding = (row, planRules, path, label, resolve) => {
1852
+ if (row.ungated !== true || row.when === undefined)
1853
+ return;
1854
+ const { rule, selectors: named } = whenRule(row.when);
1855
+ if (!rule || isBlank(rule))
1856
+ return;
1857
+ const selectors = targetsOf(rule, named);
1858
+ const belongsToRow = (candidate) => {
1859
+ if (norm(rule) === norm("api-event")) {
1860
+ return candidate?.businessEventType === named[0];
1666
1861
  }
1862
+ if (selectors.length === 0)
1863
+ return !hasSelector(candidate);
1864
+ if (!hasSelector(candidate))
1865
+ return false;
1866
+ if (resolve === undefined)
1867
+ return false;
1868
+ const pointsAt = ruleTargets(candidate, resolve, sameLabel).names;
1869
+ if (pointsAt.length === 0)
1870
+ return false;
1871
+ return selectors.some((want) => {
1872
+ const denotes = resolve.label(want);
1873
+ return pointsAt.some((got) => sameLabel(got, want) || denotes !== undefined && got === denotes);
1874
+ });
1667
1875
  };
1668
- if (matcher.filedUnique) {
1669
- for (const name of model.resourceOutputs[modelKey(taskName)] ?? []) {
1670
- add(name);
1671
- }
1672
- }
1673
- for (const producer of model.xrefOutputs) {
1674
- if (mine(producer))
1675
- add(producer.output);
1676
- }
1677
- for (const stage of model.stages) {
1678
- for (const other of stage.tasks) {
1679
- for (const row of other.producerInputs ?? []) {
1680
- const producer = row.producer;
1681
- if (producer !== undefined && mine(producer)) {
1682
- add(producer.output);
1683
- }
1684
- }
1685
- }
1686
- }
1687
- return rows;
1688
- };
1689
- var emittedName = (row) => row.split(".").at(-1) ?? row;
1690
- var OUTPUT_TYPES = new Map([
1691
- ["string", "string"],
1692
- ["object", "jsonSchema"],
1693
- ["json", "jsonSchema"],
1694
- ["jsonschema", "jsonSchema"]
1695
- ]);
1696
- var readsOutput = (reference, row) => indexKey(reference) === indexKey(row) || !reference.includes(".") && indexKey(reference) === indexKey(emittedName(row));
1697
- var outputTypeFor = (model, stageNames, taskName, output) => {
1698
- const matcher = taskMatcher(model, taskName);
1699
- for (const stage of model.stages) {
1700
- for (const task of stage.tasks) {
1701
- for (const row of task.producerInputs ?? []) {
1702
- const producer = row.producer;
1703
- if (producer === undefined || row.direct !== true || typeof row.type !== "string" || !stageNames.includes(indexKey(producer.stage)) || !matcher.names(producer.task) || !readsOutput(producer.output, output)) {
1704
- continue;
1705
- }
1706
- const mapped = OUTPUT_TYPES.get(row.type.trim().toLowerCase().replaceAll(" ", ""));
1707
- if (mapped !== undefined)
1708
- return mapped;
1709
- }
1710
- }
1711
- }
1712
- return;
1876
+ const gated = planRules.filter((candidate) => {
1877
+ if (norm(candidate?.rule) !== norm(rule))
1878
+ return false;
1879
+ if (!belongsToRow(candidate))
1880
+ return false;
1881
+ const expression = candidate?.conditionExpression;
1882
+ return typeof expression === "string" && !isBlank(expression);
1883
+ });
1884
+ if (gated.length === 0)
1885
+ return;
1886
+ const expressions = [
1887
+ ...new Set(gated.map((candidate) => String(candidate?.conditionExpression)).filter((expression) => expression !== ""))
1888
+ ];
1889
+ const on = selectors.length > 0 ? ` on "${selectors.join('", "')}"` : "";
1890
+ return error("STRICT_SDD_CONDITION_EXPRESSION_UNDECLARED", `${path}[${row.displayName}].conditionExpression`, `${label}: the SDD row ${rowLabel(row)} declares WHEN \`${rule}\`${on} with no IF gate, and the caseplan's \`${rule}\` carries \`${expressions.join("`, `")}\`. A gate the SDD does not declare narrows the transition to one branch, so it fires where the row says it should fire every time. Remove the expression, or add the IF cell that justifies it.`);
1713
1891
  };
1714
- var outputFor = (row, elementId, taken, type, taskType) => {
1715
- const leaf = row.split(".").at(-1) ?? row;
1716
- const base = leaf === "" ? leaf : `${leaf[0].toLowerCase()}${leaf.slice(1)}`;
1717
- const id = dedupedId(base, taken);
1718
- return {
1719
- name: leaf,
1720
- ...type !== undefined && { type },
1721
- id,
1722
- var: id,
1723
- value: id,
1724
- source: outputSourceFor(taskType, row),
1725
- target: `=${id}`,
1726
- elementId
1727
- };
1892
+ var apiEventFinding = (row, rules, wanted, path, label) => {
1893
+ if (wanted === undefined || isBlank(wanted))
1894
+ return;
1895
+ const carried = rules.map((r) => r?.businessEventType).filter((one) => typeof one === "string");
1896
+ if (carried.includes(wanted))
1897
+ return;
1898
+ return error(carried.length === 0 ? "STRICT_SDD_CONDITION_SELECTOR_MISSING" : "STRICT_SDD_CONDITION_SELECTOR_MISMATCH", `${path}[${row.displayName}].rule`, carried.length === 0 ? `${label}: the SDD row ${rowLabel(row)} declares WHEN \`api-event\` on business event "${wanted}", and the caseplan's \`api-event\` names no \`businessEventType\`. The rule then listens for nothing and never fires. Set \`businessEventType\` to "${wanted}".` : `${label}: the SDD row ${rowLabel(row)} declares WHEN \`api-event\` on business event "${wanted}", and the caseplan's \`api-event\` listens for "${carried.join('", "')}". Rules match on the exact name, so it fires on a different event. Set \`businessEventType\` to "${wanted}".`);
1728
1899
  };
1729
- var variableOutputType = (model, variable) => {
1730
- const declared = model.variableTypes[modelKey(variable)];
1731
- if (typeof declared !== "string" || declared.trim() === "") {
1900
+ var whenCellFinding = (row, planRules, path, label, resolve, ownRules) => {
1901
+ if (row.when === undefined)
1902
+ return;
1903
+ const { rule, selectors: named } = whenRule(row.when);
1904
+ if (!rule || isBlank(rule))
1732
1905
  return;
1906
+ const wanted = targetsOf(rule, named);
1907
+ const matching = planRules.filter((r) => norm(r?.rule) === norm(rule));
1908
+ if (matching.length === 0) {
1909
+ return error("STRICT_SDD_CONDITION_RULE_MISMATCH", `${path}[${row.displayName}].rule`, `${label}: the SDD row ${rowLabel(row)} declares WHEN \`${rule}\`, no rule on it uses that predicate. A different predicate starts the task at a different moment. Re-read that row's WHEN cell.`);
1733
1910
  }
1734
- const key = declared.trim().toLowerCase().replaceAll(" ", "");
1735
- return OUTPUT_TYPES.get(key) ?? "string";
1736
- };
1737
- var extractOutputFor = (row, elementId, taken, type, taskType) => {
1738
- const leaf = row.field.split(".").at(-1) ?? row.field;
1739
- const base = leaf === "" ? leaf : `${leaf[0].toLowerCase()}${leaf.slice(1)}`;
1740
- const id = dedupedId(base, taken);
1741
- return {
1742
- name: leaf,
1743
- ...type !== undefined && { type },
1744
- id,
1745
- var: row.variable,
1746
- value: row.variable,
1747
- source: outputSourceFor(taskType, row.field),
1748
- target: `=${id}`,
1749
- originalVar: id,
1750
- elementId
1751
- };
1752
- };
1753
- var customOutputFor = (row, type) => ({
1754
- name: row.variable,
1755
- ...type !== undefined && { type },
1756
- custom: true,
1757
- var: row.variable,
1758
- value: canonicalExpression(unquoteLiteral(row.expression)),
1759
- source: canonicalExpression(unquoteLiteral(row.expression)),
1760
- target: "",
1761
- body: "",
1762
- elementId: "root"
1763
- });
1764
- var canonicalExpression = (value) => value.startsWith("=metadata.") ? `=js:${value.slice(1)}` : value;
1765
- var unquoteLiteral = (cell) => {
1766
- const text = cell.trim();
1767
- const quote = text[0];
1768
- if (quote !== '"' && quote !== "'")
1769
- return cell;
1770
- if (text.length < 2 || text[text.length - 1] !== quote)
1771
- return cell;
1772
- const inner = text.slice(1, -1);
1773
- for (let at = 0;at < inner.length; at += 1) {
1774
- if (inner[at] === "\\") {
1775
- at += 1;
1776
- continue;
1777
- }
1778
- if (inner[at] === quote)
1779
- return cell;
1911
+ if (norm(rule) === norm("api-event")) {
1912
+ const own = (ownRules ?? []).filter((r) => norm(r?.rule) === norm(rule));
1913
+ return apiEventFinding(row, own.length > 0 ? own : matching, named[0], path, label);
1780
1914
  }
1781
- return inner.replaceAll(`\\${quote}`, quote);
1782
- };
1783
- var RANK_LITERAL = 1;
1784
- var RANK_PRODUCER = 2;
1785
- var RANK_EXPRESSION = 3;
1786
- var unboundProducerDetail = (target, found) => {
1787
- if (found === undefined) {
1788
- return `the Inputs row reads an output (${target}) that no task in this SDD declares`;
1915
+ if (wanted.length === 0)
1916
+ return;
1917
+ const own = (ownRules ?? []).filter((r) => norm(r?.rule) === norm(rule));
1918
+ const targeted = own.length > 0 ? own : matching;
1919
+ if (!targeted.some(hasSelector)) {
1920
+ return error("STRICT_SDD_CONDITION_SELECTOR_MISSING", `${path}[${row.displayName}].rule`, `${label}: the SDD row ${rowLabel(row)} declares WHEN \`${rule}\` on "${wanted.join('", "')}", and the caseplan's \`${rule}\` carries no target. The predicate then refers to nothing, so it never becomes true and the gate never opens. Set the rule's \`selected*\` field to the target the row names.`);
1789
1921
  }
1790
- const spellings = new Set(found.names);
1791
- const listed = [...spellings].map((task) => `"${task}"`).join(", ");
1792
- if (spellings.size < found.names.length) {
1793
- return `the Inputs row reads an output (${target}) of a task that ${found.names.length} tasks in that stage answer to (${listed}), so it is left unbound rather than pointed at one of them. Task names are unique across the case, so rename all but one`;
1922
+ if (resolve === undefined)
1923
+ return;
1924
+ const resolved = targeted.map((candidate) => ruleTargets(candidate, resolve, sameLabel));
1925
+ const pointsAt = resolved.flatMap((target) => target.names);
1926
+ if (pointsAt.length === 0)
1927
+ return;
1928
+ const missed = wanted.filter((want) => {
1929
+ const denotes = resolve.label(want);
1930
+ return !pointsAt.some((got) => sameLabel(got, want) || denotes !== undefined && got === denotes);
1931
+ });
1932
+ const conflict = resolved.find((target) => target.conflict !== undefined);
1933
+ if (missed.length === 0 && conflict?.conflict !== undefined) {
1934
+ return error("STRICT_SDD_CONDITION_SELECTOR_MISMATCH", `${path}[${row.displayName}].rule`, `${label}: the SDD row ${rowLabel(row)} declares WHEN \`${rule}\` on "${wanted.join('", "')}", and the caseplan's \`${rule}\` names "${conflict.names.join('", "')}" but its \`slaId\` is the SLA of "${conflict.conflict}". The rule watches one stage's SLA while naming another, so it fires on a deadline the row did not ask for. Point \`slaId\` at the SLA of "${wanted.join('", "')}" and drop the \`selected*\` field, which convert does not write for this rule.`);
1794
1935
  }
1795
- return `the Inputs row reads an output (${target}) whose task spelling ${found.names.length} tasks answer to (${listed}), so it is left unbound rather than pointed at one of them`;
1936
+ if (missed.length === 0)
1937
+ return;
1938
+ return error("STRICT_SDD_CONDITION_SELECTOR_MISMATCH", `${path}[${row.displayName}].rule`, `${label}: the SDD row ${rowLabel(row)} declares WHEN \`${rule}\` on "${missed.join('", "')}", and the caseplan's \`${rule}\` points at "${pointsAt.join('", "')}". A selector that resolves to the wrong target builds a transition the SDD never asked for and leaves the declared one absent, which no count or predicate check can see.`);
1796
1939
  };
1797
- var inputsFor = (task, elementId, where, unresolved, outputs) => {
1798
- const ranked = new Map;
1799
- const offer = (name, rank, input) => {
1800
- const existing = ranked.get(name);
1801
- if (existing === undefined || rank > existing.rank) {
1802
- ranked.set(name, { rank, input });
1803
- }
1940
+ var conditionRuleFindings = (declared, planConditions, path, label, resolve) => {
1941
+ if (declared.length === 0)
1942
+ return [];
1943
+ const conditions = planConditions.map(asRecord);
1944
+ const rulesOf = (of) => of.flatMap((condition) => asArray(condition?.rules).flatMap((group) => asArray(group).map(asRecord)));
1945
+ const planRules = rulesOf(conditions);
1946
+ if (planRules.length === 0)
1947
+ return [];
1948
+ const owners = bindByName(declared, conditions);
1949
+ const taken = new Set(owners.filter((one) => one !== undefined));
1950
+ const openRows = declared.map((row, at) => ({ row, at })).filter(({ at }) => owners[at] === undefined);
1951
+ const openConditions = conditions.filter((one) => !taken.has(one));
1952
+ const gated = (condition) => rulesOf([condition]).some((rule) => {
1953
+ const expression = rule?.conditionExpression;
1954
+ return typeof expression === "string" && !isBlank(expression);
1955
+ });
1956
+ const rowGated = (row) => row.ungated === true ? false : row.ifExpression !== undefined ? true : undefined;
1957
+ const shapeKey = (rule, isGated) => {
1958
+ const name = norm(rule);
1959
+ if (name === "" || isGated === undefined)
1960
+ return;
1961
+ return `${name}|${isGated ? "gated" : "open"}`;
1804
1962
  };
1805
- for (const row of task.literalInputs ?? []) {
1806
- offer(String(row.name ?? ""), RANK_LITERAL, {
1807
- name: String(row.name ?? ""),
1808
- value: unquoteLiteral(String(row.value ?? "")),
1809
- elementId
1810
- });
1811
- }
1812
- for (const row of task.expressionInputs ?? []) {
1813
- offer(String(row.name ?? ""), RANK_EXPRESSION, {
1814
- name: String(row.name ?? ""),
1815
- value: canonicalExpression(expandXrefs(String(row.expression ?? row.value ?? ""), {
1816
- outputs,
1817
- where,
1818
- unresolved
1819
- })),
1820
- elementId
1821
- });
1963
+ for (const [rowAt, conditionAt] of pairLeftovers(openRows.map(({ row }) => shapeKey(whenRule(row.when ?? "").rule, rowGated(row))), openConditions.map((condition) => {
1964
+ const rules = new Set(rulesOf([condition]).map((r) => norm(r?.rule)));
1965
+ return rules.size === 1 ? shapeKey([...rules][0], gated(condition)) : undefined;
1966
+ }))) {
1967
+ owners[openRows[rowAt].at] = openConditions[conditionAt];
1822
1968
  }
1823
- for (const row of task.producerInputs ?? []) {
1824
- const name = String(row.name ?? "");
1825
- const producer = row.producer;
1826
- const found = producer === undefined ? undefined : lookupOutput(outputs, `${indexKey(producer.stage)}|${indexKey(producer.task)}|${indexKey(producer.output)}`, producer.task);
1827
- if (found === undefined || !("id" in found)) {
1828
- const target = producer === undefined ? "unnamed producer" : `"${producer.stage}"."${producer.task}".${producer.output}`;
1829
- unresolved.push({
1830
- kind: "producer-reference",
1831
- where: `${where}.inputs[${name}]`,
1832
- detail: unboundProducerDetail(target, found)
1833
- });
1969
+ const unownedBy = (at) => {
1970
+ const others = new Set(owners.filter((one, index) => index !== at && one !== undefined));
1971
+ return rulesOf(conditions.filter((one) => !others.has(one)));
1972
+ };
1973
+ return declared.flatMap((row, at) => [
1974
+ whenCellFinding(row, planRules, path, label, resolve, owners[at] === undefined ? undefined : rulesOf([owners[at]])),
1975
+ undeclaredGateFinding(row, unownedBy(at), path, label, resolve)
1976
+ ]).filter((finding) => finding !== undefined);
1977
+ };
1978
+ var CONDITION_FLAGS = [
1979
+ ["interrupting", "isInterrupting", "Interrupting"],
1980
+ ["marksComplete", "marksStageComplete", "Marks Stage Complete"]
1981
+ ];
1982
+ var conditionShapeFindings = (declared, planConditions, path, label) => {
1983
+ const findings = [];
1984
+ const bound = bindConditions(declared, planConditions);
1985
+ for (const [at, row] of declared.entries()) {
1986
+ const { match, shaped } = bound[at];
1987
+ if (match === undefined)
1834
1988
  continue;
1989
+ const planName = String(match.displayName ?? "");
1990
+ const where = shaped ? planName : row.displayName;
1991
+ const which = shaped ? `the condition "${planName}", the only one in this scope no row names that holds this row's rule,` : "the condition";
1992
+ if (row.exitType !== undefined) {
1993
+ const has = typeof match.type === "string" && match.type !== "";
1994
+ const effective = norm(has ? match.type : "exit-only");
1995
+ if (effective !== norm(row.exitType)) {
1996
+ findings.push(error("STRICT_SDD_CONDITION_EXIT_TYPE_MISMATCH", `${path}[${where}].type`, `${label}: the SDD row ${rowLabel(row)} declares Exit Type "${row.exitType}", ${which} has ${has ? `"${String(match.type)}"` : "no `type` at all, which the runtime reads as exit-only"}. The exit type decides where the case goes when the gate opens, so this sends it somewhere else${has ? "" : " — check for an invented key like `exitType` holding the value `type` should carry"}.`));
1997
+ }
1998
+ }
1999
+ for (const [cell, field, title] of CONDITION_FLAGS) {
2000
+ const want = row[cell];
2001
+ const got = typeof match[field] === "boolean" ? match[field] : false;
2002
+ if (want === undefined)
2003
+ continue;
2004
+ if (want === got)
2005
+ continue;
2006
+ findings.push(error("STRICT_SDD_CONDITION_FLAG_MISMATCH", `${path}[${where}].${field}`, `${label}: the SDD row ${rowLabel(row)} declares ${title}=${want ? "Yes" : "No"}, ${which} has ${typeof match[field] === "boolean" ? String(got) : "no such field, which the runtime reads as false"}.`));
1835
2007
  }
1836
- const id = found.id;
1837
- offer(name, RANK_PRODUCER, {
1838
- name,
1839
- value: row.direct === false ? expandXrefs(String(row.value ?? `=js:vars.${id}`), {
1840
- outputs,
1841
- where,
1842
- unresolved
1843
- }) : `=vars.${id}`,
1844
- elementId
1845
- });
1846
2008
  }
1847
- return [...ranked.values()].map((entry) => entry.input);
2009
+ return findings;
2010
+ };
2011
+ var undeclaredHandoffFindings = (stage, planConditions, stageLabels, path, label) => {
2012
+ if (stage.exitToColumn !== true)
2013
+ return [];
2014
+ if (stage.exitConditions.some((row) => row.exitToStage !== undefined)) {
2015
+ return [];
2016
+ }
2017
+ return planConditions.flatMap((raw) => {
2018
+ const condition = asRecord(raw);
2019
+ const target = condition?.exitToStageId;
2020
+ if (typeof target !== "string" || target === "")
2021
+ return [];
2022
+ const name = String(condition?.displayName ?? "");
2023
+ const where = name === "" ? String(condition?.id ?? "") : name;
2024
+ const targetLabel = stageLabels.get(target);
2025
+ const named = targetLabel === undefined ? `"${target}"` : `"${targetLabel}" (${target})`;
2026
+ return [
2027
+ error("STRICT_SDD_CONDITION_UNDECLARED_HANDOFF", `${path}[${where}].exitToStageId`, `${label}: the SDD's exit tables name no Exit-To Stage for this stage, so it hands off to no one, and the condition "${where}" routes the case to ${named}. Remove exitToStageId (in Case SDK source, drop \`exitToStage\` from the exitWhen options): the next stage starts from its own entry rule.`)
2028
+ ];
2029
+ });
1848
2030
  };
1849
2031
 
1850
2032
  // src/services/strict-sdd-audit/sla-durations.ts
@@ -2016,133 +2198,6 @@ var slaDurationFindings = (sdd, plan) => {
2016
2198
  return findings;
2017
2199
  };
2018
2200
 
2019
- // src/services/strict-sdd-audit/inputs.ts
2020
- var AUDIT_CONNECTOR_TASK_TYPES = new Set([
2021
- "execute-connector-activity",
2022
- "wait-for-connector"
2023
- ]);
2024
- var CONNECTOR_ENVELOPE_INPUTS = new Set([
2025
- "pathParameters",
2026
- "queryParameters",
2027
- "body"
2028
- ]);
2029
- var resolveEnvelopeKey = (body, name) => {
2030
- const named = (record, part) => Object.entries(record).find(([key]) => norm(key) === norm(part));
2031
- const direct = named(body, name);
2032
- if (direct !== undefined)
2033
- return { value: direct[1] };
2034
- const parts = name.split(".").filter((part) => part !== "");
2035
- if (parts.length < 2)
2036
- return;
2037
- let current = body;
2038
- for (const part of parts) {
2039
- const record = asRecord(current);
2040
- if (record === undefined)
2041
- return;
2042
- const hit = named(record, part);
2043
- if (hit === undefined)
2044
- return;
2045
- current = hit[1];
2046
- }
2047
- return { value: current };
2048
- };
2049
- var carriedInput = (found, name) => {
2050
- const absent = { present: false, value: undefined, valueless: false };
2051
- const inputs = asArray(found.data.inputs).map(asRecord).filter((i) => i !== undefined);
2052
- const direct = inputs.find((i) => norm(i.name) === norm(name));
2053
- if (direct !== undefined) {
2054
- return {
2055
- present: true,
2056
- value: direct.value,
2057
- valueless: !("value" in direct)
2058
- };
2059
- }
2060
- if (typeof found.type !== "string" || !AUDIT_CONNECTOR_TASK_TYPES.has(found.type)) {
2061
- return absent;
2062
- }
2063
- for (const entry of inputs) {
2064
- const entryName = entry.name;
2065
- if (typeof entryName !== "string" || !CONNECTOR_ENVELOPE_INPUTS.has(entryName)) {
2066
- continue;
2067
- }
2068
- const body = asRecord(entry.body);
2069
- if (body === undefined)
2070
- continue;
2071
- const carried = resolveEnvelopeKey(body, name);
2072
- if (carried !== undefined) {
2073
- return { present: true, value: carried.value, valueless: false };
2074
- }
2075
- }
2076
- return absent;
2077
- };
2078
- var declaredInputNames = (task) => {
2079
- const names = [];
2080
- const add = (name) => {
2081
- if (!names.some((existing) => norm(existing) === norm(name))) {
2082
- names.push(name);
2083
- }
2084
- };
2085
- for (const entry of task.literalInputs)
2086
- add(entry.name);
2087
- for (const entry of task.expressionInputs)
2088
- add(entry.name);
2089
- for (const entry of task.producerInputs)
2090
- add(entry.name);
2091
- return names;
2092
- };
2093
- var boundInputNames = (task) => task.boundInputs.map((entry) => entry.name);
2094
- var emptyLiteralInputNames = (task) => task.literalInputs.filter((entry) => isBlank(entry.value)).map((entry) => entry.name);
2095
- var envelopeFindings = (task, found, path, label) => {
2096
- const findings = [];
2097
- const columns = [
2098
- {
2099
- declared: task.required,
2100
- actual: found.isRequired,
2101
- column: "Required",
2102
- field: "isRequired"
2103
- },
2104
- {
2105
- declared: task.runOnlyOnce,
2106
- actual: found.shouldRunOnlyOnce,
2107
- column: "Run Only Once",
2108
- field: "shouldRunOnlyOnce"
2109
- }
2110
- ];
2111
- for (const { declared, actual, column, field: field2 } of columns) {
2112
- if (declared === undefined)
2113
- continue;
2114
- if (typeof actual === "boolean" && actual === declared)
2115
- continue;
2116
- findings.push(error("STRICT_SDD_TASK_ENVELOPE_MISMATCH", `${path}.${field2}`, `${label}: the SDD Tasks table declares a "${column}" value for this task and its \`${field2}\` ${typeof actual === "boolean" ? "disagrees" : "is absent"}. Set it from that column.`));
2117
- }
2118
- return findings;
2119
- };
2120
- var emptyInputFindings = (task, found, path, label) => {
2121
- const declared = declaredInputNames(task);
2122
- if (declared.length === 0)
2123
- return [];
2124
- if (Object.keys(found.data).length === 0)
2125
- return [];
2126
- const bound = boundInputNames(task);
2127
- const emptyLiteral = emptyLiteralInputNames(task);
2128
- const findings = [];
2129
- const names = (list, name) => list.some((entry) => norm(entry) === norm(name));
2130
- for (const name of declared) {
2131
- const carried = carriedInput(found, name);
2132
- const value = carried.value;
2133
- const missing = !carried.present;
2134
- if (!missing && !carried.valueless && (typeof value !== "string" || value.trim() !== "")) {
2135
- continue;
2136
- }
2137
- if (!missing && (names(bound, name) || names(emptyLiteral, name))) {
2138
- continue;
2139
- }
2140
- const declares = names(emptyLiteral, name) ? "the empty literal" : "a value";
2141
- findings.push(error("STRICT_SDD_INPUT_EMPTY", `${path}.data.inputs[${name}]`, `${label}: the SDD Inputs row for "${name}" declares ${declares}, and the caseplan input ${missing ? "is absent" : carried.valueless ? "carries no `value` at all" : "is empty"}. Nothing reaches the task for it, and none of the value comparisons can run against an input that carries nothing. ${missing ? "The row exists, so the input has to exist too — add it, carrying that row's value." : carried.valueless ? "Its value has to live under `value` — data under `body` is the connector envelope, which does not apply to this task type." : "Re-read that row and set the input from it."}`));
2142
- }
2143
- return findings;
2144
- };
2145
-
2146
2201
  // src/services/strict-sdd-audit/task-inputs.ts
2147
2202
  var PAYLOAD_SLOTS = ["body", "queryParameters", "pathParameters"];
2148
2203
  var usesSlotInputs = (found) => {
@@ -2153,7 +2208,7 @@ var usesSlotInputs = (found) => {
2153
2208
  return names.includes(norm("pathParameters")) || names.includes(norm("queryParameters"));
2154
2209
  };
2155
2210
  var carriesPayload = (inputs) => PAYLOAD_SLOTS.some((slot) => {
2156
- const input = inputs.find((i) => norm(i.name) === norm(slot));
2211
+ const input = inputNamed(inputs, slot);
2157
2212
  if (typeof input?.body === "string")
2158
2213
  return input.body !== "";
2159
2214
  return Object.keys(asRecord(input?.body) ?? {}).length > 0;
@@ -2163,10 +2218,10 @@ var boundInputFindings = (task, found, path, label) => {
2163
2218
  return [];
2164
2219
  const inputs = asArray(found.data.inputs).map(asRecord).filter((i) => i !== undefined);
2165
2220
  const findings = [];
2166
- const isConnector = AUDIT_CONNECTOR_TASK_TYPES.has(found.type ?? "") && usesSlotInputs(found);
2221
+ const isConnector = CONNECTOR_CALL_TASK_TYPES.has(found.type ?? "") && usesSlotInputs(found);
2167
2222
  let connectorRows = 0;
2168
2223
  for (const declared of task.boundInputs) {
2169
- const input = inputs.find((i) => norm(i.name) === norm(declared.name));
2224
+ const input = inputNamed(inputs, declared.name);
2170
2225
  if (input) {
2171
2226
  const valueless = !("value" in input);
2172
2227
  if (!valueless && input.value !== "")
@@ -2214,6 +2269,35 @@ var customOutputFindings = (task, found, path, label) => {
2214
2269
  }
2215
2270
  return findings;
2216
2271
  };
2272
+ var outputTypeFindings = (task, found, path, label) => {
2273
+ if (task.extractRows.length === 0 && task.customRows.length === 0) {
2274
+ return [];
2275
+ }
2276
+ const outputs = asArray(found.data.outputs).map(asRecord).filter((o) => o !== undefined);
2277
+ const declared = [
2278
+ ...task.extractRows.map((row) => ({
2279
+ row: `\`${row.field} -> ${row.variable}\``,
2280
+ type: row.type,
2281
+ output: outputs.find((o) => o.custom !== true && o.source === outputSourceFor(found.type, row.field) && o.var === row.variable)
2282
+ })),
2283
+ ...task.customRows.map((row) => ({
2284
+ row: `\`${row.variable} = ${row.expression}\``,
2285
+ type: row.type,
2286
+ output: outputs.find((o) => o.custom === true && o.var === row.variable)
2287
+ }))
2288
+ ];
2289
+ const findings = [];
2290
+ for (const { row, type, output } of declared) {
2291
+ const wanted = outputTypeCell(type).type;
2292
+ if (wanted === undefined || output === undefined)
2293
+ continue;
2294
+ const actual = typeof output.type === "string" ? output.type : "";
2295
+ if (actual === "" || actual === wanted)
2296
+ continue;
2297
+ findings.push(error("STRICT_SDD_OUTPUT_TYPE_MISMATCH", `${path}.data.outputs[${String(output.name ?? output.var)}].type`, `${label}: the SDD row ${row} types this output "${type}", so the output's \`type\` must be "${wanted}"; the caseplan has "${actual}". The row's Type is the resource's output-schema type, which is what Studio Web and \`uip maestro case splice --described\` write.`));
2298
+ }
2299
+ return findings;
2300
+ };
2217
2301
  var gatedSlaFindings = (rules, path, label) => {
2218
2302
  if (rules.length === 0)
2219
2303
  return [];
@@ -2232,7 +2316,7 @@ var gatedSlaFindings = (rules, path, label) => {
2232
2316
  // src/services/strict-sdd-audit/case-level.ts
2233
2317
  var variablePlacementFindings = (sdd, plan, name, key) => {
2234
2318
  const category = sdd.variableCategories.get(key);
2235
- const required = VARIABLE_PLACEMENT.get(category ?? "");
2319
+ const required = VARIABLE_PLACEMENT2.get(category ?? "");
2236
2320
  if (category === undefined || required === undefined)
2237
2321
  return [];
2238
2322
  const issues = [];
@@ -2247,7 +2331,7 @@ var variablePlacementFindings = (sdd, plan, name, key) => {
2247
2331
  var variableDefaultFindings = (sdd, plan, name, key) => {
2248
2332
  const declared = sdd.variableDefaults.get(key);
2249
2333
  const category = sdd.variableCategories.get(key);
2250
- const group = DEFAULT_GROUP.get(category ?? "");
2334
+ const group = DEFAULT_GROUP2.get(category ?? "");
2251
2335
  if (declared === undefined || group === undefined)
2252
2336
  return [];
2253
2337
  const entry = plan.variableEntries[group].get(key);
@@ -2269,12 +2353,14 @@ var variableDefaultFindings = (sdd, plan, name, key) => {
2269
2353
  var compareCaseLevel = (sdd, plan) => {
2270
2354
  const issues = [
2271
2355
  ...coverageFinding("metadata.caseExitRules", "case", "exit", sdd.caseExitRows, plan.caseExitConditions),
2356
+ ...conditionRuleFindings(sdd.caseExitConditions.filter((row) => norm(whenRule(row.when ?? "").rule) === norm("api-event")), plan.caseExitConditions, "metadata.caseExitRules", "case"),
2272
2357
  ...gatedSlaFindings(plan.caseSlaRules, "metadata.slaRules", "case"),
2273
2358
  ...conditionalSlaFindings(sdd.caseConditionalSlas, plan.caseSlaRules, "metadata.slaRules", "case", plan, sdd.stages, slaReferences(sdd))
2274
2359
  ];
2275
2360
  if (sdd.caseSla && !plan.caseSla) {
2276
2361
  issues.push(error("STRICT_SDD_SLA_MISSING", "metadata.slaRules", "case: the SDD declares Case-Level SLA Escalation Rules, the caseplan metadata.slaRules is empty."));
2277
2362
  }
2363
+ issues.push(...personaFindings(sdd, plan));
2278
2364
  issues.push(...escalationFindings(sdd.caseSlaEscalations, plan.caseSlaRules, "metadata.slaRules", "case", sdd.caseSlaResponses), ...xrefOutputFindings(sdd, plan), ...conditionExpressionFindings(sdd.caseExitConditions, plan.caseExitConditions, plan, "metadata.caseExitRules", "case"), ...conditionNameFindings(sdd.caseExitConditions, plan.caseExitConditions, "metadata.caseExitRules", "case", namesDeclaredOnce(sdd)));
2279
2365
  issues.push(...slaDurationFindings(sdd, plan));
2280
2366
  const caseDefaults = withoutConditional(plan.caseSlaRules, sdd.caseConditionalSlas, plan, sdd.stages, slaReferences(sdd));
@@ -2446,7 +2532,39 @@ var slaDurationOf = (block) => {
2446
2532
  return;
2447
2533
  };
2448
2534
 
2535
+ // src/services/strict-sdd-audit/document-extraction-detail.ts
2536
+ var readDocumentExtraction = (block) => {
2537
+ const read = (label) => {
2538
+ const raw = field(block, label);
2539
+ if (raw === undefined)
2540
+ return;
2541
+ const text = stripTicks(raw).trim();
2542
+ return text === "" || isBlank(text) ? undefined : text;
2543
+ };
2544
+ const extraction = {
2545
+ deployment: read("IXP Deployment"),
2546
+ modelName: read("Model Name"),
2547
+ deploymentKey: read("Deployment Key"),
2548
+ folderKey: read("Folder Key")
2549
+ };
2550
+ const stated = Object.entries(extraction).filter(([, value]) => value !== undefined);
2551
+ return stated.length === 0 ? undefined : Object.fromEntries(stated);
2552
+ };
2553
+ var extractionFields = (block) => {
2554
+ const extraction = readDocumentExtraction(block);
2555
+ return extraction === undefined ? {} : { extraction };
2556
+ };
2557
+
2449
2558
  // src/services/strict-sdd-audit/http-request-detail.ts
2559
+ var HTTP_METHODS = new Set([
2560
+ "GET",
2561
+ "POST",
2562
+ "PUT",
2563
+ "PATCH",
2564
+ "DELETE",
2565
+ "HEAD",
2566
+ "OPTIONS"
2567
+ ]);
2450
2568
  var readHttpRequest = (block) => {
2451
2569
  const read = (label) => {
2452
2570
  const raw = field(block, label);
@@ -2500,79 +2618,6 @@ var resourceReference = (block) => {
2500
2618
  };
2501
2619
  };
2502
2620
 
2503
- // src/services/sdd-condition-cell.ts
2504
- var STAGE_SELECTOR_RULES = new Set([
2505
- "selected-stage-completed",
2506
- "selected-stage-exited"
2507
- ]);
2508
- var TASK_SELECTOR_RULES = new Set(["selected-tasks-completed"]);
2509
- var IF_PARTS = new Map([
2510
- ["conditionexpression", "expression"],
2511
- ["selected tasks", "tasks"],
2512
- ["selected task", "tasks"],
2513
- ["selected stage", "stage"],
2514
- ["selected stages", "stage"]
2515
- ]);
2516
- var splitIfParts = (text) => {
2517
- const pieces = [];
2518
- let current = "";
2519
- let quote;
2520
- for (let index = 0;index < text.length; index += 1) {
2521
- const char = text[index];
2522
- if (quote !== undefined) {
2523
- current += char;
2524
- if (char === "\\") {
2525
- current += text[index + 1] ?? "";
2526
- index += 1;
2527
- } else if (char === quote)
2528
- quote = undefined;
2529
- continue;
2530
- }
2531
- if (char === '"' || char === "'" || char === "`") {
2532
- quote = char;
2533
- current += char;
2534
- continue;
2535
- }
2536
- if (char !== ";") {
2537
- current += char;
2538
- continue;
2539
- }
2540
- pieces.push(current);
2541
- current = "";
2542
- }
2543
- pieces.push(current);
2544
- return pieces;
2545
- };
2546
- var parseIfCell = (cell) => {
2547
- const text = cell.trim();
2548
- const found = {
2549
- expression: ""
2550
- };
2551
- const parts = [];
2552
- let structured = false;
2553
- const partKey = (text2) => text2.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
2554
- for (const piece of splitIfParts(text)) {
2555
- const at = piece.indexOf(":");
2556
- const key = at === -1 ? "" : partKey(piece.slice(0, at));
2557
- const slot = IF_PARTS.get(key);
2558
- if (slot === undefined) {
2559
- parts.push(piece);
2560
- continue;
2561
- }
2562
- structured = true;
2563
- const value = piece.slice(at + 1).trim();
2564
- if (slot === "expression")
2565
- found.expression = value;
2566
- else if (slot === "tasks")
2567
- found.tasks = value;
2568
- else
2569
- found.stage = value;
2570
- }
2571
- if (!structured)
2572
- return { expression: text };
2573
- return found;
2574
- };
2575
-
2576
2621
  // src/services/strict-sdd-audit/task-detail-rows.ts
2577
2622
  var entryConditionBlocks = (section) => {
2578
2623
  const block = section.body;
@@ -2659,6 +2704,7 @@ var declaredCell = (row, at) => {
2659
2704
  const text = unwrapCodeSpan(cellAt(row, at));
2660
2705
  return isBlank(text) ? "" : text;
2661
2706
  };
2707
+ var API_EVENT_RULE = "api-event";
2662
2708
  var ARGUMENT_RULES = new Set([
2663
2709
  ...STAGE_SELECTOR_RULES,
2664
2710
  ...TASK_SELECTOR_RULES,
@@ -2669,6 +2715,8 @@ var labelledWhen = (when, ifCell) => {
2669
2715
  if (match === null)
2670
2716
  return { when };
2671
2717
  const [, rule, label] = match;
2718
+ if (rule === API_EVENT_RULE)
2719
+ return { when };
2672
2720
  if (!ARGUMENT_RULES.has(rule))
2673
2721
  return { when: rule, label };
2674
2722
  const parts = parseIfCell(ifCell);
@@ -2826,15 +2874,21 @@ var detailExtractRows = (block) => {
2826
2874
  const bindingAt = columnIndex(header, "Binding / Value", "Binding", "Value");
2827
2875
  if (fieldAt === undefined || bindingAt === undefined)
2828
2876
  continue;
2877
+ const typeAt = columnIndex(header, "Type");
2829
2878
  for (const row of tableRows) {
2830
- const field2 = stripTicks(cellAt(row, fieldAt).trim());
2879
+ const field = stripTicks(cellAt(row, fieldAt).trim());
2831
2880
  const binding = unwrapCodeSpan(cellAt(row, bindingAt));
2832
- if (!field2 || isBlank(field2) || !binding.startsWith("->"))
2881
+ if (!field || isBlank(field) || !binding.startsWith("->"))
2833
2882
  continue;
2834
2883
  const variable = binding.slice(2).trim();
2835
2884
  if (!variable || isBlank(variable))
2836
2885
  continue;
2837
- rows.push({ field: field2, variable });
2886
+ const type = typeAt === undefined ? "" : stripTicks(cellAt(row, typeAt).trim()).trim();
2887
+ rows.push({
2888
+ field,
2889
+ variable,
2890
+ ...type !== "" && !isBlank(type) && { type }
2891
+ });
2838
2892
  }
2839
2893
  }
2840
2894
  return rows;
@@ -2848,10 +2902,10 @@ var timerFields = (block) => {
2848
2902
  if (!declared || isBlank(declared))
2849
2903
  return {};
2850
2904
  if (TIMER_VALUE_FIELDS.has(norm(declared).replaceAll(" ", ""))) {
2851
- const value2 = stripTicks(field(block, "Value") ?? "");
2905
+ const value = stripTicks(field(block, "Value") ?? "");
2852
2906
  return {
2853
2907
  timerType: declared,
2854
- ...value2 && !isBlank(value2) && { timerValue: value2 }
2908
+ ...value && !isBlank(value) && { timerValue: value }
2855
2909
  };
2856
2910
  }
2857
2911
  const configured = stripTicks(field(block, "Timer Configuration") ?? "");
@@ -2895,15 +2949,6 @@ var resourceNameOf = (block) => {
2895
2949
  }
2896
2950
  return resourceReference(block).name;
2897
2951
  };
2898
- var HTTP_METHODS = new Set([
2899
- "GET",
2900
- "POST",
2901
- "PUT",
2902
- "PATCH",
2903
- "DELETE",
2904
- "HEAD",
2905
- "OPTIONS"
2906
- ]);
2907
2952
  var readConnectorTail = (text, into) => {
2908
2953
  const open = text.lastIndexOf("(");
2909
2954
  const close = text.lastIndexOf(")");
@@ -2944,10 +2989,12 @@ var readRecipient = (block) => {
2944
2989
  if (declared === undefined || isBlank(declared))
2945
2990
  return;
2946
2991
  const text = stripTicks(declared).trim();
2992
+ if (text.startsWith("="))
2993
+ return { kind: "expression", value: text };
2947
2994
  const at = text.indexOf(":");
2948
2995
  if (at === -1)
2949
2996
  return { kind: "", value: text };
2950
- const value = text.slice(at + 1).trim();
2997
+ const value = stripTicks(text.slice(at + 1));
2951
2998
  if (value === "" || isBlank(value))
2952
2999
  return;
2953
3000
  return { kind: norm(text.slice(0, at)), value };
@@ -2993,6 +3040,15 @@ var readTaskDetail = (section) => {
2993
3040
  type: stripTicks(field(block, "Type") ?? ""),
2994
3041
  resolvedResource: field(block, "Resolved Resource"),
2995
3042
  identityResolved: identity.length > 0 && !identity.some(hasUnresolvedMarker),
3043
+ identityStated: (() => {
3044
+ const stated = [
3045
+ field(block, "Resource Identity"),
3046
+ field(block, "Connection ID"),
3047
+ field(block, "Activity Type ID"),
3048
+ field(block, "Action App ID")
3049
+ ].filter((value) => !!value && !isBlank(value));
3050
+ return stated.length > 0 && !stated.some(hasUnresolvedMarker);
3051
+ })(),
2996
3052
  folder: field(block, "Deployment Folder") ?? field(block, "Folder Path") ?? resourceReference(block).folder,
2997
3053
  ...(() => {
2998
3054
  const skip = namedValue(unwrapCodeSpan(field(block, "Skip Condition") ?? columnField(block, "Skip Condition") ?? ""));
@@ -3022,7 +3078,8 @@ var readTaskDetail = (section) => {
3022
3078
  customRows: detailCustomRows(block),
3023
3079
  unreadTables: unreadIoTables(block),
3024
3080
  ...timerFields(block),
3025
- ...httpFields(block)
3081
+ ...httpFields(block),
3082
+ ...extractionFields(block)
3026
3083
  };
3027
3084
  };
3028
3085
  var TIMER_CONFIGURATION_TYPES = new Map([
@@ -3070,8 +3127,8 @@ var literalDifference = (actual, declared) => {
3070
3127
  return "does not match the literal the SDD declares";
3071
3128
  };
3072
3129
  var missingLiteralFields = (declared, actual) => {
3073
- const [declaredError, declaredValue] = catchError(() => JSON.parse(declared));
3074
- const [actualError, actualValue] = catchError(() => JSON.parse(actual));
3130
+ const [declaredError, declaredValue] = catchError2(() => JSON.parse(declared));
3131
+ const [actualError, actualValue] = catchError2(() => JSON.parse(actual));
3075
3132
  if (declaredError || actualError)
3076
3133
  return;
3077
3134
  const declaredRecord = asRecord(declaredValue);
@@ -3090,7 +3147,18 @@ var missingLiteralFields = (declared, actual) => {
3090
3147
  firstMissing: missing[0]
3091
3148
  };
3092
3149
  };
3093
- var literalFindings = (task, found, path, label) => {
3150
+ var pickedValue = (found, actual, rootBindings) => {
3151
+ const id = /^=bindings\.(.+)$/.exec(actual)?.[1];
3152
+ if (id === undefined)
3153
+ return;
3154
+ const taskCopies = Array.isArray(found.data.bindings) ? found.data.bindings.map(asRecord) : [];
3155
+ const binding = rootBindings.find((entry) => entry.id === id) ?? taskCopies.find((entry) => entry?.id === id);
3156
+ return binding?.resource !== "Connection" && typeof binding?.default === "string" ? binding.default : undefined;
3157
+ };
3158
+ var pickedFindings = (declared, picked, path, label) => sameLiteral(declared.value, picked) ? [] : [
3159
+ warning("STRICT_SDD_LITERAL_MISMATCH", `${path}.data.inputs[${declared.name}]`, `${label}: input "${declared.name}" picks a resource that ${literalDifference(picked, declared.value)}. Re-read the Inputs row for "${declared.name}" and resolve the picker to the resource it names.`)
3160
+ ];
3161
+ var literalFindings = (task, found, rootBindings, path, label) => {
3094
3162
  const findings = [];
3095
3163
  for (const declared of task.literalInputs) {
3096
3164
  const carried = carriedInput(found, declared.name);
@@ -3099,6 +3167,11 @@ var literalFindings = (task, found, path, label) => {
3099
3167
  const actual = carried.value;
3100
3168
  if (typeof actual !== "string")
3101
3169
  continue;
3170
+ const picked = pickedValue(found, actual, rootBindings);
3171
+ if (picked !== undefined) {
3172
+ findings.push(...pickedFindings(declared, picked, path, label));
3173
+ continue;
3174
+ }
3102
3175
  if (actual.startsWith("=")) {
3103
3176
  findings.push(error("STRICT_SDD_LITERAL_MISMATCH", `${path}.data.inputs[${declared.name}]`, `${label}: input "${declared.name}" carries an expression where the SDD's Inputs row declares a literal value. Re-read that row and set the literal it gives.`));
3104
3177
  continue;
@@ -3116,181 +3189,41 @@ var literalFindings = (task, found, path, label) => {
3116
3189
  return findings;
3117
3190
  };
3118
3191
 
3119
- // src/services/strict-sdd-audit/selector-targets.ts
3120
- var idsIn = (value) => {
3121
- const values = Array.isArray(value) ? value : [value];
3122
- return values.filter((entry) => typeof entry === "string" && entry.trim() !== "");
3123
- };
3124
- var selectedIds = (rule) => Object.entries(rule ?? {}).filter(([key]) => key.startsWith("selected")).flatMap(([, value]) => idsIn(value));
3125
- var hasSelector = (rule) => selectedIds(rule).length > 0 || idsIn(rule?.slaId).length > 0;
3126
- var ruleTargets = (rule, resolve, same) => {
3127
- const named = selectedIds(rule).map((id) => resolve.stage(id) ?? resolve.task(id)).filter((name) => name !== undefined);
3128
- const owners = idsIn(rule?.slaId).map((id) => resolve.sla?.(id)).filter((name) => name !== undefined);
3129
- if (named.length === 0)
3130
- return { names: owners };
3131
- const conflict = owners.find((owner) => !named.some((name) => same(name, owner)));
3132
- return conflict === undefined ? { names: named } : { names: named, conflict };
3133
- };
3134
-
3135
- // src/services/strict-sdd-audit/selectors.ts
3136
- var sameLabel = (a, b) => norm(a) === norm(b) || normAnnotationless(a) === normAnnotationless(b);
3137
- var leadingSegment = (name) => {
3138
- for (const dash of [" - ", " – ", " — "]) {
3139
- const at = name.indexOf(dash);
3140
- if (at > 0)
3141
- return name.slice(0, at);
3142
- }
3143
- return name;
3144
- };
3145
- var targetsOf = (rule, selectors) => norm(rule) === norm("sla-status-change") ? selectors.slice(0, 1) : selectors;
3146
- var undeclaredGateFinding = (row, planRules, path, label, resolve) => {
3147
- if (row.ungated !== true || row.when === undefined)
3148
- return;
3149
- const { rule, selectors: named } = whenRule(row.when);
3150
- if (!rule || isBlank(rule))
3151
- return;
3152
- const selectors = targetsOf(rule, named);
3153
- const belongsToRow = (candidate) => {
3154
- if (selectors.length === 0)
3155
- return !hasSelector(candidate);
3156
- if (!hasSelector(candidate))
3157
- return false;
3158
- if (resolve === undefined)
3159
- return false;
3160
- const pointsAt = ruleTargets(candidate, resolve, sameLabel).names;
3161
- if (pointsAt.length === 0)
3162
- return false;
3163
- return selectors.some((want) => {
3164
- const denotes = resolve.label(want);
3165
- return pointsAt.some((got) => sameLabel(got, want) || denotes !== undefined && got === denotes);
3166
- });
3167
- };
3168
- const gated = planRules.filter((candidate) => {
3169
- if (norm(candidate?.rule) !== norm(rule))
3170
- return false;
3171
- if (!belongsToRow(candidate))
3172
- return false;
3173
- const expression = candidate?.conditionExpression;
3174
- return typeof expression === "string" && !isBlank(expression);
3175
- });
3176
- if (gated.length === 0)
3177
- return;
3178
- const expressions = [
3179
- ...new Set(gated.map((candidate) => String(candidate?.conditionExpression)).filter((expression) => expression !== ""))
3180
- ];
3181
- const on = selectors.length > 0 ? ` on "${selectors.join('", "')}"` : "";
3182
- return error("STRICT_SDD_CONDITION_EXPRESSION_UNDECLARED", `${path}[${row.displayName}].conditionExpression`, `${label}: the SDD row ${rowLabel(row)} declares WHEN \`${rule}\`${on} with no IF gate, and the caseplan's \`${rule}\` carries \`${expressions.join("`, `")}\`. A gate the SDD does not declare narrows the transition to one branch, so it fires where the row says it should fire every time. Remove the expression, or add the IF cell that justifies it.`);
3183
- };
3184
- var whenCellFinding = (row, planRules, path, label, resolve, ownRules) => {
3185
- if (row.when === undefined)
3186
- return;
3187
- const { rule, selectors: named } = whenRule(row.when);
3188
- if (!rule || isBlank(rule))
3189
- return;
3190
- const wanted = targetsOf(rule, named);
3191
- const matching = planRules.filter((r) => norm(r?.rule) === norm(rule));
3192
- if (matching.length === 0) {
3193
- return error("STRICT_SDD_CONDITION_RULE_MISMATCH", `${path}[${row.displayName}].rule`, `${label}: the SDD row ${rowLabel(row)} declares WHEN \`${rule}\`, no rule on it uses that predicate. A different predicate starts the task at a different moment. Re-read that row's WHEN cell.`);
3194
- }
3195
- if (wanted.length === 0)
3196
- return;
3197
- const own = (ownRules ?? []).filter((r) => norm(r?.rule) === norm(rule));
3198
- const targeted = own.length > 0 ? own : matching;
3199
- if (!targeted.some(hasSelector)) {
3200
- return error("STRICT_SDD_CONDITION_SELECTOR_MISSING", `${path}[${row.displayName}].rule`, `${label}: the SDD row ${rowLabel(row)} declares WHEN \`${rule}\` on "${wanted.join('", "')}", and the caseplan's \`${rule}\` carries no target. The predicate then refers to nothing, so it never becomes true and the gate never opens. Set the rule's \`selected*\` field to the target the row names.`);
3201
- }
3202
- if (resolve === undefined)
3203
- return;
3204
- const resolved = targeted.map((candidate) => ruleTargets(candidate, resolve, sameLabel));
3205
- const pointsAt = resolved.flatMap((target) => target.names);
3206
- if (pointsAt.length === 0)
3207
- return;
3208
- const missed = wanted.filter((want) => {
3209
- const denotes = resolve.label(want);
3210
- return !pointsAt.some((got) => sameLabel(got, want) || denotes !== undefined && got === denotes);
3211
- });
3212
- const conflict = resolved.find((target) => target.conflict !== undefined);
3213
- if (missed.length === 0 && conflict?.conflict !== undefined) {
3214
- return error("STRICT_SDD_CONDITION_SELECTOR_MISMATCH", `${path}[${row.displayName}].rule`, `${label}: the SDD row ${rowLabel(row)} declares WHEN \`${rule}\` on "${wanted.join('", "')}", and the caseplan's \`${rule}\` names "${conflict.names.join('", "')}" but its \`slaId\` is the SLA of "${conflict.conflict}". The rule watches one stage's SLA while naming another, so it fires on a deadline the row did not ask for. Point \`slaId\` at the SLA of "${wanted.join('", "')}" and drop the \`selected*\` field, which convert does not write for this rule.`);
3192
+ // src/services/strict-sdd-audit/recipients.ts
3193
+ var recipientFindings = (task, found, path, label) => {
3194
+ if (task.recipient === undefined)
3195
+ return [];
3196
+ const carried = found.data.recipient;
3197
+ if (carried === undefined) {
3198
+ return [
3199
+ error("STRICT_SDD_TASK_RECIPIENT_MISSING", `${path}.data.recipient`, `${label}: the SDD's \`**Recipient:**\` line names "${task.recipient.value}" and the caseplan task carries no \`data.recipient\`. \`case validate\` does not check this field's presence, so a dropped recipient passes the build and the task is created with no assignee. Add \`data.recipient\` as the \`{Type, Value}\` object the SDD's recipient line resolves to.`)
3200
+ ];
3215
3201
  }
3216
- if (missed.length === 0)
3217
- return;
3218
- return error("STRICT_SDD_CONDITION_SELECTOR_MISMATCH", `${path}[${row.displayName}].rule`, `${label}: the SDD row ${rowLabel(row)} declares WHEN \`${rule}\` on "${missed.join('", "')}", and the caseplan's \`${rule}\` points at "${pointsAt.join('", "')}". A selector that resolves to the wrong target builds a transition the SDD never asked for and leaves the declared one absent, which no count or predicate check can see.`);
3219
- };
3220
- var conditionRuleFindings = (declared, planConditions, path, label, resolve) => {
3221
- if (declared.length === 0)
3202
+ if (task.recipient.kind !== "expression")
3222
3203
  return [];
3223
- const conditions = planConditions.map(asRecord);
3224
- const rulesOf = (of) => of.flatMap((condition) => asArray(condition?.rules).flatMap((group) => asArray(group).map(asRecord)));
3225
- const planRules = rulesOf(conditions);
3226
- if (planRules.length === 0)
3204
+ const recipient = asRecord(carried);
3205
+ if (recipient === undefined)
3206
+ return [];
3207
+ const value = recipient.Value;
3208
+ if (recipient.Type === 3 && typeof value === "string" && value.startsWith("=")) {
3227
3209
  return [];
3228
- const owners = bindByName(declared, conditions);
3229
- const taken = new Set(owners.filter((one) => one !== undefined));
3230
- const openRows = declared.map((row, at) => ({ row, at })).filter(({ at }) => owners[at] === undefined);
3231
- const openConditions = conditions.filter((one) => !taken.has(one));
3232
- const gated = (condition) => rulesOf([condition]).some((rule) => {
3233
- const expression = rule?.conditionExpression;
3234
- return typeof expression === "string" && !isBlank(expression);
3235
- });
3236
- const rowGated = (row) => row.ungated === true ? false : row.ifExpression !== undefined ? true : undefined;
3237
- const shapeKey = (rule, isGated) => {
3238
- const name = norm(rule);
3239
- if (name === "" || isGated === undefined)
3240
- return;
3241
- return `${name}|${isGated ? "gated" : "open"}`;
3242
- };
3243
- for (const [rowAt, conditionAt] of pairLeftovers(openRows.map(({ row }) => shapeKey(whenRule(row.when ?? "").rule, rowGated(row))), openConditions.map((condition) => {
3244
- const rules = new Set(rulesOf([condition]).map((r) => norm(r?.rule)));
3245
- return rules.size === 1 ? shapeKey([...rules][0], gated(condition)) : undefined;
3246
- }))) {
3247
- owners[openRows[rowAt].at] = openConditions[conditionAt];
3248
3210
  }
3249
- const unownedBy = (at) => {
3250
- const others = new Set(owners.filter((one, index) => index !== at && one !== undefined));
3251
- return rulesOf(conditions.filter((one) => !others.has(one)));
3252
- };
3253
- return declared.flatMap((row, at) => [
3254
- whenCellFinding(row, planRules, path, label, resolve, owners[at] === undefined ? undefined : rulesOf([owners[at]])),
3255
- undeclaredGateFinding(row, unownedBy(at), path, label, resolve)
3256
- ]).filter((finding) => finding !== undefined);
3211
+ return [
3212
+ error("STRICT_SDD_TASK_RECIPIENT_EXPRESSION", `${path}.data.recipient`, `${label}: the SDD's \`**Recipient:**\` line is the expression "${task.recipient.value}", and the caseplan task carries \`{Type: ${String(recipient.Type)}, Value: ${JSON.stringify(value)}}\`. The engine assigns a \`Type 3\` recipient to the user or group its value evaluates to; any other Type, or a value that is not an \`=\` expression, sends the literal text to a lookup and the task reaches nobody. ${expressionFix(task.recipient.value)}`)
3213
+ ];
3257
3214
  };
3258
- var CONDITION_FLAGS = [
3259
- ["interrupting", "isInterrupting", "Interrupting"],
3260
- ["marksComplete", "marksStageComplete", "Marks Stage Complete"]
3261
- ];
3262
- var conditionShapeFindings = (declared, planConditions, path, label) => {
3263
- const findings = [];
3264
- const bound = bindConditions(declared, planConditions);
3265
- for (const [at, row] of declared.entries()) {
3266
- const { match, shaped } = bound[at];
3267
- if (match === undefined)
3268
- continue;
3269
- const planName = String(match.displayName ?? "");
3270
- const where = shaped ? planName : row.displayName;
3271
- const which = shaped ? `the condition "${planName}", the only one in this scope no row names that holds this row's rule,` : "the condition";
3272
- if (row.exitType !== undefined) {
3273
- const has = typeof match.type === "string" && match.type !== "";
3274
- const effective = norm(has ? match.type : "exit-only");
3275
- if (effective !== norm(row.exitType)) {
3276
- findings.push(error("STRICT_SDD_CONDITION_EXIT_TYPE_MISMATCH", `${path}[${where}].type`, `${label}: the SDD row ${rowLabel(row)} declares Exit Type "${row.exitType}", ${which} has ${has ? `"${String(match.type)}"` : "no `type` at all, which the runtime reads as exit-only"}. The exit type decides where the case goes when the gate opens, so this sends it somewhere else${has ? "" : " — check for an invented key like `exitType` holding the value `type` should carry"}.`));
3277
- }
3278
- }
3279
- for (const [cell, field2, title] of CONDITION_FLAGS) {
3280
- const want = row[cell];
3281
- const got = typeof match[field2] === "boolean" ? match[field2] : false;
3282
- if (want === undefined)
3283
- continue;
3284
- if (want === got)
3285
- continue;
3286
- findings.push(error("STRICT_SDD_CONDITION_FLAG_MISMATCH", `${path}[${where}].${field2}`, `${label}: the SDD row ${rowLabel(row)} declares ${title}=${want ? "Yes" : "No"}, ${which} has ${typeof match[field2] === "boolean" ? String(got) : "no such field, which the runtime reads as false"}.`));
3287
- }
3215
+ var expressionFix = (sddValue) => {
3216
+ if (sddValue.startsWith("=") && !sddValue.includes("$xref(")) {
3217
+ return `Write \`{"Type": 3, "Value": ${JSON.stringify(sddValue)}}\`.`;
3288
3218
  }
3289
- return findings;
3219
+ if (sddValue.includes("$xref(")) {
3220
+ return "Write `Type: 3` with the expression's `$xref(...)` replaced by the producing output's `vars.<id>`, as `sdd convert` does.";
3221
+ }
3222
+ return "Write `Type: 3` with an `=` expression for it (`=vars.<variable>`), and fix the SDD line to `Expression: =vars.<variable>` so the next convert writes it.";
3290
3223
  };
3291
3224
 
3292
3225
  // src/services/strict-sdd-audit/compare-task.ts
3293
- var compareTask = (plan, task, found, stagePath, label, selectors, ledger, stageSpellings2 = [], once = new Set) => {
3226
+ var compareTask = (plan, task, found, stagePath, label, selectors, ledger, stageSpellings = [], once = new Set) => {
3294
3227
  const issues = [];
3295
3228
  const taskPath = `${stagePath}.data.tasks[${found.name ?? task.name}]`;
3296
3229
  if (task.type && found.type && norm(task.type) !== norm(found.type)) {
@@ -3309,7 +3242,7 @@ var compareTask = (plan, task, found, stagePath, label, selectors, ledger, stage
3309
3242
  issues.push(error("STRICT_SDD_ENTRY_RULE_EMPTY", `${taskPath}.entryConditions`, `${label}: entryConditions carry no rules.`));
3310
3243
  }
3311
3244
  const isPlaceholder = Object.keys(found.data).length === 0;
3312
- const miss = isPlaceholder ? ledgerMissFor(ledger, stageSpellings2, task.name) : undefined;
3245
+ const miss = isPlaceholder ? ledgerMissFor(ledger, stageSpellings, task.name) : undefined;
3313
3246
  if (isPlaceholder && task.identityResolved && miss !== undefined) {
3314
3247
  issues.push(warning("STRICT_SDD_PLACEHOLDER_UNRESOLVABLE", `${taskPath}.data`, `${label}: placeholder task, and the run's registry ledger records no match for "${miss.query}"${miss.cacheFile ? ` in ${miss.cacheFile}` : ""} — so the placeholder is correct and the SDD names a resource this tenant does not have. Resolve it on a tenant that does, or leave the placeholder.`));
3315
3248
  } else if (isPlaceholder && task.identityResolved) {
@@ -3318,13 +3251,17 @@ var compareTask = (plan, task, found, stagePath, label, selectors, ledger, stage
3318
3251
  } else if (isPlaceholder) {
3319
3252
  issues.push(warning("STRICT_SDD_PLACEHOLDER_TASK", `${taskPath}.data`, `${label}: placeholder task (empty \`data\`) — the SDD leaves its resource unresolved.`));
3320
3253
  }
3254
+ if (!isPlaceholder && task.identityStated !== true && task.resourceName !== undefined && bindingId(found.data.name) !== undefined && !ledgerFoundFor(ledger, stageSpellings, task.name, {
3255
+ name: namedValue(task.resourceName),
3256
+ folder: namedValue(task.folder)
3257
+ })) {
3258
+ issues.push(warning("STRICT_SDD_RESOURCE_UNVERIFIED", `${taskPath}.data.name`, `${label}: bound to "${task.resourceName}", which the SDD names without a Resource Identity, and no registry ledger records finding it — so nothing checked that this ${task.type || "resource"} exists on the tenant. A binding to a resource that does not exist passes validate and faults at \`case debug\`. Run \`uip maestro case registry pull\`, then \`uip maestro case sdd resolve <sdd> --out tasks/registry-resolved.json\` from the caseplan's folder (validate reads the ledger only from a file of that name beside the caseplan or above it): bind it if it is found, and use a placeholder (empty \`data\`) if it is not.`));
3259
+ }
3321
3260
  issues.push(...conditionRuleFindings(task.entryConditions, found.entry, `${taskPath}.entryConditions`, label, selectors), ...conditionNameFindings(task.entryConditions, found.entry, `${taskPath}.entryConditions`, label, once), ...envelopeFindings(task, found, taskPath, label));
3322
3261
  if (isPlaceholder)
3323
3262
  return issues;
3324
- if (task.recipient !== undefined && found.data.recipient === undefined) {
3325
- issues.push(error("STRICT_SDD_TASK_RECIPIENT_MISSING", `${taskPath}.data.recipient`, `${label}: the SDD's \`**Recipient:**\` line names "${task.recipient.value}" and the caseplan task carries no \`data.recipient\`. \`case validate\` does not check this field's presence, so a dropped recipient passes the build and the task is created with no assignee. Add \`data.recipient\` as the \`{Type, Value}\` object the SDD's recipient line resolves to.`));
3326
- }
3327
- issues.push(...bindingFindings(plan, found, taskPath, label, namedValue(task.folder) || undefined), ...literalFindings(task, found, taskPath, label), ...boundInputFindings(task, found, taskPath, label), ...timerFindings(task, found, taskPath, label), ...wiringFindings(task, found, taskPath, label), ...producerFindings(task, found, plan, taskPath, label), ...emptyInputFindings(task, found, taskPath, label), ...expressionShapeFindings(task, found, plan, taskPath, label), ...outputRowFindings(task, found, taskPath, label), ...extractFindings(task, found, taskPath, label), ...customOutputFindings(task, found, taskPath, label));
3263
+ issues.push(...recipientFindings(task, found, taskPath, label));
3264
+ issues.push(...bindingFindings(plan, found, taskPath, label, namedValue(task.folder) || undefined), ...literalFindings(task, found, plan.bindings, taskPath, label), ...boundInputFindings(task, found, taskPath, label), ...timerFindings(task, found, taskPath, label), ...wiringFindings(task, found, taskPath, label), ...producerFindings(task, found, plan, taskPath, label), ...emptyInputFindings(task, found, taskPath, label), ...expressionShapeFindings(task, found, plan, taskPath, label), ...outputRowFindings(task, found, taskPath, label), ...extractFindings(task, found, taskPath, label), ...customOutputFindings(task, found, taskPath, label), ...outputTypeFindings(task, found, taskPath, label));
3328
3265
  return issues;
3329
3266
  };
3330
3267
 
@@ -3332,7 +3269,7 @@ var compareTask = (plan, task, found, stagePath, label, selectors, ledger, stage
3332
3269
  var compareStage = (plan, stage, planStage, selectors, ledger, stagePosition, once = new Set, sddStages = [stage], references = []) => {
3333
3270
  const stagePath = `nodes[${planStage.name ?? stage.name}]`;
3334
3271
  const ordinal = stagePosition === undefined ? undefined : `Stage ${stagePosition + 1}`;
3335
- const stageSpellings2 = [
3272
+ const stageSpellings = [
3336
3273
  stage.name,
3337
3274
  ordinal,
3338
3275
  ordinal === undefined ? undefined : `${ordinal}: ${stage.name}`,
@@ -3352,7 +3289,7 @@ var compareStage = (plan, stage, planStage, selectors, ledger, stagePosition, on
3352
3289
  if (stage.hasSla && stageDefaults.length > 1) {
3353
3290
  issues.push(error("STRICT_SDD_SLA_EXTRA", `${stagePath}.data.slaRules`, `${stageLabel}: the SDD declares one Stage SLA and ${stageDefaults.length} SLA rules are present. Its escalations belong to that one SLA, so splitting them across rules leaves each with part of it and the runtime applies whichever rule it reaches — the other escalations are lost. Carry one rule with every escalation the table declares.`));
3354
3291
  }
3355
- issues.push(...conditionRuleFindings(stage.entryConditions, planStage.entry, `${stagePath}.data.entryConditions`, stageLabel, selectors), ...conditionRuleFindings(stage.exitConditions, planStage.exit, `${stagePath}.data.exitConditions`, stageLabel, selectors), ...conditionShapeFindings(stage.entryConditions, planStage.entry, `${stagePath}.data.entryConditions`, stageLabel), ...conditionShapeFindings(stage.exitConditions, planStage.exit, `${stagePath}.data.exitConditions`, stageLabel), ...conditionNameFindings(stage.entryConditions, planStage.entry, `${stagePath}.data.entryConditions`, stageLabel, once), ...conditionNameFindings(stage.exitConditions, planStage.exit, `${stagePath}.data.exitConditions`, stageLabel, once), ...conditionExpressionFindings(stage.entryConditions, planStage.entry, plan, `${stagePath}.data.entryConditions`, stageLabel), ...conditionExpressionFindings(stage.exitConditions, planStage.exit, plan, `${stagePath}.data.exitConditions`, stageLabel), ...escalationFindings(stage.slaEscalations, planStage.sla, `${stagePath}.data.slaRules`, stageLabel, stage.slaResponses));
3292
+ issues.push(...conditionRuleFindings(stage.entryConditions, planStage.entry, `${stagePath}.data.entryConditions`, stageLabel, selectors), ...conditionRuleFindings(stage.exitConditions, planStage.exit, `${stagePath}.data.exitConditions`, stageLabel, selectors), ...conditionShapeFindings(stage.entryConditions, planStage.entry, `${stagePath}.data.entryConditions`, stageLabel), ...conditionShapeFindings(stage.exitConditions, planStage.exit, `${stagePath}.data.exitConditions`, stageLabel), ...undeclaredHandoffFindings(stage, planStage.exit, new Map(plan.stages.flatMap((one) => one.id !== undefined && one.name !== undefined ? [[one.id, one.name]] : [])), `${stagePath}.data.exitConditions`, stageLabel), ...conditionNameFindings(stage.entryConditions, planStage.entry, `${stagePath}.data.entryConditions`, stageLabel, once), ...conditionNameFindings(stage.exitConditions, planStage.exit, `${stagePath}.data.exitConditions`, stageLabel, once), ...conditionExpressionFindings(stage.entryConditions, planStage.entry, plan, `${stagePath}.data.entryConditions`, stageLabel), ...conditionExpressionFindings(stage.exitConditions, planStage.exit, plan, `${stagePath}.data.exitConditions`, stageLabel), ...escalationFindings(stage.slaEscalations, planStage.sla, `${stagePath}.data.slaRules`, stageLabel, stage.slaResponses));
3356
3293
  const bound = resolveTasks(stage.tasks, planStage.tasks);
3357
3294
  const unboundRows = stage.tasks.map((_, row) => row).filter((row) => bound[row] === undefined);
3358
3295
  const unboundTasks = planStage.tasks.map((_, index) => index).filter((index) => !bound.includes(index));
@@ -3370,7 +3307,7 @@ var compareStage = (plan, stage, planStage, selectors, ledger, stagePosition, on
3370
3307
  issues.push(error("STRICT_SDD_TASK_MISSING", `${stagePath}.data.tasks[${task.name}]`, `${label} is declared in the SDD and absent from the caseplan.`));
3371
3308
  return;
3372
3309
  }
3373
- issues.push(...compareTask(plan, task, planStage.tasks[taskIndex], stagePath, label, selectors, ledger, stageSpellings2, once));
3310
+ issues.push(...compareTask(plan, task, planStage.tasks[taskIndex], stagePath, label, selectors, ledger, stageSpellings, once));
3374
3311
  });
3375
3312
  planStage.tasks.forEach((extra, index) => {
3376
3313
  if (bound.includes(index))
@@ -3415,7 +3352,7 @@ var compare = (sdd, plan, ledger) => {
3415
3352
  }
3416
3353
  };
3417
3354
  const paired = new Set;
3418
- for (const [s, p] of pairByEvidence(unbound.length, leftover.length, (s2, p2) => evidence(sdd.stages[unbound[s2]], plan.stages[leftover[p2]]) !== undefined)) {
3355
+ for (const [s, p] of pairByEvidence(unbound.length, leftover.length, (s, p) => evidence(sdd.stages[unbound[s]], plan.stages[leftover[p]]) !== undefined)) {
3419
3356
  const sddIndex = unbound[s];
3420
3357
  const at = leftover[p];
3421
3358
  const stage = sdd.stages[sddIndex];
@@ -3499,6 +3436,89 @@ class SddNameCollisionError extends Error {
3499
3436
  }
3500
3437
  }
3501
3438
 
3439
+ // src/services/sdd-personas-table.ts
3440
+ var PERSONA_NAME_COLUMNS = [
3441
+ "Persona",
3442
+ "Role",
3443
+ "Case Role",
3444
+ "Name"
3445
+ ];
3446
+ var WHOLE_CASE_CELLS = new Set(["all", "case", "all stages", "whole case"]);
3447
+ var splitList = (cell) => cell.split(/[,;]/).map((part) => stripTicks(part.trim()).trim()).filter((part) => part !== "" && !isBlank(part));
3448
+ var EMPTY = { personas: [], notes: [] };
3449
+ var personasOf = (block) => {
3450
+ const tables = pipeTables(block).map(firstTable).filter(({ header }) => columnIndex(header, ...PERSONA_NAME_COLUMNS) !== undefined);
3451
+ const table = tables.find(({ rows }) => rows.length > 0) ?? tables[0];
3452
+ if (table === undefined)
3453
+ return EMPTY;
3454
+ const { header, rows } = table;
3455
+ const nameAt = columnIndex(header, ...PERSONA_NAME_COLUMNS);
3456
+ const scopeAt = columnIndex(header, "Scope");
3457
+ const stagesAt = columnIndex(header, "Stages", "Stage Scope", "Stage");
3458
+ const permissionsAt = columnIndex(header, "Permissions", "Actions");
3459
+ const descriptionAt = columnIndex(header, "Description");
3460
+ const hasScopeColumn = scopeAt !== undefined;
3461
+ const templateShape = hasScopeColumn && permissionsAt !== undefined;
3462
+ const personas = [];
3463
+ const notes = [];
3464
+ for (const [at, row] of rows.entries()) {
3465
+ const name = stripTicks(cellAt(row, nameAt).trim()).trim();
3466
+ if (name === "" || isBlank(name)) {
3467
+ notes.push(`Personas row ${at + 1} has no name and is not read; a persona becomes a role by its name`);
3468
+ continue;
3469
+ }
3470
+ const stagesCell = cellAt(row, stagesAt).trim();
3471
+ const scopeCell = stripTicks(cellAt(row, scopeAt).trim()).trim();
3472
+ const legacyWholeCase = !hasScopeColumn && (stagesCell === "" || isBlank(stagesCell) || WHOLE_CASE_CELLS.has(stagesCell.toLowerCase()));
3473
+ const description = cellAt(row, descriptionAt).trim();
3474
+ personas.push({
3475
+ name,
3476
+ ...hasScopeColumn ? { scope: scopeCell } : { scope: legacyWholeCase ? "Case" : "Stages" },
3477
+ stages: legacyWholeCase ? [] : splitList(stagesCell),
3478
+ permissions: splitList(cellAt(row, permissionsAt)),
3479
+ ...description !== "" && !isBlank(description) && { description },
3480
+ templateShape
3481
+ });
3482
+ }
3483
+ return { personas, notes };
3484
+ };
3485
+
3486
+ // src/services/strict-sdd-audit/business-events-read.ts
3487
+ var readBusinessEvents = (sdd, title, body) => {
3488
+ const { header, rows } = firstTable(body);
3489
+ const wanted = ["Name", "Event", "Business Event"];
3490
+ const nameAt = columnIndex(header, ...wanted);
3491
+ if (nameAt === undefined) {
3492
+ if (rows.length > 0) {
3493
+ sdd.parseNotes.push(noNameColumnNote(title, wanted, header));
3494
+ }
3495
+ return;
3496
+ }
3497
+ const descriptionAt = columnIndex(header, "Description");
3498
+ const payloadAt = columnIndex(header, "Payload", "Schema", "Payload Schema");
3499
+ const seen = new Set;
3500
+ for (const [at, row] of rows.entries()) {
3501
+ const name = stripTicks(cellAt(row, nameAt)).trim();
3502
+ if (name === "" || isBlank(name)) {
3503
+ sdd.parseNotes.push(`section "${title}": row ${at + 1} has no Name, so no business event was declared for it`);
3504
+ continue;
3505
+ }
3506
+ if (seen.has(name)) {
3507
+ sdd.parseNotes.push(`section "${title}": business event "${name}" is declared twice; rules match on the name, so only the first row is kept`);
3508
+ continue;
3509
+ }
3510
+ seen.add(name);
3511
+ const description = cellAt(row, descriptionAt).trim();
3512
+ const payload = stripTicks(cellAt(row, payloadAt)).trim();
3513
+ sdd.businessEvents.push({
3514
+ row: at + 1,
3515
+ name,
3516
+ ...description !== "" && !isBlank(description) && { description },
3517
+ ...payload !== "" && !isBlank(payload) && { payload }
3518
+ });
3519
+ }
3520
+ };
3521
+
3502
3522
  // src/services/strict-sdd-audit/canvas-notes-read.ts
3503
3523
  var cellsOf = (line) => splitRowCells(line.trim().replace(/^\|/, "").replace(/\|$/, ""));
3504
3524
  var noteTable = (table) => {
@@ -3541,9 +3561,8 @@ var readCanvasNotes = (sdd, title, body) => {
3541
3561
  }
3542
3562
  };
3543
3563
  var readCanvasNotesSection = (sdd, section, seen) => {
3544
- const { title, body } = section;
3545
- const own = section.children.length > 0 ? body.slice(0, section.children[0].start - section.contentStart) : body;
3546
- const tables = pipeTables(own).filter((table) => noteTable(table).rows.some((row) => row.some((cell) => cell.trim() !== "")));
3564
+ const { title } = section;
3565
+ const tables = pipeTables(ownBodyOf(section)).filter((table) => noteTable(table).rows.some((row) => row.some((cell) => cell.trim() !== "")));
3547
3566
  if (tables.length === 0)
3548
3567
  return seen;
3549
3568
  const later = seen ? tables.length : tables.length - 1;
@@ -3602,6 +3621,9 @@ var applyStageSection = (stage, title, block, notes) => {
3602
3621
  const merged = addConditionTable(stage.exitRows, stage.exitConditions, block);
3603
3622
  stage.exitRows = merged.rows;
3604
3623
  stage.exitConditions = merged.conditions;
3624
+ if (columnIndex(firstTable(block).header, "Exit-To Stage", "Target Stage") !== undefined) {
3625
+ stage.exitToColumn = true;
3626
+ }
3605
3627
  } else if (headingIs(title, STAGE_CONDITIONAL_SLA_HEADINGS)) {
3606
3628
  stage.conditionalSlas = conditionalSlaRows(block);
3607
3629
  } else if (headingIs(title, STAGE_SLA_HEADINGS)) {
@@ -3712,16 +3734,16 @@ var parseStage = (name, section, notes) => {
3712
3734
  });
3713
3735
  }
3714
3736
  }
3715
- const tasksUnder = (key, name2) => stage.tasks.filter((other) => key(other.name) === key(name2)).length;
3737
+ const tasksUnder = (key, name) => stage.tasks.filter((other) => key(other.name) === key(name)).length;
3716
3738
  const exactSeen = new Map;
3717
- const detailFor = (name2) => {
3718
- const candidates = details.get(norm(name2)) ?? [];
3719
- if (candidates.length === 1 && tasksUnder(norm, name2) === 1) {
3739
+ const detailFor = (name) => {
3740
+ const candidates = details.get(norm(name)) ?? [];
3741
+ if (candidates.length === 1 && tasksUnder(norm, name) === 1) {
3720
3742
  return candidates[0].detail;
3721
3743
  }
3722
- const exact = candidates.filter((c) => c.name === name2.trim());
3723
- const nth = exactSeen.get(name2.trim()) ?? 0;
3724
- exactSeen.set(name2.trim(), nth + 1);
3744
+ const exact = candidates.filter((c) => c.name === name.trim());
3745
+ const nth = exactSeen.get(name.trim()) ?? 0;
3746
+ exactSeen.set(name.trim(), nth + 1);
3725
3747
  return exact[nth]?.detail;
3726
3748
  };
3727
3749
  for (const task of stage.tasks) {
@@ -3745,6 +3767,7 @@ var parseStage = (name, section, notes) => {
3745
3767
  task.unreadTables = detail.unreadTables;
3746
3768
  task.resolvedResource = detail.resolvedResource;
3747
3769
  task.identityResolved = detail.identityResolved;
3770
+ task.identityStated = detail.identityStated;
3748
3771
  task.folder = detail.folder;
3749
3772
  task.proseLabel = detail.proseLabel;
3750
3773
  task.prose = detail.prose;
@@ -3770,6 +3793,8 @@ var parseStage = (name, section, notes) => {
3770
3793
  task.type = detail.type;
3771
3794
  if (detail.http !== undefined && String(task.type ?? "").trim().toLowerCase() === "http-request")
3772
3795
  task.http = detail.http;
3796
+ if (detail.extraction !== undefined && String(task.type ?? "").trim().toLowerCase() === "document-extraction")
3797
+ task.extraction = detail.extraction;
3773
3798
  }
3774
3799
  const empty = {
3775
3800
  conditions: !(stage.entryRows && stage.exitRows),
@@ -3788,7 +3813,7 @@ var parseStage = (name, section, notes) => {
3788
3813
  };
3789
3814
 
3790
3815
  // src/services/strict-sdd-audit/sdd-parse.ts
3791
- var applyCaseSection = (sdd, title, body, headingLine, declaredNames) => {
3816
+ var applyCaseSection = (sdd, title, body, headingLine, declaredNames, own = body) => {
3792
3817
  if (headingIs(title, CASE_EXIT_HEADINGS)) {
3793
3818
  const merged = addConditionTable(sdd.caseExitRows, sdd.caseExitConditions, body);
3794
3819
  sdd.caseExitRows = merged.rows;
@@ -3882,6 +3907,13 @@ var applyCaseSection = (sdd, title, body, headingLine, declaredNames) => {
3882
3907
  }
3883
3908
  }
3884
3909
  }
3910
+ } else if (headingIs(title, PERSONA_HEADINGS)) {
3911
+ const { personas, notes } = personasOf(own);
3912
+ sdd.personas.push(...personas);
3913
+ sdd.personaNotes.push(...notes);
3914
+ sdd.parseNotes.push(...notes.map((note) => `section "${title}": ${note}`));
3915
+ } else if (headingIs(title, BUSINESS_EVENT_HEADINGS)) {
3916
+ readBusinessEvents(sdd, title, body);
3885
3917
  } else if (headingIs(title, CASE_VARIABLE_HEADINGS)) {
3886
3918
  sdd.sawVariableSection = true;
3887
3919
  const { header, rows, rowLines } = firstTable(body);
@@ -3915,7 +3947,7 @@ var applyCaseSection = (sdd, title, body, headingLine, declaredNames) => {
3915
3947
  sdd.variableBodies.set(norm(name), bodySchema);
3916
3948
  }
3917
3949
  const category = norm(cellAt(row, categoryAt));
3918
- if (VARIABLE_PLACEMENT.has(category)) {
3950
+ if (VARIABLE_PLACEMENT2.has(category)) {
3919
3951
  sdd.variableCategories.set(norm(name), category);
3920
3952
  }
3921
3953
  const boundTrigger = stripTicks(cellAt(row, sourceTriggersAt).trim()).trim();
@@ -4038,7 +4070,10 @@ var parseSdd = (text) => {
4038
4070
  caseConditionalSlas: [],
4039
4071
  xrefOutputs: xrefProducers(document),
4040
4072
  resourceOutputs: resourceOutputsByTask(document),
4073
+ personas: [],
4074
+ personaNotes: [],
4041
4075
  canvasNotes: [],
4076
+ businessEvents: [],
4042
4077
  parseNotes: []
4043
4078
  };
4044
4079
  const lookalikes = [];
@@ -4053,7 +4088,7 @@ var parseSdd = (text) => {
4053
4088
  if (level !== 3)
4054
4089
  continue;
4055
4090
  const headingLine = lineAt(text, section.start);
4056
- if (applyCaseSection(sdd, title, body, headingLine, declaredNames))
4091
+ if (applyCaseSection(sdd, title, body, headingLine, declaredNames, ownBodyOf(section)))
4057
4092
  continue;
4058
4093
  const stageName = readStageHeading(title);
4059
4094
  if (stageName !== undefined) {
@@ -4083,7 +4118,7 @@ var parseSdd = (text) => {
4083
4118
  };
4084
4119
 
4085
4120
  // src/services/strict-sdd-audit/entry.ts
4086
- var parseSddToJson = (text) => {
4121
+ var parseSddToJson2 = (text) => {
4087
4122
  const sdd = parseSdd(text);
4088
4123
  return {
4089
4124
  stages: sdd.stages.map((stage) => ({
@@ -4145,7 +4180,12 @@ var parseSddToJson = (text) => {
4145
4180
  ...sdd.caseDescription !== undefined && {
4146
4181
  caseDescription: sdd.caseDescription
4147
4182
  },
4183
+ personas: sdd.personas,
4184
+ ...sdd.personaNotes.length > 0 && { personaNotes: sdd.personaNotes },
4148
4185
  ...sdd.canvasNotes.length > 0 && { canvasNotes: sdd.canvasNotes },
4186
+ ...sdd.businessEvents.length > 0 && {
4187
+ businessEvents: sdd.businessEvents
4188
+ },
4149
4189
  intake: {
4150
4190
  caseExitRows: sdd.caseExitRows,
4151
4191
  sawVariableSection: sdd.sawVariableSection,
@@ -4153,8 +4193,8 @@ var parseSddToJson = (text) => {
4153
4193
  }
4154
4194
  };
4155
4195
  };
4156
- var auditCaseplanAgainstSdd = (parsed, sddText, sddName, ledger) => {
4157
- const [parseError, sdd] = catchError(() => parseSdd(sddText));
4196
+ var auditCaseplanAgainstSdd2 = (parsed, sddText, sddName, ledger) => {
4197
+ const [parseError, sdd] = catchError2(() => parseSdd(sddText));
4158
4198
  if (parseError instanceof SddNameCollisionError) {
4159
4199
  return [
4160
4200
  error("STRICT_SDD_NAME_COLLISION", sddName, `"${sddName}": ${parseError.message}`)
@@ -4171,6 +4211,6 @@ var auditCaseplanAgainstSdd = (parsed, sddText, sddName, ledger) => {
4171
4211
  issues.push(...slaReferenceFindings(sddText, sddName));
4172
4212
  return issues;
4173
4213
  };
4174
- export { CASE_SLA_PROPERTY, VARIABLE_PLACEMENT, DEFAULT_GROUP, markDeferredOutputs, expandXrefs, expandOutputMarkers, declaredOutputs, outputTypeFor, outputFor, variableOutputType, extractOutputFor, customOutputFor, canonicalExpression, unquoteLiteral, inputsFor, STAGE_SELECTOR_RULES, TASK_SELECTOR_RULES, parseIfCell, bodySchemaIn, parseSddToJson, auditCaseplanAgainstSdd };
4214
+ export { CASE_SLA_PROPERTY, VARIABLE_PLACEMENT2, DEFAULT_GROUP2, parseSddToJson2, auditCaseplanAgainstSdd2 };
4175
4215
 
4176
- //# debugId=1BFE2BF48D4AB38A64756E2164756E21
4216
+ //# debugId=0BFEE81926C8921664756E2164756E21