@hotmeshio/long-tail 0.22.0 → 0.22.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (356) hide show
  1. package/build/api/escalations/accumulate.d.ts +10 -1
  2. package/build/api/escalations/accumulate.js +121 -34
  3. package/build/api/escalations/metadata.d.ts +5 -0
  4. package/build/api/escalations/metadata.js +6 -2
  5. package/build/api/escalations/resolve-batch.js +18 -11
  6. package/build/api/roles.js +4 -0
  7. package/build/api/scan-codes/badge-policy.d.ts +10 -0
  8. package/build/api/scan-codes/badge-policy.js +72 -0
  9. package/build/api/scan-codes/choice.js +18 -11
  10. package/build/api/scan-codes/context.d.ts +45 -1
  11. package/build/api/scan-codes/context.js +51 -3
  12. package/build/api/scan-codes/execute.js +73 -35
  13. package/build/api/scan-codes/grant.d.ts +27 -0
  14. package/build/api/scan-codes/grant.js +67 -0
  15. package/build/api/scan-codes/hold.d.ts +10 -0
  16. package/build/api/scan-codes/hold.js +98 -0
  17. package/build/api/scan-codes/identity.d.ts +5 -2
  18. package/build/api/scan-codes/identity.js +26 -8
  19. package/build/api/scan-codes/subject.d.ts +15 -0
  20. package/build/api/scan-codes/subject.js +68 -0
  21. package/build/api/scan-codes/verb-accumulate.d.ts +24 -0
  22. package/build/api/scan-codes/verb-accumulate.js +147 -0
  23. package/build/api/scan-codes/verb-fill.d.ts +6 -0
  24. package/build/api/scan-codes/verb-fill.js +152 -0
  25. package/build/api/scan-codes/verbs-subject.d.ts +14 -0
  26. package/build/api/scan-codes/verbs-subject.js +270 -0
  27. package/build/api/scan-codes/verbs.d.ts +7 -22
  28. package/build/api/scan-codes/verbs.js +35 -114
  29. package/build/api/workflows/invocation.d.ts +8 -4
  30. package/build/api/workflows/invocation.js +26 -9
  31. package/build/index.d.ts +1 -0
  32. package/build/index.js +8 -2
  33. package/build/lib/cli/commands/workflows.js +9 -4
  34. package/build/lib/db/schemas/037_scan_gtin_and_subject_grants.sql +27 -0
  35. package/build/routes/escalations/accumulate.js +13 -6
  36. package/build/routes/escalations/acting.d.ts +5 -0
  37. package/build/routes/escalations/acting.js +8 -1
  38. package/build/routes/scan-codes.js +3 -0
  39. package/build/sdk/index.d.ts +3 -0
  40. package/build/services/escalation/crud.d.ts +2 -2
  41. package/build/services/escalation/crud.js +4 -2
  42. package/build/services/escalation/facet-sql.js +8 -0
  43. package/build/services/escalation/sql.d.ts +4 -2
  44. package/build/services/escalation/sql.js +10 -2
  45. package/build/services/export/index.d.ts +8 -3
  46. package/build/services/export/index.js +15 -21
  47. package/build/services/iam/acting-identity.d.ts +29 -4
  48. package/build/services/iam/acting-identity.js +60 -17
  49. package/build/services/iam/ephemeral.d.ts +21 -0
  50. package/build/services/iam/ephemeral.js +32 -1
  51. package/build/services/iam/sql.d.ts +10 -0
  52. package/build/services/iam/sql.js +32 -3
  53. package/build/services/pipelines/events.js +2 -1
  54. package/build/services/pipelines/execution-builder.js +2 -1
  55. package/build/services/pipelines/queries.js +2 -1
  56. package/build/services/role/badge-grant.d.ts +5 -0
  57. package/build/services/role/badge-grant.js +36 -0
  58. package/build/services/role/seed.js +5 -0
  59. package/build/services/role/types.d.ts +14 -0
  60. package/build/services/role/types.js +5 -0
  61. package/build/services/scan-code/index.d.ts +3 -2
  62. package/build/services/scan-code/index.js +7 -1
  63. package/build/services/scan-code/parse.d.ts +23 -20
  64. package/build/services/scan-code/parse.js +45 -90
  65. package/build/services/scan-code/read.js +1 -0
  66. package/build/services/scan-code/sql.d.ts +3 -3
  67. package/build/services/scan-code/sql.js +11 -8
  68. package/build/services/scan-code/validate-subject.d.ts +7 -0
  69. package/build/services/scan-code/validate-subject.js +158 -0
  70. package/build/services/scan-code/validate.d.ts +7 -0
  71. package/build/services/scan-code/validate.js +111 -16
  72. package/build/services/scan-code/write.d.ts +1 -0
  73. package/build/services/scan-code/write.js +9 -3
  74. package/build/services/workflow-state.d.ts +21 -0
  75. package/build/services/workflow-state.js +39 -0
  76. package/build/shared/form-validation/field-validator.js +4 -0
  77. package/build/shared/form-validation/index.d.ts +1 -0
  78. package/build/shared/form-validation/index.js +1 -0
  79. package/build/shared/form-validation/x-lt-scan.d.ts +52 -0
  80. package/build/shared/form-validation/x-lt-scan.js +166 -0
  81. package/build/shared/scan-code/gtin.d.ts +5 -0
  82. package/build/shared/scan-code/gtin.js +25 -0
  83. package/build/shared/scan-code/index.d.ts +2 -0
  84. package/build/shared/scan-code/index.js +10 -0
  85. package/build/shared/scan-code/parse.d.ts +46 -0
  86. package/build/shared/scan-code/parse.js +133 -0
  87. package/build/shared/workflow-state.d.ts +17 -0
  88. package/build/shared/workflow-state.js +29 -0
  89. package/build/system/mcp-servers/admin/escalations.js +8 -4
  90. package/build/system/mcp-servers/admin/scan-codes.js +10 -4
  91. package/build/system/mcp-servers/admin/schemas.d.ts +792 -23
  92. package/build/system/mcp-servers/admin/schemas.js +61 -4
  93. package/build/system/mcp-servers/admin/workflows.js +20 -31
  94. package/build/system/mcp-servers/human-queue-schemas.d.ts +6 -6
  95. package/build/system/seed/tool-manifests-admin.d.ts +94 -0
  96. package/build/system/seed/tool-manifests-admin.js +3 -3
  97. package/build/tsconfig.tsbuildinfo +1 -1
  98. package/build/types/escalation.d.ts +2 -0
  99. package/build/types/escalation.js +3 -1
  100. package/build/types/facets.d.ts +14 -0
  101. package/build/types/index.d.ts +3 -3
  102. package/build/types/index.js +8 -1
  103. package/build/types/scan-code/constants.d.ts +182 -0
  104. package/build/types/{scan-code.js → scan-code/constants.js} +62 -12
  105. package/build/types/scan-code/index.d.ts +3 -0
  106. package/build/types/scan-code/index.js +17 -0
  107. package/build/types/scan-code/response.d.ts +140 -0
  108. package/build/types/scan-code/response.js +2 -0
  109. package/build/types/scan-code/rule.d.ts +199 -0
  110. package/build/types/scan-code/rule.js +2 -0
  111. package/build/types/startup.d.ts +3 -1
  112. package/dashboard/dist/assets/{AdminDashboard-Covdam48.js → AdminDashboard-DBW12qQt.js} +2 -2
  113. package/dashboard/dist/assets/{AdminDashboard-Covdam48.js.map → AdminDashboard-DBW12qQt.js.map} +1 -1
  114. package/dashboard/dist/assets/{AgentConfigPage-CE1k6J5r.js → AgentConfigPage-CmHv8U9M.js} +2 -2
  115. package/dashboard/dist/assets/{AgentConfigPage-CE1k6J5r.js.map → AgentConfigPage-CmHv8U9M.js.map} +1 -1
  116. package/dashboard/dist/assets/{AgentDetailPage-CWJZIZP1.js → AgentDetailPage-D1scIbz-.js} +3 -3
  117. package/dashboard/dist/assets/{AgentDetailPage-CWJZIZP1.js.map → AgentDetailPage-D1scIbz-.js.map} +1 -1
  118. package/dashboard/dist/assets/{AgentsPage---Dg5ufF.js → AgentsPage-BA_ai9HJ.js} +2 -2
  119. package/dashboard/dist/assets/{AgentsPage---Dg5ufF.js.map → AgentsPage-BA_ai9HJ.js.map} +1 -1
  120. package/dashboard/dist/assets/AvailableEscalationsPage-C2_5aEFW.js +2 -0
  121. package/dashboard/dist/assets/AvailableEscalationsPage-C2_5aEFW.js.map +1 -0
  122. package/dashboard/dist/assets/{BotPicker-D3drO3-T.js → BotPicker-BZo4Kp4a.js} +2 -2
  123. package/dashboard/dist/assets/{BotPicker-D3drO3-T.js.map → BotPicker-BZo4Kp4a.js.map} +1 -1
  124. package/dashboard/dist/assets/{BulkAssignModal-BOAWsViq.js → BulkAssignModal-BFhyxY2T.js} +2 -2
  125. package/dashboard/dist/assets/{BulkAssignModal-BOAWsViq.js.map → BulkAssignModal-BFhyxY2T.js.map} +1 -1
  126. package/dashboard/dist/assets/CapabilitiesPage-CvOX6vAc.js +2 -0
  127. package/dashboard/dist/assets/{CapabilitiesPage-RGgD3YZO.js.map → CapabilitiesPage-CvOX6vAc.js.map} +1 -1
  128. package/dashboard/dist/assets/{CollapsibleSection-DNkZn87n.js → CollapsibleSection-GCJsV9Oq.js} +2 -2
  129. package/dashboard/dist/assets/{CollapsibleSection-DNkZn87n.js.map → CollapsibleSection-GCJsV9Oq.js.map} +1 -1
  130. package/dashboard/dist/assets/{ConfirmDeleteModal-BqzOwhPr.js → ConfirmDeleteModal-CAgYLcef.js} +2 -2
  131. package/dashboard/dist/assets/{ConfirmDeleteModal-BqzOwhPr.js.map → ConfirmDeleteModal-CAgYLcef.js.map} +1 -1
  132. package/dashboard/dist/assets/CountdownTimer-Dik2z0M_.js +2 -0
  133. package/dashboard/dist/assets/{CountdownTimer-BaexQMK6.js.map → CountdownTimer-Dik2z0M_.js.map} +1 -1
  134. package/dashboard/dist/assets/{CredentialsPage-D7abp0aS.js → CredentialsPage-twd2xCOW.js} +2 -2
  135. package/dashboard/dist/assets/{CredentialsPage-D7abp0aS.js.map → CredentialsPage-twd2xCOW.js.map} +1 -1
  136. package/dashboard/dist/assets/{CronLabel-DZiWmTwJ.js → CronLabel-DdnIuudX.js} +2 -2
  137. package/dashboard/dist/assets/{CronLabel-DZiWmTwJ.js.map → CronLabel-DdnIuudX.js.map} +1 -1
  138. package/dashboard/dist/assets/{DropZone-f1o-GLM8.js → DropZone-B1Tdfddc.js} +2 -2
  139. package/dashboard/dist/assets/{DropZone-f1o-GLM8.js.map → DropZone-B1Tdfddc.js.map} +1 -1
  140. package/dashboard/dist/assets/{ElapsedCell-BdGO_w4v.js → ElapsedCell-DCctVv5R.js} +2 -2
  141. package/dashboard/dist/assets/{ElapsedCell-BdGO_w4v.js.map → ElapsedCell-DCctVv5R.js.map} +1 -1
  142. package/dashboard/dist/assets/{EntityTimelinePanel-DIDcF5Yu.js → EntityTimelinePanel-CMnskyWv.js} +2 -2
  143. package/dashboard/dist/assets/{EntityTimelinePanel-DIDcF5Yu.js.map → EntityTimelinePanel-CMnskyWv.js.map} +1 -1
  144. package/dashboard/dist/assets/{EscalationListSchemaPage-C28rn9XC.js → EscalationListSchemaPage-VfCn6WDb.js} +3 -3
  145. package/dashboard/dist/assets/{EscalationListSchemaPage-C28rn9XC.js.map → EscalationListSchemaPage-VfCn6WDb.js.map} +1 -1
  146. package/dashboard/dist/assets/{EscalationListView-CkBnkpDJ.js → EscalationListView-CGQZxcC-.js} +2 -2
  147. package/dashboard/dist/assets/{EscalationListView-CkBnkpDJ.js.map → EscalationListView-CGQZxcC-.js.map} +1 -1
  148. package/dashboard/dist/assets/EscalationSchemaPage-BX0tx-PM.js +9 -0
  149. package/dashboard/dist/assets/EscalationSchemaPage-BX0tx-PM.js.map +1 -0
  150. package/dashboard/dist/assets/EscalationTimeline-DKoSM56J.js +2 -0
  151. package/dashboard/dist/assets/EscalationTimeline-DKoSM56J.js.map +1 -0
  152. package/dashboard/dist/assets/{EscalationTitleSelect-CzKfavri.js → EscalationTitleSelect-DWGLBOmS.js} +2 -2
  153. package/dashboard/dist/assets/{EscalationTitleSelect-CzKfavri.js.map → EscalationTitleSelect-DWGLBOmS.js.map} +1 -1
  154. package/dashboard/dist/assets/{EscalationsOverview-Tdu3Yx2c.js → EscalationsOverview-C2XoguaA.js} +2 -2
  155. package/dashboard/dist/assets/{EscalationsOverview-Tdu3Yx2c.js.map → EscalationsOverview-C2XoguaA.js.map} +1 -1
  156. package/dashboard/dist/assets/{EventTable-DiIxOJwO.js → EventTable-CBUWq7--.js} +2 -2
  157. package/dashboard/dist/assets/{EventTable-DiIxOJwO.js.map → EventTable-CBUWq7--.js.map} +1 -1
  158. package/dashboard/dist/assets/{FilterBar-CvMBCVIo.js → FilterBar-8IdbleZ9.js} +2 -2
  159. package/dashboard/dist/assets/{FilterBar-CvMBCVIo.js.map → FilterBar-8IdbleZ9.js.map} +1 -1
  160. package/dashboard/dist/assets/GrantScopeField-BzWGzBfA.js +2 -0
  161. package/dashboard/dist/assets/GrantScopeField-BzWGzBfA.js.map +1 -0
  162. package/dashboard/dist/assets/{GraphInvokePage-DO-yyhi6.js → GraphInvokePage-CUvoPzsW.js} +2 -2
  163. package/dashboard/dist/assets/{GraphInvokePage-DO-yyhi6.js.map → GraphInvokePage-CUvoPzsW.js.map} +1 -1
  164. package/dashboard/dist/assets/{HomePage-DSS3eHR2.js → HomePage-BP7v9GLJ.js} +2 -2
  165. package/dashboard/dist/assets/{HomePage-DSS3eHR2.js.map → HomePage-BP7v9GLJ.js.map} +1 -1
  166. package/dashboard/dist/assets/{ListToolbar-C3-Tqqub.js → ListToolbar-Cnacuwoy.js} +2 -2
  167. package/dashboard/dist/assets/{ListToolbar-C3-Tqqub.js.map → ListToolbar-Cnacuwoy.js.map} +1 -1
  168. package/dashboard/dist/assets/{McpOverview-DEJmsdVi.js → McpOverview-C2JoNoDA.js} +2 -2
  169. package/dashboard/dist/assets/{McpOverview-DEJmsdVi.js.map → McpOverview-C2JoNoDA.js.map} +1 -1
  170. package/dashboard/dist/assets/McpQueryDetailPage-C_nih0w-.js +5 -0
  171. package/dashboard/dist/assets/McpQueryDetailPage-C_nih0w-.js.map +1 -0
  172. package/dashboard/dist/assets/{McpQueryPage-BBcG_UuU.js → McpQueryPage-U_wNXow2.js} +2 -2
  173. package/dashboard/dist/assets/{McpQueryPage-BBcG_UuU.js.map → McpQueryPage-U_wNXow2.js.map} +1 -1
  174. package/dashboard/dist/assets/{McpRunDetailPage-C6xw0kBg.js → McpRunDetailPage-DtKoJq6J.js} +2 -2
  175. package/dashboard/dist/assets/{McpRunDetailPage-C6xw0kBg.js.map → McpRunDetailPage-DtKoJq6J.js.map} +1 -1
  176. package/dashboard/dist/assets/{McpRunsPage-CRQqrF4Q.js → McpRunsPage-CnYtfGhf.js} +2 -2
  177. package/dashboard/dist/assets/{McpRunsPage-CRQqrF4Q.js.map → McpRunsPage-CnYtfGhf.js.map} +1 -1
  178. package/dashboard/dist/assets/{NamespacePill-DADRJOUl.js → NamespacePill-DwM5pNA4.js} +2 -2
  179. package/dashboard/dist/assets/{NamespacePill-DADRJOUl.js.map → NamespacePill-DwM5pNA4.js.map} +1 -1
  180. package/dashboard/dist/assets/{OperationsPage-Dlfwzf-0.js → OperationsPage-C7hupIer.js} +2 -2
  181. package/dashboard/dist/assets/{OperationsPage-Dlfwzf-0.js.map → OperationsPage-C7hupIer.js.map} +1 -1
  182. package/dashboard/dist/assets/{OperatorDashboard-DoiDGZ9R.js → OperatorDashboard-fVuLn2D5.js} +2 -2
  183. package/dashboard/dist/assets/{OperatorDashboard-DoiDGZ9R.js.map → OperatorDashboard-fVuLn2D5.js.map} +1 -1
  184. package/dashboard/dist/assets/{PageHeader-Csq434-v.js → PageHeader-CMd3f4bz.js} +2 -2
  185. package/dashboard/dist/assets/{PageHeader-Csq434-v.js.map → PageHeader-CMd3f4bz.js.map} +1 -1
  186. package/dashboard/dist/assets/{PageHeaderWithStats-CSnk1p5A.js → PageHeaderWithStats-DHj8Mqox.js} +2 -2
  187. package/dashboard/dist/assets/{PageHeaderWithStats-CSnk1p5A.js.map → PageHeaderWithStats-DHj8Mqox.js.map} +1 -1
  188. package/dashboard/dist/assets/{PersonaDetailPage-BzbA4Prr.js → PersonaDetailPage-C-4o8lPw.js} +2 -2
  189. package/dashboard/dist/assets/{PersonaDetailPage-BzbA4Prr.js.map → PersonaDetailPage-C-4o8lPw.js.map} +1 -1
  190. package/dashboard/dist/assets/{PersonasPage-B36yn4hU.js → PersonasPage-BL1FwLmv.js} +2 -2
  191. package/dashboard/dist/assets/{PersonasPage-B36yn4hU.js.map → PersonasPage-BL1FwLmv.js.map} +1 -1
  192. package/dashboard/dist/assets/{PortalPage-BkD4YfDf.js → PortalPage-tJG5sgb1.js} +2 -2
  193. package/dashboard/dist/assets/{PortalPage-BkD4YfDf.js.map → PortalPage-tJG5sgb1.js.map} +1 -1
  194. package/dashboard/dist/assets/{ProcessDetailPage-BT0eiBWY.js → ProcessDetailPage-Dz_jY4Yt.js} +2 -2
  195. package/dashboard/dist/assets/{ProcessDetailPage-BT0eiBWY.js.map → ProcessDetailPage-Dz_jY4Yt.js.map} +1 -1
  196. package/dashboard/dist/assets/{ProcessesListPage-BEqeeNae.js → ProcessesListPage-Cbn9cter.js} +2 -2
  197. package/dashboard/dist/assets/{ProcessesListPage-BEqeeNae.js.map → ProcessesListPage-Cbn9cter.js.map} +1 -1
  198. package/dashboard/dist/assets/ResolverForm-Bk8iXQ6-.js +8 -0
  199. package/dashboard/dist/assets/ResolverForm-Bk8iXQ6-.js.map +1 -0
  200. package/dashboard/dist/assets/RoleDetailPage-owPi6gI9.js +8 -0
  201. package/dashboard/dist/assets/RoleDetailPage-owPi6gI9.js.map +1 -0
  202. package/dashboard/dist/assets/{RolePill-BafrASDX.js → RolePill-Di2CAHuD.js} +2 -2
  203. package/dashboard/dist/assets/{RolePill-BafrASDX.js.map → RolePill-Di2CAHuD.js.map} +1 -1
  204. package/dashboard/dist/assets/{RolesPage-80quJU5-.js → RolesPage-NwmMvZFa.js} +2 -2
  205. package/dashboard/dist/assets/{RolesPage-80quJU5-.js.map → RolesPage-NwmMvZFa.js.map} +1 -1
  206. package/dashboard/dist/assets/{RunAsSelector-BoePhWVD.js → RunAsSelector-tgEooTyU.js} +2 -2
  207. package/dashboard/dist/assets/{RunAsSelector-BoePhWVD.js.map → RunAsSelector-tgEooTyU.js.map} +1 -1
  208. package/dashboard/dist/assets/ScanCodesPage-CUv_8ic6.js +2 -0
  209. package/dashboard/dist/assets/ScanCodesPage-CUv_8ic6.js.map +1 -0
  210. package/dashboard/dist/assets/ScanSchemeDetailPage-YHqbu7oK.js +2 -0
  211. package/dashboard/dist/assets/ScanSchemeDetailPage-YHqbu7oK.js.map +1 -0
  212. package/dashboard/dist/assets/ScanStationPage-B275l6r_.js +2 -0
  213. package/dashboard/dist/assets/ScanStationPage-B275l6r_.js.map +1 -0
  214. package/dashboard/dist/assets/{ScopeBadge-b6MIBiYD.js → ScopeBadge-CSItPVOH.js} +2 -2
  215. package/dashboard/dist/assets/{ScopeBadge-b6MIBiYD.js.map → ScopeBadge-CSItPVOH.js.map} +1 -1
  216. package/dashboard/dist/assets/{StickyPagination-9ITrei_d.js → StickyPagination-CsTI_gvJ.js} +2 -2
  217. package/dashboard/dist/assets/{StickyPagination-9ITrei_d.js.map → StickyPagination-CsTI_gvJ.js.map} +1 -1
  218. package/dashboard/dist/assets/{StreamMessageDetail-BkYvEHFY.js → StreamMessageDetail-auRlvjTh.js} +2 -2
  219. package/dashboard/dist/assets/{StreamMessageDetail-BkYvEHFY.js.map → StreamMessageDetail-auRlvjTh.js.map} +1 -1
  220. package/dashboard/dist/assets/{SwimlaneTimeline-B1rotFix.js → SwimlaneTimeline-BLoVHYAd.js} +2 -2
  221. package/dashboard/dist/assets/{SwimlaneTimeline-B1rotFix.js.map → SwimlaneTimeline-BLoVHYAd.js.map} +1 -1
  222. package/dashboard/dist/assets/{TagInput-ZrIIrEUP.js → TagInput-CAgdLN_b.js} +2 -2
  223. package/dashboard/dist/assets/{TagInput-ZrIIrEUP.js.map → TagInput-CAgdLN_b.js.map} +1 -1
  224. package/dashboard/dist/assets/{TaskDetailPage-zhn_U_eC.js → TaskDetailPage-BPsZ1bK_.js} +2 -2
  225. package/dashboard/dist/assets/{TaskDetailPage-zhn_U_eC.js.map → TaskDetailPage-BPsZ1bK_.js.map} +1 -1
  226. package/dashboard/dist/assets/{TaskQueuePill-DkQ1uc6y.js → TaskQueuePill-h0oorVMe.js} +2 -2
  227. package/dashboard/dist/assets/{TaskQueuePill-DkQ1uc6y.js.map → TaskQueuePill-h0oorVMe.js.map} +1 -1
  228. package/dashboard/dist/assets/{TasksListPage-CNbNeagx.js → TasksListPage-BNGM74yT.js} +2 -2
  229. package/dashboard/dist/assets/{TasksListPage-CNbNeagx.js.map → TasksListPage-BNGM74yT.js.map} +1 -1
  230. package/dashboard/dist/assets/{TimeAgo-0YVMBwls.js → TimeAgo-BnAHXGK7.js} +2 -2
  231. package/dashboard/dist/assets/{TimeAgo-0YVMBwls.js.map → TimeAgo-BnAHXGK7.js.map} +1 -1
  232. package/dashboard/dist/assets/{TimestampCell-DB7r3np8.js → TimestampCell-J4yae6mc.js} +2 -2
  233. package/dashboard/dist/assets/{TimestampCell-DB7r3np8.js.map → TimestampCell-J4yae6mc.js.map} +1 -1
  234. package/dashboard/dist/assets/{ToolPill-Baxm02L6.js → ToolPill-CQCsVdzD.js} +2 -2
  235. package/dashboard/dist/assets/{ToolPill-Baxm02L6.js.map → ToolPill-CQCsVdzD.js.map} +1 -1
  236. package/dashboard/dist/assets/{ToolTestPanel-BLfA02wv.js → ToolTestPanel-BdzzgXDu.js} +2 -2
  237. package/dashboard/dist/assets/{ToolTestPanel-BLfA02wv.js.map → ToolTestPanel-BdzzgXDu.js.map} +1 -1
  238. package/dashboard/dist/assets/{TopicDetailPage-eAqPKShq.js → TopicDetailPage-DJKg8lb7.js} +4 -4
  239. package/dashboard/dist/assets/{TopicDetailPage-eAqPKShq.js.map → TopicDetailPage-DJKg8lb7.js.map} +1 -1
  240. package/dashboard/dist/assets/{TopicsPage-BNNWD-zl.js → TopicsPage-DQV0WxG6.js} +2 -2
  241. package/dashboard/dist/assets/{TopicsPage-BNNWD-zl.js.map → TopicsPage-DQV0WxG6.js.map} +1 -1
  242. package/dashboard/dist/assets/{UserName-Pw46tO_k.js → UserName-Ct09nGh5.js} +2 -2
  243. package/dashboard/dist/assets/{UserName-Pw46tO_k.js.map → UserName-Ct09nGh5.js.map} +1 -1
  244. package/dashboard/dist/assets/{WorkflowExecutionPage-DvA51lT3.js → WorkflowExecutionPage-B5WJOsee.js} +2 -2
  245. package/dashboard/dist/assets/{WorkflowExecutionPage-DvA51lT3.js.map → WorkflowExecutionPage-B5WJOsee.js.map} +1 -1
  246. package/dashboard/dist/assets/{WorkflowPill-ZepbO_ny.js → WorkflowPill-Dg7BDTyM.js} +2 -2
  247. package/dashboard/dist/assets/{WorkflowPill-ZepbO_ny.js.map → WorkflowPill-Dg7BDTyM.js.map} +1 -1
  248. package/dashboard/dist/assets/{WorkflowsDashboard-DFDTwnhn.js → WorkflowsDashboard-Hc87TRgD.js} +2 -2
  249. package/dashboard/dist/assets/{WorkflowsDashboard-DFDTwnhn.js.map → WorkflowsDashboard-Hc87TRgD.js.map} +1 -1
  250. package/dashboard/dist/assets/{WorkflowsOverview-5i2Tcy-p.js → WorkflowsOverview-CJQbyOj-.js} +2 -2
  251. package/dashboard/dist/assets/{WorkflowsOverview-5i2Tcy-p.js.map → WorkflowsOverview-CJQbyOj-.js.map} +1 -1
  252. package/dashboard/dist/assets/{YamlWorkflowDetailPage-p4VEYZS0.js → YamlWorkflowDetailPage-BMeED677.js} +8 -8
  253. package/dashboard/dist/assets/{YamlWorkflowDetailPage-p4VEYZS0.js.map → YamlWorkflowDetailPage-BMeED677.js.map} +1 -1
  254. package/dashboard/dist/assets/{YamlWorkflowsPage-Cq-hMVxl.js → YamlWorkflowsPage-BMMg0Id6.js} +2 -2
  255. package/dashboard/dist/assets/{YamlWorkflowsPage-Cq-hMVxl.js.map → YamlWorkflowsPage-BMMg0Id6.js.map} +1 -1
  256. package/dashboard/dist/assets/{agents-EyBPJMYa.js → agents-CdFkgFvg.js} +2 -2
  257. package/dashboard/dist/assets/{agents-EyBPJMYa.js.map → agents-CdFkgFvg.js.map} +1 -1
  258. package/dashboard/dist/assets/{bots-DX6jjqtF.js → bots-Mj3LHWWg.js} +2 -2
  259. package/dashboard/dist/assets/{bots-DX6jjqtF.js.map → bots-Mj3LHWWg.js.map} +1 -1
  260. package/dashboard/dist/assets/{capabilities-ZdLv9xBQ.js → capabilities-Dw1ObK2-.js} +2 -2
  261. package/dashboard/dist/assets/{capabilities-ZdLv9xBQ.js.map → capabilities-Dw1ObK2-.js.map} +1 -1
  262. package/dashboard/dist/assets/{controlplane-CpvZaJuw.js → controlplane-DcNE1APv.js} +2 -2
  263. package/dashboard/dist/assets/{controlplane-CpvZaJuw.js.map → controlplane-DcNE1APv.js.map} +1 -1
  264. package/dashboard/dist/assets/{escalation-columns-Mvsze5eV.js → escalation-columns-D364Trpy.js} +2 -2
  265. package/dashboard/dist/assets/{escalation-columns-Mvsze5eV.js.map → escalation-columns-D364Trpy.js.map} +1 -1
  266. package/dashboard/dist/assets/{index-Cn8uJsd7.js → index-B3veJ698.js} +2 -2
  267. package/dashboard/dist/assets/{index-Cn8uJsd7.js.map → index-B3veJ698.js.map} +1 -1
  268. package/dashboard/dist/assets/index-BQeHJHYN.js +67 -0
  269. package/dashboard/dist/assets/index-BQeHJHYN.js.map +1 -0
  270. package/dashboard/dist/assets/{index-D-Xs4rAW.js → index-BRGTmoYY.js} +2 -2
  271. package/dashboard/dist/assets/{index-D-Xs4rAW.js.map → index-BRGTmoYY.js.map} +1 -1
  272. package/dashboard/dist/assets/{index-DkrBFFSO.js → index-Bd6-KmBE.js} +3 -3
  273. package/dashboard/dist/assets/{index-DkrBFFSO.js.map → index-Bd6-KmBE.js.map} +1 -1
  274. package/dashboard/dist/assets/{index-Bhb89oHH.js → index-BiVce3n0.js} +2 -2
  275. package/dashboard/dist/assets/{index-Bhb89oHH.js.map → index-BiVce3n0.js.map} +1 -1
  276. package/dashboard/dist/assets/{index-z5Hpeidu.js → index-BmNAVd4s.js} +2 -2
  277. package/dashboard/dist/assets/{index-z5Hpeidu.js.map → index-BmNAVd4s.js.map} +1 -1
  278. package/dashboard/dist/assets/{index-BbCjGO3M.js → index-Bq5XNEzD.js} +2 -2
  279. package/dashboard/dist/assets/{index-BbCjGO3M.js.map → index-Bq5XNEzD.js.map} +1 -1
  280. package/dashboard/dist/assets/{index-DGmzBY9D.js → index-DPStdq4T.js} +2 -2
  281. package/dashboard/dist/assets/{index-DGmzBY9D.js.map → index-DPStdq4T.js.map} +1 -1
  282. package/dashboard/dist/assets/index-DcppjzP5.css +1 -0
  283. package/dashboard/dist/assets/{index-PfSRKerc.js → index-DkcI4V_H.js} +2 -2
  284. package/dashboard/dist/assets/{index-PfSRKerc.js.map → index-DkcI4V_H.js.map} +1 -1
  285. package/dashboard/dist/assets/{index-oFKUFGQL.js → index-Dyotfa_k.js} +2 -2
  286. package/dashboard/dist/assets/{index-oFKUFGQL.js.map → index-Dyotfa_k.js.map} +1 -1
  287. package/dashboard/dist/assets/{index-Eo-l3Lqo.js → index-MDD4JYtu.js} +2 -2
  288. package/dashboard/dist/assets/{index-Eo-l3Lqo.js.map → index-MDD4JYtu.js.map} +1 -1
  289. package/dashboard/dist/assets/index-WSm8bgE7.js +3 -0
  290. package/dashboard/dist/assets/index-WSm8bgE7.js.map +1 -0
  291. package/dashboard/dist/assets/{index-DZhtP2Ok.js → index-eDm6cz0F.js} +2 -2
  292. package/dashboard/dist/assets/{index-DZhtP2Ok.js.map → index-eDm6cz0F.js.map} +1 -1
  293. package/dashboard/dist/assets/{knowledge-CgloEkaf.js → knowledge-Bafewjxk.js} +2 -2
  294. package/dashboard/dist/assets/{knowledge-CgloEkaf.js.map → knowledge-Bafewjxk.js.map} +1 -1
  295. package/dashboard/dist/assets/{mcp-query-o-sGX2ST.js → mcp-query-BzTtIG90.js} +2 -2
  296. package/dashboard/dist/assets/{mcp-query-o-sGX2ST.js.map → mcp-query-BzTtIG90.js.map} +1 -1
  297. package/dashboard/dist/assets/{mcp-Bix7QYO7.js → mcp-vLIutbnc.js} +2 -2
  298. package/dashboard/dist/assets/{mcp-Bix7QYO7.js.map → mcp-vLIutbnc.js.map} +1 -1
  299. package/dashboard/dist/assets/{personas-30azkJ_s.js → personas-WD40EDS_.js} +2 -2
  300. package/dashboard/dist/assets/{personas-30azkJ_s.js.map → personas-WD40EDS_.js.map} +1 -1
  301. package/dashboard/dist/assets/{pipelines-Bfk2GFK7.js → pipelines-DFwULWs3.js} +2 -2
  302. package/dashboard/dist/assets/{pipelines-Bfk2GFK7.js.map → pipelines-DFwULWs3.js.map} +1 -1
  303. package/dashboard/dist/assets/{tasks-CpdopewW.js → tasks-La_-WAUn.js} +2 -2
  304. package/dashboard/dist/assets/{tasks-CpdopewW.js.map → tasks-La_-WAUn.js.map} +1 -1
  305. package/dashboard/dist/assets/{topics--bvCV9Yp.js → topics-CzYMlvqm.js} +2 -2
  306. package/dashboard/dist/assets/{topics--bvCV9Yp.js.map → topics-CzYMlvqm.js.map} +1 -1
  307. package/dashboard/dist/assets/{useDebouncedValue-Da0lPmj3.js → useDebouncedValue-C0FQyANW.js} +2 -2
  308. package/dashboard/dist/assets/{useDebouncedValue-Da0lPmj3.js.map → useDebouncedValue-C0FQyANW.js.map} +1 -1
  309. package/dashboard/dist/assets/{useNamespace-DD_Esl6n.js → useNamespace-WW_XjAb3.js} +2 -2
  310. package/dashboard/dist/assets/{useNamespace-DD_Esl6n.js.map → useNamespace-WW_XjAb3.js.map} +1 -1
  311. package/dashboard/dist/assets/{useYamlActivityEvents-AeAhZJJk.js → useYamlActivityEvents-B3YxPeFt.js} +2 -2
  312. package/dashboard/dist/assets/{useYamlActivityEvents-AeAhZJJk.js.map → useYamlActivityEvents-B3YxPeFt.js.map} +1 -1
  313. package/dashboard/dist/assets/{vendor-icons-B7OcUM6b.js → vendor-icons-CeVtUKyP.js} +2 -2
  314. package/dashboard/dist/assets/{vendor-icons-B7OcUM6b.js.map → vendor-icons-CeVtUKyP.js.map} +1 -1
  315. package/dashboard/dist/assets/{yaml-workflows-BHAOOGvn.js → yaml-workflows-COHOjdhF.js} +2 -2
  316. package/dashboard/dist/assets/{yaml-workflows-BHAOOGvn.js.map → yaml-workflows-COHOjdhF.js.map} +1 -1
  317. package/dashboard/dist/index.html +3 -3
  318. package/docs/accumulator.md +37 -1
  319. package/docs/api/mcp/admin.md +3 -2
  320. package/docs/api/sdk/escalations.md +6 -0
  321. package/docs/api/sdk/exports.md +1 -1
  322. package/docs/api/sdk/workflows.md +23 -4
  323. package/docs/cli.md +1 -1
  324. package/docs/dashboard.md +2 -2
  325. package/docs/faceted-routing.md +2 -0
  326. package/docs/hitl/x-lt-scan.md +51 -0
  327. package/docs/hitl/x-lt-widget.md +5 -0
  328. package/docs/hitl-guide.md +2 -0
  329. package/docs/scan-codes.md +252 -19
  330. package/package.json +1 -1
  331. package/build/types/scan-code.d.ts +0 -308
  332. package/dashboard/dist/assets/AvailableEscalationsPage-CpbC8oTN.js +0 -2
  333. package/dashboard/dist/assets/AvailableEscalationsPage-CpbC8oTN.js.map +0 -1
  334. package/dashboard/dist/assets/CapabilitiesPage-RGgD3YZO.js +0 -2
  335. package/dashboard/dist/assets/CountdownTimer-BaexQMK6.js +0 -2
  336. package/dashboard/dist/assets/EscalationSchemaPage-W5r0A1kj.js +0 -9
  337. package/dashboard/dist/assets/EscalationSchemaPage-W5r0A1kj.js.map +0 -1
  338. package/dashboard/dist/assets/EscalationTimeline-DKyTz6sM.js +0 -2
  339. package/dashboard/dist/assets/EscalationTimeline-DKyTz6sM.js.map +0 -1
  340. package/dashboard/dist/assets/McpQueryDetailPage-DYJr16l7.js +0 -5
  341. package/dashboard/dist/assets/McpQueryDetailPage-DYJr16l7.js.map +0 -1
  342. package/dashboard/dist/assets/ResolverForm-BwgjIX97.js +0 -7
  343. package/dashboard/dist/assets/ResolverForm-BwgjIX97.js.map +0 -1
  344. package/dashboard/dist/assets/RoleDetailPage-BFuLNJXa.js +0 -8
  345. package/dashboard/dist/assets/RoleDetailPage-BFuLNJXa.js.map +0 -1
  346. package/dashboard/dist/assets/ScanCodesPage-Cu1bKEix.js +0 -2
  347. package/dashboard/dist/assets/ScanCodesPage-Cu1bKEix.js.map +0 -1
  348. package/dashboard/dist/assets/ScanSchemeDetailPage-D9AbS6UY.js +0 -2
  349. package/dashboard/dist/assets/ScanSchemeDetailPage-D9AbS6UY.js.map +0 -1
  350. package/dashboard/dist/assets/ScanStationPage-gcZGefvT.js +0 -2
  351. package/dashboard/dist/assets/ScanStationPage-gcZGefvT.js.map +0 -1
  352. package/dashboard/dist/assets/index-B_GT23TE.js +0 -3
  353. package/dashboard/dist/assets/index-B_GT23TE.js.map +0 -1
  354. package/dashboard/dist/assets/index-CgUcYveM.js +0 -67
  355. package/dashboard/dist/assets/index-CgUcYveM.js.map +0 -1
  356. package/dashboard/dist/assets/index-Dh8MJl3L.css +0 -1
@@ -23,6 +23,7 @@ canonical action surface. A scan is an **ECA rule** over that surface:
23
23
  - [Rules and steps](#rules-and-steps)
24
24
  - [Confirmation](#confirmation)
25
25
  - [Info-choice screens](#info-choice-screens)
26
+ - [Holding an item: the bench motion](#holding-an-item-the-bench-motion)
26
27
  - [Identity schemes and acting identity](#identity-schemes-and-acting-identity)
27
28
  - [Station deployments](#station-deployments)
28
29
  - [The fallback screen](#the-fallback-screen)
@@ -31,6 +32,7 @@ canonical action surface. A scan is an **ECA rule** over that surface:
31
32
  - [Scanner setup](#scanner-setup)
32
33
  - [Admin configuration](#admin-configuration)
33
34
  - [The printer demo](#the-printer-demo)
35
+ - [The bag and bin demo](#the-bag-and-bin-demo)
34
36
 
35
37
  ## The code
36
38
 
@@ -62,6 +64,14 @@ A scheme (`lt_config_scan_schemes`, indexed 10–99) declares:
62
64
  `1015949975930`. Fits UPC-A/EAN/ITF labels; a trailing check digit is
63
65
  accepted. Two digits of version + one of category + `target_length`
64
66
  digits of target.
67
+ - `gtin` — manufacturer barcodes: UPC-A, EAN-13, EAN-8 and GTIN-14, as
68
+ printed on products. The code carries no version or category; a valid
69
+ check digit identifies it. The target is the 14-digit GTIN (a UPC-A
70
+ `036000291452` targets `00036000291452`), `{scan.code}` keeps the code as
71
+ scanned, and the scheme's one rule is category `0`. A deployment has at
72
+ most one gtin scheme, and while it is enabled no enabled fixed scheme may
73
+ accept an 8, 12, 13 or 14 digit code: the scheme write is refused, naming
74
+ both schemes, so a digits-only code always has exactly one reading.
65
75
 
66
76
  ## Rules and steps
67
77
 
@@ -98,7 +108,21 @@ Verbs are the canonical escalation actions:
98
108
  | `resolve` | Atomic claim + resolve with a canned payload | `resolverPayload`, `metadata` |
99
109
  | `escalate` | Create an escalation in another queue, optionally closing the located one | `targetRole`, `closeCurrent`, `escalationType`, `description`, `metadata` |
100
110
  | `cancel` | Claim-as-lock, then cancel | — |
101
- | `accumulate` | Add the scanned item to an open accumulator escalation | `itemKey` (template), `resolverPayload`, `metadata`, `accumulate: { containerFacet, containerRoles, reciprocal }` |
111
+ | `accumulate` | Add the scanned item to an open accumulator escalation | `itemKey` (template), `resolverPayload`, `metadata`, `accumulate: { containerFacet, container, containerRoles, reciprocal }` |
112
+
113
+ `availability` names the claim state a step's row must be in:
114
+
115
+ | `availability` | Matches |
116
+ |---|---|
117
+ | `mine` | a row under a live claim held by the acting identity (the badged user, else the session user) |
118
+ | `available` | an unclaimed row, or one whose claim has lapsed |
119
+ | `claimed` | a row under anyone's live claim |
120
+ | `any` (default) | every row the rest of the query matches |
121
+
122
+ A `resolve` step applies it inside the atomic resolve: `mine` resolves the
123
+ actor's own claim, the most recently claimed first when there are several,
124
+ so a container scan places the item in the associate's hand even when
125
+ another item for the same container is waiting. With no matching row the step falls through.
102
126
 
103
127
  String values inside `itemKey`, `resolverPayload`, and `metadata` interpolate
104
128
  `{scan.target}`, `{scan.category}`, and `{scan.scannedAt}`. Two facet bags
@@ -107,27 +131,44 @@ extend them: `{claim.<facet>}` reads the acting user's single live claim
107
131
  make the step fall through), and `{item.<facet>}` reads the row an item-mode
108
132
  `accumulate` step located. A token that cannot resolve falls through instead
109
133
  of writing the literal, and the rule editor rejects `{item.…}` on any other
110
- step. A bag-first ladder therefore names the order from the claim:
111
- `"itemKey": "{claim.orderId}"` on the shelf scan. Every mutating verb stamps
134
+ step. An item-first ladder therefore names the item from the claim:
135
+ `"itemKey": "{claim.itemId}"` on the container scan. Every mutating verb stamps
112
136
  provenance facets onto the row it touches — `scanScheme`, `scanCategory`,
113
137
  `scanActionName`, `scannedAt` — so scan-driven transitions stay queryable.
114
138
 
115
139
  `accumulate` has two modes. With `params.accumulate.containerFacet` the
116
140
  scanned target is the ITEM: the step locates the item's own pending row
117
- through the scheme facet, reads that facet from the row (a bag row carrying
118
- `binKey: "B-7"`), and adds the target to the pending accumulator whose
141
+ through the scheme facet, reads that facet from the row (an item row carrying
142
+ `containerKey: "C-7"`), and adds the target to the pending accumulator whose
119
143
  metadata carries the same value, writing the item's row as the reciprocal in
120
- the same statement (`reciprocal: false` skips it; `containerRoles` names the
121
- container queues). The container pick considers only pending rows that carry
122
- the accumulator declaration, so a release row sharing the facet during a
123
- pack-out is never the target. Without it the scanned target is the CONTAINER and the step
144
+ the same statement (`reciprocal: false` skips it). The container pick
145
+ considers only pending rows that carry the accumulator declaration, so a
146
+ release row sharing the facet during a pack-out is never the target.
147
+ `container` narrows the pick further:
148
+
149
+ ```jsonc
150
+ "accumulate": {
151
+ "containerFacet": "containerKey",
152
+ "container": {
153
+ "roles": ["packing"], // container queues (containerRoles is an alias)
154
+ "types": ["container"], // escalation type
155
+ "subtypes": ["open"], // escalation subtype
156
+ "facets": { "open": true } // extra metadata guards
157
+ }
158
+ }
159
+ ```
160
+
161
+ A design that parks each item as its own accumulator in the same queue as
162
+ its container (an item's slot beside its container, both carrying
163
+ `containerKey`) declares the container's type or subtype, so the add lands in
164
+ the container and never in a sibling's slot. The item's own row is never its container. Without it the scanned target is the CONTAINER and the step
124
165
  adds `params.itemKey` (a template) to it. An item with no row or no container
125
166
  facet falls through to the next step; a container already holding the item
126
167
  reports a conflict. When the item's row exists but no pending container
127
168
  carries its facet (the previous container closed and its successor has not
128
169
  parked yet), the step answers `no_open_container` with the item row, the
129
170
  facet, and the rule's fallback markdown, so the station reads "Container
130
- closing, scan again" instead of the bag with no hint.
171
+ closing, scan again" instead of the item with no hint.
131
172
 
132
173
  Ordering is the power move: put the expected state first and a broad
133
174
  `show-detail` last. A machine whose twin is in the wrong queue still answers
@@ -151,7 +192,7 @@ guarded call every other surface uses.
151
192
 
152
193
  ## Info-choice screens
153
194
 
154
- Some objects carry one code for their whole life — an item tag, an order
195
+ Some objects carry one code for their whole life — an item tag, a record
155
196
  label. One code, many possible intents, and the right one depends on where
156
197
  the object is in its journey. The `present` verb closes that gap: the step
157
198
  locates the row, states its reality, and returns a configured, labeled
@@ -178,8 +219,10 @@ unsatisfied. Picking one calls `POST /api/scan-codes/execute-choice` with a
178
219
  pointer (scheme, category, step index, choice index, escalation id) — and a
179
220
  pointer is never authority: the server re-reads live config, re-locates the
180
221
  row under the step's query, re-applies the identity gate, and runs the verb
181
- through the same atomic executors a direct scan uses. A row that moved on
182
- between render and tap answers `conflict`, exactly as a lost double-scan.
222
+ through the same atomic executors a direct scan uses. A resolve choice
223
+ resolves the exact row the screen presented, re-checked under the step's
224
+ query in the same statement. A row that moved on between render and tap
225
+ answers `conflict`, exactly as a lost double-scan.
183
226
 
184
227
  A choice's `code` is a short printable token (letters, digits, underscore,
185
228
  dash) enabling double-scan selection: scan the object, then scan an action
@@ -198,6 +241,121 @@ duration, form on screen. An unsatisfied identity requirement never
198
241
  auto-fires as the wrong actor — the scan stops over at the badge screen and
199
242
  completes on its own once a badge primes.
200
243
 
244
+ ## Holding an item: the bench motion
245
+
246
+ At a bench where items go into containers, every act is one item going into
247
+ one container, or one expected item checked off a record. The motion is three scans with nothing in between: **the item, your badge, the
248
+ container**. The container scan is the act. The item scan only says which
249
+ item; the badge says who.
250
+
251
+ **`hold`** is the item scan. The step locates the row and answers `held`
252
+ with a **subject**: the code that held it, the row id, a label, an expiry,
253
+ and what to scan next. It writes nothing and spends no badge use. The
254
+ station keeps the subject (device-local, in memory) and sends it back with
255
+ the next scans as `subject: { code, escalationId }`. Scanning another item
256
+ replaces it; it lapses on its own after `ttlSeconds` (default 45).
257
+
258
+ ```jsonc
259
+ { "query": { "roles": ["packing"] }, "verb": "hold",
260
+ "params": { "hold": { "ttlSeconds": 300, "label": "{scan.target}",
261
+ "headline": "{item.containerCode}", "subline": "{item.locationName}",
262
+ "expect": { "schemes": [14], "prompt": "Walk to this container and scan it.\n\n{item.labelNote}" } } } }
263
+ ```
264
+
265
+ **What the station shows.** With `hold.headline` the destination leads the
266
+ screen: the headline (e.g. `{item.containerCode}`) in very large type, `hold.subline`
267
+ under it (e.g. `{item.locationName}`), the held item itself as a quieter line
268
+ above, then the `expect.prompt`. A template that renders empty drops its line,
269
+ so a row-level note such as `{item.labelNote}` appears only when the workflow
270
+ set one ("Take the new label from the printer and stick it on C-12").
271
+ Any step that writes may carry `done: { markdown }`: the copy the station shows,
272
+ large, once the write lands and until the next scan ("Place it in
273
+ **{container.containerCode}**. All good."). It reads the same bags its refusal copy
274
+ reads. A fill that leaves items to scan shows its done copy as the progress
275
+ line under the hold.
276
+
277
+ The server trusts nothing about the subject. Each request re-parses its
278
+ code, re-reads the row under the station's read scope, requires the row to
279
+ still carry the code's target and to be pending. A subject that fails is
280
+ stale: steps that need one are skipped, and when nothing else matches the
281
+ answer is `subject_stale` with `clearSubject: true`.
282
+
283
+ **Subject steps** run only while a subject from one of `subject.schemes` is
284
+ held, and act on it. The `{subject.<facet>}` template bag reads the held
285
+ row.
286
+
287
+ | Step | What the container scan does |
288
+ |---|---|
289
+ | `accumulate` with `accumulate.from: "subject"` | The held item joins the accumulator the scan names (found by the scheme facet, narrowed by `accumulate.container`). The held row is written as the reciprocal in the same statement, so a held row parked as a one-slot accumulator completes and its workflow wakes. `params.itemKey` names the item, e.g. `{subject.itemId}`. |
290
+ | `accumulate` with `accumulate.into: "subject"` | The held row collects the scanned code (`itemKey` defaults to `{scan.target}`). Use it when the container has no row yet: the first item for a container tells the workflow which container was scanned. |
291
+ | `fill` with `fill.into: "subject"` | One expected item is checked off: the held row is a batch whose keys are codes, and the scan fills the first open key for its code. A code expected twice is declared `<code>#1`, `<code>#2` and takes two scans; a third is refused. The answer carries `progress: { filled, total, remaining }`, the subject stays held while items remain, and the last fill completes the row. `fill.into: "scanned"` fills the row the scan itself names. |
292
+
293
+ **`match` and `refuse`** check the pairing before anything is written.
294
+ `match.target` lists templates the scanned target must equal (e.g.
295
+ `["{subject.containerCode}"]`); `match.facets` lists facets the located container
296
+ must share with the held row (e.g. `["containerKey"]`). A miss answers `refused`
297
+ with `refuse.markdown` rendered, and nothing is written. The copy may read
298
+ `{container.<facet>}` (the container the scan named) and `{fill.pending}`
299
+ (what a fill still expects), so the station says what is wrong and where the
300
+ item goes: "That's Acme West's container. This one goes in **C-12**." When the
301
+ write itself loses a race (the container closed, another bench took the free
302
+ container), the answer is `refused` with `refuse.conflict`, and a step that
303
+ declares `refuse.conflict` also clears the subject so the next item scan
304
+ reads fresh state. Exactly one of two racing acts wins whenever both name
305
+ the same row: the reciprocal statement writes both rows or neither.
306
+
307
+ **One rule, several kinds of held item.** `subject.facets` makes a step
308
+ apply only while the held row carries those facet values; otherwise the step
309
+ is skipped and the next one runs. A placing bench uses it to tell an item
310
+ whose destination already has a container from one that needs a free
311
+ container: the item row carries `placement: 'assigned'` or `'unassigned'`, and
312
+ the container label's rule has one step for each.
313
+
314
+ ```jsonc
315
+ // 14:0, container label
316
+ [
317
+ { "verb": "accumulate", "requireActingIdentity": true,
318
+ "subject": { "schemes": [11], "facets": { "placement": "assigned" } },
319
+ "match": { "target": ["{subject.containerCode}"] },
320
+ "refuse": { "markdown": "That's {container.locationName}'s container. This one goes in **{subject.containerCode}**." },
321
+ "params": { "itemKey": "{subject.itemId}",
322
+ "accumulate": { "from": "subject", "container": { "types": ["container"], "subtypes": ["open"] } } } },
323
+ { "verb": "accumulate", "requireActingIdentity": true,
324
+ "subject": { "schemes": [11], "facets": { "placement": "unassigned" } },
325
+ "match": { "target": ["{subject.offeredContainers}"] },
326
+ "refuse": { "markdown": "Choose a free container: {subject.offeredContainers}.",
327
+ "conflict": "That container was just taken. Scan the item again.",
328
+ "missing": "That container was just taken. Scan the item again." },
329
+ "params": { "itemKey": "{subject.itemId}",
330
+ "accumulate": { "from": "subject", "container": { "types": ["container"], "subtypes": ["free"] } } } },
331
+ { "query": { "roles": ["packing"] }, "verb": "present", "choices": [ /* Close this container, View container */ ] }
332
+ ]
333
+ ```
334
+
335
+ The workflow stamps the containers it offers on the item row (`offeredContainers`). A
336
+ `match.target` entry that is a lone token naming a list facet allows every
337
+ entry, so any offered container takes the item and any other is refused with
338
+ the offer listed. A free container is a `max: 1` accumulator row, so of two
339
+ items racing for one free container exactly one lands; the other is refused with the
340
+ conflict copy. `refuse.missing` covers an offered container with no open row (taken
341
+ a moment ago): the scan is refused instead of falling through to the next
342
+ step. The third step has no subject gate: scanning a container with no item
343
+ held offers to close it.
344
+
345
+ Someone else's live claim on the held row refuses the act with their name
346
+ (`subject.claimedByOther: "allow"` lifts this). An item already in the
347
+ scanned container answers `executed` with `already: true`, and nothing is
348
+ written twice.
349
+
350
+ The badge can come before the item or between the item and the container. A
351
+ container scan that needs a badge answers `not_primed` with
352
+ `replayable: true`; the station keeps the subject, asks for the badge, and
353
+ replays the container scan once the badge primes.
354
+
355
+ Subject steps sit beside the steps a rule already has: with no subject held
356
+ they are skipped, so the same rule keeps working for scans made without the
357
+ item scan first.
358
+
201
359
  ## Identity schemes and acting identity
202
360
 
203
361
  A scheme with `kind: "identity"` is the badge layer. Its `target_facet`
@@ -214,7 +372,39 @@ scheme's policy:
214
372
  | Scheme field | Meaning |
215
373
  |---|---|
216
374
  | `grant_ttl_seconds` | How long the grant lives (1–86400). |
217
- | `grant_max_uses` | `0` = TTL-bound; `n` = the grant covers n requests that carry it, scans and work verbs alike (a strict one-request policy is `1`). The primed response carries `maxUses`; a single-shot grant is retired on the device the moment its one request returns, and a scan that comes back `not_primed` while a grant is held drops that grant, so the next scan runs unprimed instead of repeating the badge screen. A badge-gated submit likewise retires its badge once the write lands. |
375
+ | `grant_max_uses` | `0` = TTL-bound; `n` = the grant covers n acts (a strict one-act policy is `1`). |
376
+ | `grant_scope` | `action` (default): each act spends one use. `subject`: the first act binds the grant to the held subject; further acts on that subject spend nothing, and the grant acts on no other subject. One badge then covers one item's whole motion (every part checked off one record) and never carries to the next item. |
377
+
378
+ **The station decides the policy.** One printed badge works at every bench,
379
+ but benches want different policies: a workstation grants one act per badge
380
+ scan (claim, then submit, takes two scans), a high-volume placing bench grants
381
+ ten minutes of acts. A role may declare its stations' policy in `properties.badge_grant`:
382
+
383
+ ```jsonc
384
+ "properties": { "kiosk": true, "badge_grant": { "ttl_seconds": 600, "max_uses": 0 } }
385
+ ```
386
+
387
+ A badge scanned on a device signed in as a member of that role mints under
388
+ it; fields left out keep the badge scheme's values (`scope` is the role's
389
+ `grant_scope`). A device that belongs to several roles with different
390
+ policies uses the role it is locked to (the dashboard sends its kiosk role as
391
+ `stationRole` with every scan); with no role policy, or no way to choose
392
+ between several, the badge scheme's own policy applies. The policy is
393
+ validated where the role is written: an unusable `badge_grant` is a 400 from
394
+ the role API and fails a code-owned role's startup apply. The role page edits
395
+ it under **Members → Badge at this station**.
396
+
397
+ A grant is spent by **acts**, not by looking. A scan that shows, presents,
398
+ holds, or refuses reads the grant without spending it; a mutating verb spends
399
+ one use just before it writes, and a write that does not land (the step
400
+ falls through, the scan is refused) gives the use back. Every scan response
401
+ that carried a grant reports `acting: { consumed, remaining, bound }`, and
402
+ the escalation work routes report the uses left in the
403
+ `X-LT-Acting-Remaining` header. The device retires its copy of the grant when
404
+ the server reports none left, or when a subject-bound grant's subject is
405
+ done; reads never carry the grant at all. A scan that comes back
406
+ `not_primed` while a grant is held drops that grant, so the next scan runs
407
+ unprimed instead of repeating the badge screen.
218
408
 
219
409
  The grant rides subsequent scans as `actingToken`. Verbs then run **as the
220
410
  badged person under their own live RBAC** — the grant confers attribution,
@@ -300,6 +490,12 @@ to continue. The badge scan primes the session and the pending action
300
490
  completes on its own. With a live grant the stop-over never appears — the
301
491
  identity layer is invisible when all is well.
302
492
 
493
+ A field can take a scan while the form is open: with
494
+ [`x-lt-scan`](hitl/x-lt-scan.md), scanning a container into a claimed form
495
+ fills its container field (checked against what the record expects), and
496
+ `x-lt-scan-submit` submits once the scan fields are filled, which raises the
497
+ badge prompt. A scan no field accepts runs globally as usual.
498
+
303
499
  Badge tokens are printed credentials: seed them as long random strings,
304
500
  bind them server-side (`lt_users.metadata`), and treat badge possession
305
501
  with the same physical policy as any badge system. Unknown badges answer
@@ -314,9 +510,10 @@ renders the markdown; a configured route navigates.
314
510
 
315
511
  ## Executing a scan
316
512
 
317
- `POST /api/scan-codes/execute` takes `{ "code": "10:1:SN-123" }` and runs
318
- as the calling user under normal RBAC. Every terminal state is a structured
319
- 200 outcome:
513
+ `POST /api/scan-codes/execute` takes `{ "code": "10:1:SN-123" }` (plus
514
+ `actingToken` and `subject` when the station holds them) and runs as the
515
+ calling user under normal RBAC. Every terminal state is a structured 200
516
+ outcome:
320
517
 
321
518
  | Outcome | Meaning |
322
519
  |---------|---------|
@@ -329,6 +526,15 @@ as the calling user under normal RBAC. Every terminal state is a structured
329
526
  | `invalid_code` | The string parses under no enabled scheme |
330
527
  | `forbidden` | The caller's roles bar the matched action |
331
528
  | `conflict` | A concurrent actor won the row |
529
+ | `choices` | A `present` step located its row; `escalation` + `choices` included |
530
+ | `held` | A `hold` step located its row; `subject` included |
531
+ | `refused` | The scan was understood and not acted on; nothing written; `refusal.markdown` (and `refusal.expected`) included |
532
+ | `subject_stale` | The held subject moved on; `clearSubject: true` |
533
+ | `not_primed` | The act needs a badge; `notPrimed` included, `replayable: true` when the scan may run again once one primes |
534
+ | `identity_primed` / `identity_unknown` | A badge scan matched a person, or did not |
535
+
536
+ A step's payload rejected by the role's form schema answers `refused` with
537
+ the reason, never a raw 422.
332
538
 
333
539
  The endpoint is source-agnostic — anything that produces a string can drive
334
540
  it: a barcode scanner, an RFID reader, a camera decode, an MCP tool
@@ -348,8 +554,18 @@ When enabled, the dashboard listens for scans globally — any page, any focus
348
554
  state. A scanner paired as an HID keyboard "types" its decode and finishes
349
555
  with Enter; capture is **pattern-anchored**: a capture-phase window listener
350
556
  accumulates keystrokes freely, and when the terminator arrives it checks
351
- whether the recent keys end with a scan-code shape
352
- (`[1-9][0-9]:[0-9]:target` or the digits-only fixed form).
557
+ whether the recent keys end with a scan-code shape. The shapes come from the
558
+ configured schemes: each delimited version's `VV:C:target`, each fixed
559
+ scheme's exact digit lengths, and for a gtin scheme a whole 8, 12, 13 or 14
560
+ digit run whose check digit holds (a leading zero is part of the code). A
561
+ code never starts inside a longer run of digits.
562
+
563
+ - **Digits-only codes fire only at scanner speed** (avg ≤ 50 ms/key). A
564
+ typed PO number or count followed by Enter stays in its field, even when
565
+ its digits happen to form a valid barcode.
566
+ - **An open form can take the scan first.** A field with
567
+ [`x-lt-scan`](hitl/x-lt-scan.md) that accepts the scan's scheme is filled
568
+ instead; every other scan, and every badge, runs globally.
353
569
 
354
570
  - On a match, the terminator is swallowed, the code's characters are
355
571
  stripped back out of whatever editable held focus (byte-exact, at the
@@ -454,3 +670,20 @@ of each machine:
454
670
  Each rule ends on a broad `show-detail` and the "no twin found" fallback.
455
671
  Walk it hardware-free: run the twin farm, open the scan panel, and paste
456
672
  `10:1:<a-printing-serial>`.
673
+
674
+ ## The bag and bin demo
675
+
676
+ `examples/seed-scan-bins.ts` puts the bench motion on the
677
+ [rollup-bin](../examples/workflows/rollup-bin/) example. Start a `rollupBin`
678
+ with a `binKey` and a few `rollupMember` bags carrying the same `binKey`,
679
+ then at the scan station:
680
+
681
+ | Scan | Code | What happens |
682
+ |---|---|---|
683
+ | Bag | `12:0:<orderId>` | The station holds the bag and shows its bin, large |
684
+ | Badge | `11:0:<badge>` | Primes the person (before the bag works too) |
685
+ | Bin | `13:0:<binKey>` | The bag joins the bin and its own row completes; the station shows "Drop it in **bin-7**. All good." until the next scan |
686
+
687
+ Scan a different bin's label instead and the station refuses with the bag's
688
+ bin named, and nothing is written. Scan the bin before the badge and the
689
+ station asks for the badge, then places the bag on its own.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/long-tail",
3
- "version": "0.22.0",
3
+ "version": "0.22.2",
4
4
  "description": "Long Tail Workflows — Durable AI workflows with human-in-the-loop escalation. Powered by PostgreSQL.",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",