@hotmeshio/long-tail 0.18.1 → 0.19.1

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 (367) hide show
  1. package/build/api/escalations/index.d.ts +1 -1
  2. package/build/api/escalations/index.js +2 -1
  3. package/build/api/escalations/list.d.ts +11 -0
  4. package/build/api/escalations/list.js +31 -2
  5. package/build/api/users.d.ts +11 -0
  6. package/build/api/users.js +19 -0
  7. package/build/api/workflows/config.d.ts +5 -1
  8. package/build/api/workflows/config.js +9 -1
  9. package/build/api/workflows/index.d.ts +3 -0
  10. package/build/api/workflows/index.js +7 -1
  11. package/build/api/workflows/input-validation.d.ts +13 -0
  12. package/build/api/workflows/input-validation.js +38 -0
  13. package/build/api/workflows/invocable.d.ts +19 -0
  14. package/build/api/workflows/invocable.js +106 -0
  15. package/build/api/workflows/invocation.js +6 -0
  16. package/build/lib/db/schemas/034_workflow_input_schema.sql +13 -0
  17. package/build/lib/uuid.d.ts +11 -0
  18. package/build/lib/uuid.js +20 -0
  19. package/build/routes/escalations/list.js +10 -1
  20. package/build/routes/users.js +9 -0
  21. package/build/routes/workflows/config.js +2 -0
  22. package/build/routes/workflows/discovery.js +14 -0
  23. package/build/scripts/ortho-populate.d.ts +3 -1
  24. package/build/scripts/ortho-populate.js +15 -5
  25. package/build/sdk/index.d.ts +1 -0
  26. package/build/services/config/cache.js +2 -0
  27. package/build/services/config/read.js +4 -0
  28. package/build/services/config/sql.d.ts +2 -2
  29. package/build/services/config/sql.js +7 -5
  30. package/build/services/config/write.js +11 -1
  31. package/build/services/escalation/batch.js +3 -0
  32. package/build/services/escalation/bulk.js +8 -0
  33. package/build/services/escalation/crud.js +26 -0
  34. package/build/services/escalation/queries.d.ts +14 -1
  35. package/build/services/escalation/queries.js +49 -7
  36. package/build/services/escalation/sql.d.ts +4 -2
  37. package/build/services/escalation/sql.js +4 -0
  38. package/build/services/interceptor/index.js +7 -0
  39. package/build/services/invocation-access.d.ts +18 -0
  40. package/build/services/invocation-access.js +32 -0
  41. package/build/services/role/types.d.ts +1 -1
  42. package/build/services/role/types.js +1 -1
  43. package/build/services/user/auth.js +2 -2
  44. package/build/services/user/crud.d.ts +13 -0
  45. package/build/services/user/crud.js +26 -3
  46. package/build/services/user/index.d.ts +1 -1
  47. package/build/services/user/index.js +2 -1
  48. package/build/services/user/sql.d.ts +11 -5
  49. package/build/services/user/sql.js +18 -6
  50. package/build/services/workflow-invocation.js +4 -9
  51. package/build/shared/form-validation/index.d.ts +1 -0
  52. package/build/shared/form-validation/index.js +1 -0
  53. package/build/shared/form-validation/invoke-context.d.ts +7 -0
  54. package/build/shared/form-validation/invoke-context.js +11 -0
  55. package/build/shared/form-validation/validate-resolver-payload.d.ts +1 -1
  56. package/build/shared/form-validation/validate-resolver-payload.js +1 -0
  57. package/build/shared/form-validation/x-lt-help.d.ts +2 -2
  58. package/build/shared/form-validation/x-lt-help.js +2 -2
  59. package/build/shared/form-validation/x-lt-show-if.d.ts +1 -0
  60. package/build/shared/form-validation/x-lt-show-if.js +3 -0
  61. package/build/start/workers.js +2 -0
  62. package/build/system/mcp-servers/admin/workflows.js +15 -2
  63. package/build/tsconfig.tsbuildinfo +1 -1
  64. package/build/types/config.d.ts +6 -0
  65. package/build/types/index.d.ts +2 -0
  66. package/build/types/index.js +4 -1
  67. package/build/types/startup.d.ts +10 -0
  68. package/build/types/validation.d.ts +4 -2
  69. package/build/types/workflow-icons.d.ts +86 -0
  70. package/build/types/workflow-icons.js +92 -0
  71. package/dashboard/dist/assets/{AdminDashboard-DDY8bceq.js → AdminDashboard-Drs-9TRM.js} +2 -2
  72. package/dashboard/dist/assets/{AdminDashboard-DDY8bceq.js.map → AdminDashboard-Drs-9TRM.js.map} +1 -1
  73. package/dashboard/dist/assets/{AgentConfigPage-D47sHnup.js → AgentConfigPage-BDPDZ7-v.js} +3 -3
  74. package/dashboard/dist/assets/{AgentConfigPage-D47sHnup.js.map → AgentConfigPage-BDPDZ7-v.js.map} +1 -1
  75. package/dashboard/dist/assets/{AgentDetailPage-DwrfByA2.js → AgentDetailPage-CGBulM4_.js} +3 -3
  76. package/dashboard/dist/assets/{AgentDetailPage-DwrfByA2.js.map → AgentDetailPage-CGBulM4_.js.map} +1 -1
  77. package/dashboard/dist/assets/AgentsPage-D77JeViQ.js +2 -0
  78. package/dashboard/dist/assets/{AgentsPage-CLSjlyXy.js.map → AgentsPage-D77JeViQ.js.map} +1 -1
  79. package/dashboard/dist/assets/{AutoGrowTextarea-DgEQeRQw.js → AutoGrowTextarea-7X3T9La8.js} +2 -2
  80. package/dashboard/dist/assets/{AutoGrowTextarea-DgEQeRQw.js.map → AutoGrowTextarea-7X3T9La8.js.map} +1 -1
  81. package/dashboard/dist/assets/AvailableEscalationsPage-CqM_MOMy.js +2 -0
  82. package/dashboard/dist/assets/AvailableEscalationsPage-CqM_MOMy.js.map +1 -0
  83. package/dashboard/dist/assets/{BotPicker-CbL7gQFq.js → BotPicker-CC5vI_Sv.js} +2 -2
  84. package/dashboard/dist/assets/{BotPicker-CbL7gQFq.js.map → BotPicker-CC5vI_Sv.js.map} +1 -1
  85. package/dashboard/dist/assets/BulkAssignModal-BXiJnlqt.js +2 -0
  86. package/dashboard/dist/assets/BulkAssignModal-BXiJnlqt.js.map +1 -0
  87. package/dashboard/dist/assets/{CapabilitiesPage-Q9Pe0y_p.js → CapabilitiesPage-D9TC2iKy.js} +2 -2
  88. package/dashboard/dist/assets/{CapabilitiesPage-Q9Pe0y_p.js.map → CapabilitiesPage-D9TC2iKy.js.map} +1 -1
  89. package/dashboard/dist/assets/{CollapsibleSection-BZOtaWPR.js → CollapsibleSection-D10lGw_u.js} +2 -2
  90. package/dashboard/dist/assets/{CollapsibleSection-BZOtaWPR.js.map → CollapsibleSection-D10lGw_u.js.map} +1 -1
  91. package/dashboard/dist/assets/{ConfirmDeleteModal-6CidwpPx.js → ConfirmDeleteModal-BJdLN6No.js} +2 -2
  92. package/dashboard/dist/assets/{ConfirmDeleteModal-6CidwpPx.js.map → ConfirmDeleteModal-BJdLN6No.js.map} +1 -1
  93. package/dashboard/dist/assets/{CopyableId-4Lqlc5cU.js → CopyableId-Bn02zYxt.js} +2 -2
  94. package/dashboard/dist/assets/{CopyableId-4Lqlc5cU.js.map → CopyableId-Bn02zYxt.js.map} +1 -1
  95. package/dashboard/dist/assets/CountdownTimer-D9B-BjQ7.js +2 -0
  96. package/dashboard/dist/assets/CountdownTimer-D9B-BjQ7.js.map +1 -0
  97. package/dashboard/dist/assets/{CredentialsPage-dxjrUEyO.js → CredentialsPage-hniUAFBK.js} +2 -2
  98. package/dashboard/dist/assets/{CredentialsPage-dxjrUEyO.js.map → CredentialsPage-hniUAFBK.js.map} +1 -1
  99. package/dashboard/dist/assets/{CronLabel-D8oo8eaA.js → CronLabel-v9DCD2FX.js} +2 -2
  100. package/dashboard/dist/assets/{CronLabel-D8oo8eaA.js.map → CronLabel-v9DCD2FX.js.map} +1 -1
  101. package/dashboard/dist/assets/{DataTable-DFPMeqyQ.js → DataTable-7C7riavf.js} +2 -2
  102. package/dashboard/dist/assets/{DataTable-DFPMeqyQ.js.map → DataTable-7C7riavf.js.map} +1 -1
  103. package/dashboard/dist/assets/{DropZone-CWCh5ItZ.js → DropZone-XlcY8hEH.js} +2 -2
  104. package/dashboard/dist/assets/{DropZone-CWCh5ItZ.js.map → DropZone-XlcY8hEH.js.map} +1 -1
  105. package/dashboard/dist/assets/{ElapsedCell-FHHQ69G3.js → ElapsedCell-_xvVTK8N.js} +2 -2
  106. package/dashboard/dist/assets/{ElapsedCell-FHHQ69G3.js.map → ElapsedCell-_xvVTK8N.js.map} +1 -1
  107. package/dashboard/dist/assets/{EmptyState-lpUFYfun.js → EmptyState-DgC_6njU.js} +2 -2
  108. package/dashboard/dist/assets/{EmptyState-lpUFYfun.js.map → EmptyState-DgC_6njU.js.map} +1 -1
  109. package/dashboard/dist/assets/EntityTimelinePanel-1lT3jkCM.js +2 -0
  110. package/dashboard/dist/assets/EntityTimelinePanel-1lT3jkCM.js.map +1 -0
  111. package/dashboard/dist/assets/{EscalationListSchemaPage-D5L5lDNb.js → EscalationListSchemaPage-BeQUTUEO.js} +3 -3
  112. package/dashboard/dist/assets/{EscalationListSchemaPage-D5L5lDNb.js.map → EscalationListSchemaPage-BeQUTUEO.js.map} +1 -1
  113. package/dashboard/dist/assets/EscalationListView-uuZRMSLB.js +2 -0
  114. package/dashboard/dist/assets/EscalationListView-uuZRMSLB.js.map +1 -0
  115. package/dashboard/dist/assets/{EscalationSchemaPage-bYbfEpSB.js → EscalationSchemaPage-D4VlEwyS.js} +3 -3
  116. package/dashboard/dist/assets/{EscalationSchemaPage-bYbfEpSB.js.map → EscalationSchemaPage-D4VlEwyS.js.map} +1 -1
  117. package/dashboard/dist/assets/{EscalationsOverview-DjtIjL1b.js → EscalationsOverview-Bx6Zrzm-.js} +2 -2
  118. package/dashboard/dist/assets/{EscalationsOverview-DjtIjL1b.js.map → EscalationsOverview-Bx6Zrzm-.js.map} +1 -1
  119. package/dashboard/dist/assets/{EventTable-CihVTREC.js → EventTable-KEC3YWBf.js} +2 -2
  120. package/dashboard/dist/assets/{EventTable-CihVTREC.js.map → EventTable-KEC3YWBf.js.map} +1 -1
  121. package/dashboard/dist/assets/{FilterBar-Fin1IqHc.js → FilterBar-xwN_b5fj.js} +2 -2
  122. package/dashboard/dist/assets/{FilterBar-Fin1IqHc.js.map → FilterBar-xwN_b5fj.js.map} +1 -1
  123. package/dashboard/dist/assets/{GraphInvokePage-D77ILPd3.js → GraphInvokePage-DV7Y0fe2.js} +2 -2
  124. package/dashboard/dist/assets/{GraphInvokePage-D77ILPd3.js.map → GraphInvokePage-DV7Y0fe2.js.map} +1 -1
  125. package/dashboard/dist/assets/HomePage-G785SWcW.js +2 -0
  126. package/dashboard/dist/assets/HomePage-G785SWcW.js.map +1 -0
  127. package/dashboard/dist/assets/ListToolbar-zPVYPg3W.js +2 -0
  128. package/dashboard/dist/assets/{ListToolbar-DyHaJBTk.js.map → ListToolbar-zPVYPg3W.js.map} +1 -1
  129. package/dashboard/dist/assets/{McpOverview-qz7Ivf0G.js → McpOverview-CMGUq8hx.js} +2 -2
  130. package/dashboard/dist/assets/{McpOverview-qz7Ivf0G.js.map → McpOverview-CMGUq8hx.js.map} +1 -1
  131. package/dashboard/dist/assets/{McpQueryDetailPage-D6MB4CZM.js → McpQueryDetailPage-CdhLhaKn.js} +3 -3
  132. package/dashboard/dist/assets/{McpQueryDetailPage-D6MB4CZM.js.map → McpQueryDetailPage-CdhLhaKn.js.map} +1 -1
  133. package/dashboard/dist/assets/McpQueryPage-CNSNMagY.js +2 -0
  134. package/dashboard/dist/assets/{McpQueryPage-BqqRF5cQ.js.map → McpQueryPage-CNSNMagY.js.map} +1 -1
  135. package/dashboard/dist/assets/{McpRunDetailPage-DxLEhzhn.js → McpRunDetailPage-DBm8hUaW.js} +2 -2
  136. package/dashboard/dist/assets/{McpRunDetailPage-DxLEhzhn.js.map → McpRunDetailPage-DBm8hUaW.js.map} +1 -1
  137. package/dashboard/dist/assets/{McpRunsPage-BOsS4Hdf.js → McpRunsPage-B6rb5QBf.js} +2 -2
  138. package/dashboard/dist/assets/{McpRunsPage-BOsS4Hdf.js.map → McpRunsPage-B6rb5QBf.js.map} +1 -1
  139. package/dashboard/dist/assets/NamespacePill-Bb3revqD.js +2 -0
  140. package/dashboard/dist/assets/{NamespacePill-CJikgGzE.js.map → NamespacePill-Bb3revqD.js.map} +1 -1
  141. package/dashboard/dist/assets/OperationsPage-Dxk1fAMg.js +3 -0
  142. package/dashboard/dist/assets/OperationsPage-Dxk1fAMg.js.map +1 -0
  143. package/dashboard/dist/assets/OperatorDashboard-DBOscDnf.js +2 -0
  144. package/dashboard/dist/assets/{OperatorDashboard-C5rlijvh.js.map → OperatorDashboard-DBOscDnf.js.map} +1 -1
  145. package/dashboard/dist/assets/{PageHeader-DZmymFBS.js → PageHeader-Di5Sz3an.js} +2 -2
  146. package/dashboard/dist/assets/{PageHeader-DZmymFBS.js.map → PageHeader-Di5Sz3an.js.map} +1 -1
  147. package/dashboard/dist/assets/{PageHeaderWithStats-DuAm6YK2.js → PageHeaderWithStats-DutKJ-h2.js} +2 -2
  148. package/dashboard/dist/assets/{PageHeaderWithStats-DuAm6YK2.js.map → PageHeaderWithStats-DutKJ-h2.js.map} +1 -1
  149. package/dashboard/dist/assets/{PersonaDetailPage-JBdVvT5g.js → PersonaDetailPage-BGRdGpeR.js} +2 -2
  150. package/dashboard/dist/assets/{PersonaDetailPage-JBdVvT5g.js.map → PersonaDetailPage-BGRdGpeR.js.map} +1 -1
  151. package/dashboard/dist/assets/PersonasPage-DRzOZcVa.js +2 -0
  152. package/dashboard/dist/assets/PersonasPage-DRzOZcVa.js.map +1 -0
  153. package/dashboard/dist/assets/{PriorityBadge-BETdj4M-.js → PriorityBadge-r33mpDju.js} +2 -2
  154. package/dashboard/dist/assets/{PriorityBadge-BETdj4M-.js.map → PriorityBadge-r33mpDju.js.map} +1 -1
  155. package/dashboard/dist/assets/{ProcessDetailPage-CHnzOzQn.js → ProcessDetailPage-BLGjZXpM.js} +2 -2
  156. package/dashboard/dist/assets/{ProcessDetailPage-CHnzOzQn.js.map → ProcessDetailPage-BLGjZXpM.js.map} +1 -1
  157. package/dashboard/dist/assets/ProcessesListPage-5NnysKFC.js +2 -0
  158. package/dashboard/dist/assets/{ProcessesListPage-DuXTS5-v.js.map → ProcessesListPage-5NnysKFC.js.map} +1 -1
  159. package/dashboard/dist/assets/ResolverForm-Dy0yirtP.js +2 -0
  160. package/dashboard/dist/assets/ResolverForm-Dy0yirtP.js.map +1 -0
  161. package/dashboard/dist/assets/RoleDetailPage-C_LIoe1j.js +8 -0
  162. package/dashboard/dist/assets/RoleDetailPage-C_LIoe1j.js.map +1 -0
  163. package/dashboard/dist/assets/{RolePill-C9LSs1aK.js → RolePill-DhnVPK4q.js} +2 -2
  164. package/dashboard/dist/assets/{RolePill-C9LSs1aK.js.map → RolePill-DhnVPK4q.js.map} +1 -1
  165. package/dashboard/dist/assets/RolesPage-BmW1LzkU.js +2 -0
  166. package/dashboard/dist/assets/{RolesPage-h77-JOLd.js.map → RolesPage-BmW1LzkU.js.map} +1 -1
  167. package/dashboard/dist/assets/{RowActions-eYrzgQSj.js → RowActions-Bdd7z4eB.js} +2 -2
  168. package/dashboard/dist/assets/{RowActions-eYrzgQSj.js.map → RowActions-Bdd7z4eB.js.map} +1 -1
  169. package/dashboard/dist/assets/RunAsSelector-BwZTJ4Yj.js +2 -0
  170. package/dashboard/dist/assets/{RunAsSelector-DoLFJ0mZ.js.map → RunAsSelector-BwZTJ4Yj.js.map} +1 -1
  171. package/dashboard/dist/assets/ScanCodesPage-FiYIYB-m.js +2 -0
  172. package/dashboard/dist/assets/{ScanCodesPage-b5NZM_-f.js.map → ScanCodesPage-FiYIYB-m.js.map} +1 -1
  173. package/dashboard/dist/assets/{ScanSchemeDetailPage-t7X6ekaW.js → ScanSchemeDetailPage-BiAqtD-a.js} +2 -2
  174. package/dashboard/dist/assets/{ScanSchemeDetailPage-t7X6ekaW.js.map → ScanSchemeDetailPage-BiAqtD-a.js.map} +1 -1
  175. package/dashboard/dist/assets/ScanStationPage-8CdOtbCD.js +2 -0
  176. package/dashboard/dist/assets/{ScanStationPage-r02hkTAG.js.map → ScanStationPage-8CdOtbCD.js.map} +1 -1
  177. package/dashboard/dist/assets/{ScopeBadge-CjyC7iyO.js → ScopeBadge-CXUg0FHo.js} +2 -2
  178. package/dashboard/dist/assets/{ScopeBadge-CjyC7iyO.js.map → ScopeBadge-CXUg0FHo.js.map} +1 -1
  179. package/dashboard/dist/assets/{SegmentedTabs-DXNzY05q.js → SegmentedTabs-BpJh4Hwn.js} +2 -2
  180. package/dashboard/dist/assets/{SegmentedTabs-DXNzY05q.js.map → SegmentedTabs-BpJh4Hwn.js.map} +1 -1
  181. package/dashboard/dist/assets/{StatCard-BXQ8dD9Y.js → StatCard-nWY4sndl.js} +2 -2
  182. package/dashboard/dist/assets/{StatCard-BXQ8dD9Y.js.map → StatCard-nWY4sndl.js.map} +1 -1
  183. package/dashboard/dist/assets/{StatusBadge-BDeUwPtb.js → StatusBadge-DHVwxre2.js} +2 -2
  184. package/dashboard/dist/assets/{StatusBadge-BDeUwPtb.js.map → StatusBadge-DHVwxre2.js.map} +1 -1
  185. package/dashboard/dist/assets/{StickyPagination-BGHQbp0_.js → StickyPagination-DRuqKhZt.js} +2 -2
  186. package/dashboard/dist/assets/{StickyPagination-BGHQbp0_.js.map → StickyPagination-DRuqKhZt.js.map} +1 -1
  187. package/dashboard/dist/assets/{StreamMessageDetail-C-wfdbLJ.js → StreamMessageDetail-JwlbRiMn.js} +2 -2
  188. package/dashboard/dist/assets/{StreamMessageDetail-C-wfdbLJ.js.map → StreamMessageDetail-JwlbRiMn.js.map} +1 -1
  189. package/dashboard/dist/assets/{SwimlaneTimeline-Dnoz-me3.js → SwimlaneTimeline-hu3c4bCJ.js} +2 -2
  190. package/dashboard/dist/assets/{SwimlaneTimeline-Dnoz-me3.js.map → SwimlaneTimeline-hu3c4bCJ.js.map} +1 -1
  191. package/dashboard/dist/assets/{TagInput-D-vLSMQC.js → TagInput-BZ8vb2oW.js} +2 -2
  192. package/dashboard/dist/assets/{TagInput-D-vLSMQC.js.map → TagInput-BZ8vb2oW.js.map} +1 -1
  193. package/dashboard/dist/assets/{TaskDetailPage-c9IiAhVJ.js → TaskDetailPage-NqVZF3jW.js} +2 -2
  194. package/dashboard/dist/assets/{TaskDetailPage-c9IiAhVJ.js.map → TaskDetailPage-NqVZF3jW.js.map} +1 -1
  195. package/dashboard/dist/assets/{TaskQueuePill-sFRMrQ5D.js → TaskQueuePill-Bxir8WRl.js} +2 -2
  196. package/dashboard/dist/assets/{TaskQueuePill-sFRMrQ5D.js.map → TaskQueuePill-Bxir8WRl.js.map} +1 -1
  197. package/dashboard/dist/assets/TasksListPage-BQNaSzCn.js +2 -0
  198. package/dashboard/dist/assets/{TasksListPage-Dor8LSc5.js.map → TasksListPage-BQNaSzCn.js.map} +1 -1
  199. package/dashboard/dist/assets/TimeAgo-Cv35DOiI.js +2 -0
  200. package/dashboard/dist/assets/{TimeAgo-Dd8vmRnM.js.map → TimeAgo-Cv35DOiI.js.map} +1 -1
  201. package/dashboard/dist/assets/{TimestampCell-qJfC6-iT.js → TimestampCell-DHUXey9s.js} +2 -2
  202. package/dashboard/dist/assets/{TimestampCell-qJfC6-iT.js.map → TimestampCell-DHUXey9s.js.map} +1 -1
  203. package/dashboard/dist/assets/{ToolPill-OQ3mC11d.js → ToolPill-FkgTXIhq.js} +2 -2
  204. package/dashboard/dist/assets/{ToolPill-OQ3mC11d.js.map → ToolPill-FkgTXIhq.js.map} +1 -1
  205. package/dashboard/dist/assets/ToolTestPanel-DvuLFWqQ.js +2 -0
  206. package/dashboard/dist/assets/{ToolTestPanel-CEGY5jss.js.map → ToolTestPanel-DvuLFWqQ.js.map} +1 -1
  207. package/dashboard/dist/assets/{TopicDetailPage-SI80ZTVv.js → TopicDetailPage-TXX3QNSo.js} +3 -3
  208. package/dashboard/dist/assets/{TopicDetailPage-SI80ZTVv.js.map → TopicDetailPage-TXX3QNSo.js.map} +1 -1
  209. package/dashboard/dist/assets/{TopicsPage-DiVB8tNr.js → TopicsPage-h-oyTeEJ.js} +2 -2
  210. package/dashboard/dist/assets/{TopicsPage-DiVB8tNr.js.map → TopicsPage-h-oyTeEJ.js.map} +1 -1
  211. package/dashboard/dist/assets/UserName-BceRCeDF.js +2 -0
  212. package/dashboard/dist/assets/UserName-BceRCeDF.js.map +1 -0
  213. package/dashboard/dist/assets/WorkflowExecutionPage-CmHGcaN9.js +2 -0
  214. package/dashboard/dist/assets/{WorkflowExecutionPage-e-Q57yiJ.js.map → WorkflowExecutionPage-CmHGcaN9.js.map} +1 -1
  215. package/dashboard/dist/assets/{WorkflowPill-Dh3hIW1l.js → WorkflowPill--AYMW7u_.js} +2 -2
  216. package/dashboard/dist/assets/WorkflowPill--AYMW7u_.js.map +1 -0
  217. package/dashboard/dist/assets/WorkflowsDashboard-B0r2lQXk.js +2 -0
  218. package/dashboard/dist/assets/{WorkflowsDashboard-DLKzN80h.js.map → WorkflowsDashboard-B0r2lQXk.js.map} +1 -1
  219. package/dashboard/dist/assets/WorkflowsOverview-BTMc2PJ_.js +2 -0
  220. package/dashboard/dist/assets/{WorkflowsOverview-J-ITQiO5.js.map → WorkflowsOverview-BTMc2PJ_.js.map} +1 -1
  221. package/dashboard/dist/assets/{YamlWorkflowDetailPage-B_-ahhf1.js → YamlWorkflowDetailPage-DUiCVnZp.js} +2 -2
  222. package/dashboard/dist/assets/{YamlWorkflowDetailPage-B_-ahhf1.js.map → YamlWorkflowDetailPage-DUiCVnZp.js.map} +1 -1
  223. package/dashboard/dist/assets/YamlWorkflowsPage-BAuXfwi0.js +2 -0
  224. package/dashboard/dist/assets/{YamlWorkflowsPage-DQNeERXU.js.map → YamlWorkflowsPage-BAuXfwi0.js.map} +1 -1
  225. package/dashboard/dist/assets/{agents-BsJ-MizQ.js → agents-BvmLCk4h.js} +2 -2
  226. package/dashboard/dist/assets/{agents-BsJ-MizQ.js.map → agents-BvmLCk4h.js.map} +1 -1
  227. package/dashboard/dist/assets/{bots-q1UYqaIh.js → bots-f_tyhl4I.js} +2 -2
  228. package/dashboard/dist/assets/{bots-q1UYqaIh.js.map → bots-f_tyhl4I.js.map} +1 -1
  229. package/dashboard/dist/assets/capabilities-DNo5bqLx.js +2 -0
  230. package/dashboard/dist/assets/{capabilities-BvjPXUcJ.js.map → capabilities-DNo5bqLx.js.map} +1 -1
  231. package/dashboard/dist/assets/{controlplane-BwlNpoJ_.js → controlplane-Dyyj2Irb.js} +2 -2
  232. package/dashboard/dist/assets/{controlplane-BwlNpoJ_.js.map → controlplane-Dyyj2Irb.js.map} +1 -1
  233. package/dashboard/dist/assets/escalation-columns-DIigeTOK.js +2 -0
  234. package/dashboard/dist/assets/escalation-columns-DIigeTOK.js.map +1 -0
  235. package/dashboard/dist/assets/index-BMlQYOE1.js +67 -0
  236. package/dashboard/dist/assets/index-BMlQYOE1.js.map +1 -0
  237. package/dashboard/dist/assets/index-BNZknxFY.js +2 -0
  238. package/dashboard/dist/assets/{index-cvmt1plS.js.map → index-BNZknxFY.js.map} +1 -1
  239. package/dashboard/dist/assets/index-Bpvfr4OW.css +1 -0
  240. package/dashboard/dist/assets/{index-CldVY0UQ.js → index-C-Xr2V3F.js} +2 -2
  241. package/dashboard/dist/assets/{index-CldVY0UQ.js.map → index-C-Xr2V3F.js.map} +1 -1
  242. package/dashboard/dist/assets/index-CegPUKpc.js +3 -0
  243. package/dashboard/dist/assets/index-CegPUKpc.js.map +1 -0
  244. package/dashboard/dist/assets/{index-O9uuK2yO.js → index-CsKiRyhy.js} +2 -2
  245. package/dashboard/dist/assets/{index-O9uuK2yO.js.map → index-CsKiRyhy.js.map} +1 -1
  246. package/dashboard/dist/assets/index-D7ESqn22.js +2 -0
  247. package/dashboard/dist/assets/{index-BhzsR_gZ.js.map → index-D7ESqn22.js.map} +1 -1
  248. package/dashboard/dist/assets/{index-Hbbox1A4.js → index-DL83W7Gu.js} +2 -2
  249. package/dashboard/dist/assets/{index-Hbbox1A4.js.map → index-DL83W7Gu.js.map} +1 -1
  250. package/dashboard/dist/assets/index-DPLssnMW.js +6 -0
  251. package/dashboard/dist/assets/index-DPLssnMW.js.map +1 -0
  252. package/dashboard/dist/assets/{index-DsKqDMs4.js → index-DWse7ERF.js} +2 -2
  253. package/dashboard/dist/assets/{index-DsKqDMs4.js.map → index-DWse7ERF.js.map} +1 -1
  254. package/dashboard/dist/assets/index-DoETPlJP.js +11 -0
  255. package/dashboard/dist/assets/index-DoETPlJP.js.map +1 -0
  256. package/dashboard/dist/assets/{index-8Gmtvd8e.js → index-DvxxrFBo.js} +2 -2
  257. package/dashboard/dist/assets/{index-8Gmtvd8e.js.map → index-DvxxrFBo.js.map} +1 -1
  258. package/dashboard/dist/assets/{index-DDHoDZRg.js → index-PcyGjmBm.js} +2 -2
  259. package/dashboard/dist/assets/{index-DDHoDZRg.js.map → index-PcyGjmBm.js.map} +1 -1
  260. package/dashboard/dist/assets/{index-BvGIyZUt.js → index-e2X-s0EQ.js} +2 -2
  261. package/dashboard/dist/assets/{index-BvGIyZUt.js.map → index-e2X-s0EQ.js.map} +1 -1
  262. package/dashboard/dist/assets/{knowledge-DM0tNphj.js → knowledge-Coo6CKzo.js} +2 -2
  263. package/dashboard/dist/assets/{knowledge-DM0tNphj.js.map → knowledge-Coo6CKzo.js.map} +1 -1
  264. package/dashboard/dist/assets/{mcp-CQ_TD2P9.js → mcp-Cz5eFnP_.js} +2 -2
  265. package/dashboard/dist/assets/{mcp-CQ_TD2P9.js.map → mcp-Cz5eFnP_.js.map} +1 -1
  266. package/dashboard/dist/assets/{mcp-query-BP7KSGhc.js → mcp-query-BtnuO4LF.js} +2 -2
  267. package/dashboard/dist/assets/{mcp-query-BP7KSGhc.js.map → mcp-query-BtnuO4LF.js.map} +1 -1
  268. package/dashboard/dist/assets/{personas-DclHdHKK.js → personas-C2-LgpHQ.js} +2 -2
  269. package/dashboard/dist/assets/{personas-DclHdHKK.js.map → personas-C2-LgpHQ.js.map} +1 -1
  270. package/dashboard/dist/assets/{pipelines-LwuKJfj-.js → pipelines-TuXQvUA-.js} +2 -2
  271. package/dashboard/dist/assets/{pipelines-LwuKJfj-.js.map → pipelines-TuXQvUA-.js.map} +1 -1
  272. package/dashboard/dist/assets/resolver-payload-C1dXB3n6.js +2 -0
  273. package/dashboard/dist/assets/resolver-payload-C1dXB3n6.js.map +1 -0
  274. package/dashboard/dist/assets/seed-form-json-DvPsISxD.js +2 -0
  275. package/dashboard/dist/assets/seed-form-json-DvPsISxD.js.map +1 -0
  276. package/dashboard/dist/assets/{tasks-CDs-Li2v.js → tasks-OUzpu__z.js} +2 -2
  277. package/dashboard/dist/assets/{tasks-CDs-Li2v.js.map → tasks-OUzpu__z.js.map} +1 -1
  278. package/dashboard/dist/assets/{topics-BeOSelBX.js → topics-B8bdrBaW.js} +2 -2
  279. package/dashboard/dist/assets/{topics-BeOSelBX.js.map → topics-B8bdrBaW.js.map} +1 -1
  280. package/dashboard/dist/assets/useDebouncedValue-Cp5ZOtVE.js +2 -0
  281. package/dashboard/dist/assets/{useDebouncedValue-DhhAKjDC.js.map → useDebouncedValue-Cp5ZOtVE.js.map} +1 -1
  282. package/dashboard/dist/assets/{useFilterParams-crmlBw_l.js → useFilterParams-DBRw1Hcv.js} +2 -2
  283. package/dashboard/dist/assets/{useFilterParams-crmlBw_l.js.map → useFilterParams-DBRw1Hcv.js.map} +1 -1
  284. package/dashboard/dist/assets/{useNamespace-C-E2gNEK.js → useNamespace-DtQe3wZ5.js} +2 -2
  285. package/dashboard/dist/assets/{useNamespace-C-E2gNEK.js.map → useNamespace-DtQe3wZ5.js.map} +1 -1
  286. package/dashboard/dist/assets/{useYamlActivityEvents-CmLzX3YU.js → useYamlActivityEvents-CS22c15X.js} +2 -2
  287. package/dashboard/dist/assets/{useYamlActivityEvents-CmLzX3YU.js.map → useYamlActivityEvents-CS22c15X.js.map} +1 -1
  288. package/dashboard/dist/assets/{vendor-icons-R9b9gQ04.js → vendor-icons-DeBdvyKJ.js} +368 -143
  289. package/dashboard/dist/assets/vendor-icons-DeBdvyKJ.js.map +1 -0
  290. package/dashboard/dist/assets/vendor-query-DvWWuNIB.js +18 -0
  291. package/dashboard/dist/assets/vendor-query-DvWWuNIB.js.map +1 -0
  292. package/dashboard/dist/assets/{vendor-react-CdqEC2zv.js → vendor-react-_9U4mKFr.js} +2 -2
  293. package/dashboard/dist/assets/{vendor-react-CdqEC2zv.js.map → vendor-react-_9U4mKFr.js.map} +1 -1
  294. package/dashboard/dist/assets/{yaml-workflows-BgAsc6-I.js → yaml-workflows-HQIki3hI.js} +2 -2
  295. package/dashboard/dist/assets/{yaml-workflows-BgAsc6-I.js.map → yaml-workflows-HQIki3hI.js.map} +1 -1
  296. package/dashboard/dist/index.html +5 -5
  297. package/docs/api/http/workflows.md +25 -1
  298. package/docs/code-owned-configuration.md +1 -1
  299. package/docs/dashboard.md +179 -80
  300. package/docs/events.md +3 -1
  301. package/docs/faceted-routing.md +5 -1
  302. package/docs/hitl/iframe.md +35 -6
  303. package/docs/hitl/invoke-form.md +98 -0
  304. package/docs/hitl/x-lt-list-schema.md +3 -0
  305. package/docs/hitl/x-lt-show-if.md +11 -0
  306. package/docs/hitl-guide.md +4 -2
  307. package/docs/iam.md +1 -1
  308. package/docs/operations.md +61 -60
  309. package/docs/scan-codes.md +30 -3
  310. package/docs/workflows.md +1 -1
  311. package/package.json +1 -1
  312. package/dashboard/dist/assets/AgentsPage-CLSjlyXy.js +0 -2
  313. package/dashboard/dist/assets/AvailableEscalationsPage-D2ukvuQh.js +0 -2
  314. package/dashboard/dist/assets/AvailableEscalationsPage-D2ukvuQh.js.map +0 -1
  315. package/dashboard/dist/assets/BulkAssignModal-CMSgvHIs.js +0 -2
  316. package/dashboard/dist/assets/BulkAssignModal-CMSgvHIs.js.map +0 -1
  317. package/dashboard/dist/assets/CountdownTimer-B5lM7PfE.js +0 -2
  318. package/dashboard/dist/assets/CountdownTimer-B5lM7PfE.js.map +0 -1
  319. package/dashboard/dist/assets/EntityTimelinePanel-B5NuHnPF.js +0 -2
  320. package/dashboard/dist/assets/EntityTimelinePanel-B5NuHnPF.js.map +0 -1
  321. package/dashboard/dist/assets/HomePage-8XsQFqKL.js +0 -2
  322. package/dashboard/dist/assets/HomePage-8XsQFqKL.js.map +0 -1
  323. package/dashboard/dist/assets/ListToolbar-DyHaJBTk.js +0 -2
  324. package/dashboard/dist/assets/McpQueryPage-BqqRF5cQ.js +0 -2
  325. package/dashboard/dist/assets/NamespacePill-CJikgGzE.js +0 -2
  326. package/dashboard/dist/assets/OperationsPage-Cj1mJLMC.js +0 -3
  327. package/dashboard/dist/assets/OperationsPage-Cj1mJLMC.js.map +0 -1
  328. package/dashboard/dist/assets/OperatorDashboard-C5rlijvh.js +0 -2
  329. package/dashboard/dist/assets/PersonasPage-Xp_Gt6El.js +0 -2
  330. package/dashboard/dist/assets/PersonasPage-Xp_Gt6El.js.map +0 -1
  331. package/dashboard/dist/assets/ProcessesListPage-DuXTS5-v.js +0 -2
  332. package/dashboard/dist/assets/RoleDetailPage-C9GGHNf4.js +0 -8
  333. package/dashboard/dist/assets/RoleDetailPage-C9GGHNf4.js.map +0 -1
  334. package/dashboard/dist/assets/RolesPage-h77-JOLd.js +0 -2
  335. package/dashboard/dist/assets/RunAsSelector-DoLFJ0mZ.js +0 -2
  336. package/dashboard/dist/assets/ScanCodesPage-b5NZM_-f.js +0 -2
  337. package/dashboard/dist/assets/ScanStationPage-r02hkTAG.js +0 -2
  338. package/dashboard/dist/assets/TasksListPage-Dor8LSc5.js +0 -2
  339. package/dashboard/dist/assets/TimeAgo-Dd8vmRnM.js +0 -2
  340. package/dashboard/dist/assets/ToolTestPanel-CEGY5jss.js +0 -2
  341. package/dashboard/dist/assets/UserName-Dmm456xT.js +0 -2
  342. package/dashboard/dist/assets/UserName-Dmm456xT.js.map +0 -1
  343. package/dashboard/dist/assets/WorkflowExecutionPage-e-Q57yiJ.js +0 -2
  344. package/dashboard/dist/assets/WorkflowPill-Dh3hIW1l.js.map +0 -1
  345. package/dashboard/dist/assets/WorkflowsDashboard-DLKzN80h.js +0 -2
  346. package/dashboard/dist/assets/WorkflowsOverview-J-ITQiO5.js +0 -2
  347. package/dashboard/dist/assets/YamlWorkflowsPage-DQNeERXU.js +0 -2
  348. package/dashboard/dist/assets/capabilities-BvjPXUcJ.js +0 -2
  349. package/dashboard/dist/assets/escalation-columns-Bo3PfIZj.js +0 -2
  350. package/dashboard/dist/assets/escalation-columns-Bo3PfIZj.js.map +0 -1
  351. package/dashboard/dist/assets/index-B8JfGtQB.js +0 -3
  352. package/dashboard/dist/assets/index-B8JfGtQB.js.map +0 -1
  353. package/dashboard/dist/assets/index-Bb9ZZUyB.js +0 -5
  354. package/dashboard/dist/assets/index-Bb9ZZUyB.js.map +0 -1
  355. package/dashboard/dist/assets/index-BhzsR_gZ.js +0 -2
  356. package/dashboard/dist/assets/index-D88VWEVp.js +0 -67
  357. package/dashboard/dist/assets/index-D88VWEVp.js.map +0 -1
  358. package/dashboard/dist/assets/index-DBvkaa-B.js +0 -6
  359. package/dashboard/dist/assets/index-DBvkaa-B.js.map +0 -1
  360. package/dashboard/dist/assets/index-cvmt1plS.js +0 -2
  361. package/dashboard/dist/assets/index-qU_dXqP4.css +0 -1
  362. package/dashboard/dist/assets/useDebouncedValue-DhhAKjDC.js +0 -2
  363. package/dashboard/dist/assets/vendor-icons-R9b9gQ04.js.map +0 -1
  364. package/dashboard/dist/assets/vendor-query-RUZdMvN6.js +0 -18
  365. package/dashboard/dist/assets/vendor-query-RUZdMvN6.js.map +0 -1
  366. package/dashboard/dist/assets/workflows-CYv1CTU0.js +0 -2
  367. package/dashboard/dist/assets/workflows-CYv1CTU0.js.map +0 -1
package/docs/dashboard.md CHANGED
@@ -10,12 +10,15 @@ The sidebar organizes pages into six groups.
10
10
 
11
11
  The reactive, event-driven surface — where operations watch the floor and builders configure choreography.
12
12
 
13
- | Page | Route | Purpose |
14
- |------|-------|---------|
15
- | **Pace Board** | `/operations` | COO shop-floor view pace chart of actual-vs-target flow across every station role, station table with live metrics, and the station detail panel. Readable by every login (aggregate counts and trends) while `features.publicPaceBoard`, default on, stands. |
16
- | **Event Topics** | `/topics` | Topic catalogbrowse all known event topics with descriptions, schemas, and subscriber counts. |
17
- | **Agents** | `/agents` | Autonomous event-driven automations (labeled **Automations** when AI is not configured). Configure subscriptions, schedules, and knowledge domains. |
18
- | **Capabilities** | `/capabilities` | Browse MCP tools grouped by capability category, with a live run panel. |
13
+ | Page | Route | Icon | Purpose |
14
+ |------|-------|------|---------|
15
+ | **Pace Board** | `/pace` | Gauge | Actual-vs-target flow across a segment of roles a pace chart over the sequence, a live role table, and a role detail panel. Readable by every login (aggregate counts and trends) while `features.publicPaceBoard`, default on, stands. |
16
+ | **Trend Board** | `/trends` | TrendingUp | Where the time went the entity lens over a metadata facet (`serialNumber`, `orderId`): a state-mix summary, dwell rankings, and per-entity cross-queue timelines. Shares the combined board selector with the Pace Board. |
17
+ | **Event Topics** | `/topics` | Radio | Topic catalog browse all known event topics with descriptions, schemas, and subscriber counts. |
18
+ | **Agents** | `/agents` | Bot | Autonomous event-driven automations (labeled **Automations** when AI is not configured). Configure subscriptions, schedules, and knowledge domains. |
19
+ | **Capabilities** | `/capabilities` | Zap | Browse MCP tools grouped by capability category, with a live run panel. |
20
+
21
+ Pace Board and Trend Board are two anchors on one page — the pathname picks the board, and the shared board selector switches between a Pace segment and a Trend lens without a reload. `/operations` redirects to `/pace`. Both entries appear only for logins that may read the board (admins, superadmins, and every login while `features.publicPaceBoard` stands).
19
22
 
20
23
  ### Orchestrate
21
24
 
@@ -29,7 +32,7 @@ Each flavor exposes the same shape: configure, invoke, executions.
29
32
  | Page | Route | Purpose |
30
33
  |------|-------|---------|
31
34
  | **Procedural → Registry** | `/workflows/registry` | All discovered workflows with tier, queue, and access columns. Configure, certify, or invoke from here. |
32
- | **Procedural → Invoke** | `/workflows/durable/invoke` | Start any invocable procedural workflow. Two-panel layout with workflow selector and envelope editor. |
35
+ | **Procedural → Invoke Tool** | `/workflows/durable/invoke` | Start a tool the caller may invoke. Grouped list beside the form; rich x-lt-* forms when a workflow declares `inputSchema`. Builders reach it under Orchestrate, everyone else under Tools. |
33
36
  | **Procedural → Executions** | `/workflows/executions` | All procedural runs with status, duration, and tier. Click through to task records and escalation history. |
34
37
  | **Graph → Configure** | `/mcp/workflows` | Graph workflows available to the orchestrator — compiled deterministic YAML DAGs, grouped by namespace. |
35
38
  | **Graph → Invoke** | `/mcp/workflows/invoke` | Start any active graph flow. Same two-panel layout as procedural invoke. |
@@ -49,7 +52,7 @@ The LLM authoring add-on. Appears when an Anthropic key is configured.
49
52
  | Page | Route | Purpose |
50
53
  |------|-------|---------|
51
54
  | **Files** | `/files` | Browse and manage files in connected storage (MinIO/S3/GCS). |
52
- | **Knowledge** | `/knowledge` | Knowledge base entries for workflow context and retrieval. |
55
+ | **Knowledge** | `/knowledge` | Versioned knowledge base domain data (catalogs, reference tables, cascade options) that workflows and resolver forms read at runtime, every edit an immutable edition. |
53
56
 
54
57
  ### Identity & Access
55
58
 
@@ -58,9 +61,12 @@ The LLM authoring add-on. Appears when an Anthropic key is configured.
58
61
  | **Accounts** | `/admin/users` | User accounts and service accounts (bots). Create, edit, assign roles, manage API keys. |
59
62
  | **Roles** | `/admin/roles` | Define roles — the queues, forms, and membership that connect workflows to people. Pace Board dials, capacity settings, versioned schemas, members, and default pins live here. |
60
63
  | **Personas** | `/admin/personas` | Bundle roles into named, one-step assignments. Each linked role carries a relationship scope (write-all, write-self, read-all); assigning a persona composes the member's whole surface from its roles' pins and schemas. |
64
+ | **Scan Codes** | `/admin/scan-codes` | Configure barcode schemes and their scan-driven rules (event-condition-action over escalations). Shown when `features.scanCodes` stands. |
61
65
 
62
66
  ### Infrastructure
63
67
 
68
+ Builder-only and off by default. A superadmin or engineer opts in per browser with the **Infrastructure** toggle in the easter-egg Features panel (Ctrl or Cmd click the logo); the section then joins the sidebar. The pages themselves stay reachable by URL under the builder guard.
69
+
64
70
  Builder-only.
65
71
 
66
72
  | Page | Route | Purpose |
@@ -74,11 +80,13 @@ Builder-only.
74
80
  The top navigation bar contains:
75
81
 
76
82
  - **Home logo** — links to the home page (`/`), Recent Activity.
83
+ - **Search and run bar** — one input, two verbs: find an escalation by id, workflow, or a configured facet, or run a scan rule against a typed target. Search is opt-in via `search` in `start()` config or `LT_SEARCH_BAR`; run modes ride `features.scanCodes`. See [Search and run](#search-and-run).
77
84
  - **all** — links to `/escalations/available` with a live count of unclaimed escalations.
78
85
  - **mine** — links to `/escalations/queue` with a live count of escalations assigned to you.
86
+ - **scan** (Barcode icon) — opens the scan panel for manual code entry and capture settings, shown when `features.scanCodes` stands. See [Scan Codes](#scan-codes).
79
87
  - **events** — toggles the live event feed (builders and ops; doubles as the connection indicator).
80
88
  - **docs** (BookOpen icon) — toggles the in-app documentation drawer. Each page also has a contextual docs link next to its title that opens the drawer to the relevant section.
81
- - **User menu** — Credentials, theme picker (five accent themes), and Sign Out.
89
+ - **User menu** — **Credentials** (OAuth connections and API keys), **Link variables** (per-device facet bindings that scope pins and the Pace Board — see [Faceted Routing](faceted-routing.md#link-variables)), the **theme picker** (five accent themes), and **Sign Out**.
82
90
 
83
91
  ## Home — Recent Activity
84
92
 
@@ -111,25 +119,34 @@ workers: [
111
119
  certified: true,
112
120
  roles: ['reviewer', 'admin'],
113
121
  envelopeSchema: { data: { field1: '', field2: 0 } },
122
+ inputSchema: MY_INPUT_FORM, // x-lt-* form for the Invoke Tool page; see Invoke forms
114
123
  resolverSchema: { approved: true, notes: '' }, // deprecated legacy fallback — the escalation form is owned by the target role as a versioned form_schema
115
124
  },
116
125
  },
117
126
  ]
118
127
  ```
119
128
 
129
+ The detail page has three columns. **Identity** carries the type, an **Icon** picker with a filter over the curated `WORKFLOW_ICONS` set (the chosen glyph leads the workflow's row and heading on the Invoke Tool page in place of the tier glyph), the description, and the queue. **Invocation** edits the roles and the **Input Form**; with an input form declared, the envelope field narrows to the `metadata` stamped on every run. **Preview** renders that form live from the editor as operators will meet it, interactive so conditional sections can be walked before saving. **Unregister** sits with the header actions beside Cancel and Save. See [Invoke forms](hitl/invoke-form.md).
130
+
120
131
  **API:** `GET /api/workflows/discovered` returns the unified list. `PUT /api/workflows/:type/config` creates or updates a config entry. `DELETE /api/workflows/:type/config` removes it.
121
132
 
122
- ### Invoke Workflow
133
+ ### Invoke Tool
123
134
 
124
- A two-panel page for starting any invocable procedural workflow. The left panel lists invocable workflows grouped by task queue, with a queue select and search in the filter bar. Workflows with active cron schedules show a clock icon. Selecting a workflow opens the invocation form on the right:
135
+ Accessible at `/workflows/durable/invoke` to anyone the server lists an invokable workflow for. The server decides the list with the same predicate the invoke gate runs (`invocationRoles` on each config; empty means every authenticated user; superadmin and admin see everything, including active durable workers with no registration). Builders keep **Invoke Tool** under Orchestrate; every other persona gets a **Tools** nav section with the same **Invoke Tool** entry that appears only when the list is non-empty, and the route sends a caller with nothing to invoke home.
125
136
 
126
- - **Identity summary** shows who will execute (current user, configured bot, or admin override).
127
- - **Envelope editor** — dual-mode input: a structured form view (when `envelope_schema.data` has scalar fields) or a raw JSON editor. The form auto-generates fields from the schema with inferred types.
128
- - **Start Workflow** button — invokes the workflow and navigates to the executions page.
137
+ Every invokable workflow is presented as a tool. The list of tools takes the left quarter of the row, grouped by task queue, with the prompt **Choose a tool to begin** until one is selected. Names read as titles (`fleetTools` shows as **Fleet Tools**, queues the same way) and each row leads with the workflow's icon, or its tier glyph when none is declared. The first row is preselected, `?type=<WorkflowType>` tracks the choice, and every choice is a history entry, so the page opens on a form and the back button retraces picks. The form fills the rest of the row, its heading and Submit staying put while the body scrolls:
129
138
 
130
- Recurring (cron) execution is owned by Automations schedule workflows from the Agents page.
139
+ - **Heading and description** the icon and title, with the identifier, tier, and queue as metadata, then the config's one-line description. Keep reference material in `x-lt-help`; it appears in the side panel on demand.
140
+ - **Identity summary** — who will execute: the current user, the workflow's configured `execute_as` bot ("configured default"), or, for admins and superadmins, an override chosen from the bot picker ("admin override").
141
+ - **Certification checkbox** — for a certified workflow, stamps `metadata.certified` on this one run.
142
+ - **The form** — a workflow that declares `inputSchema` renders the x-lt-* form: sections, two columns, conditional fields and instruction blocks, and a side panel with **Instructions** (the interpolated `x-lt-help`) and **Issues** (violations, click to focus). Every other workflow renders the envelope template form from `envelopeSchema`, with its Form and JSON views. See [Invoke forms](hitl/invoke-form.md).
143
+ - **Submit** — posts `{ data, metadata }` to the invoke endpoint. The page stays put, reports the started id, and subscribes to that run's `system.workflow.{id}.completed` and `.failed` events, so the outcome and the workflow's returned `data` appear beside Submit without leaving the page. One click disarms the button until the person chooses **Submit again**. While live events are off, a warning beside the button offers a reconnect, since the result could not arrive otherwise. Builders also get a **View workflow** link to its execution. A `422` from the input schema gate lands in the Issues view.
131
144
 
132
- **API:** `POST /api/workflows/:type/invoke` starts a workflow.
145
+ Below 1280px the list folds into a select and the form takes the full width.
146
+
147
+ Recurring (cron) execution is owned by Automations — schedule workflows from the Agents page. The graph equivalent, **Graph → Invoke** (`/mcp/workflows/invoke`), starts a compiled YAML flow.
148
+
149
+ **API:** `GET /api/workflows/invocable` backs the list and the nav. `POST /api/workflows/:type/invoke` starts a workflow (body `{ data, metadata?, execute_as? }`, returns `202` with the workflow id; `422` with the canonical validation body when `input_schema` rejects the data).
133
150
 
134
151
  ### MCP Tool Designer
135
152
 
@@ -193,16 +210,18 @@ Execution history for all graph runs — both dynamic (agentic LLM loops) and co
193
210
 
194
211
  ### Procedural Executions
195
212
 
196
- Lists all procedural workflow runs across the system.
213
+ Accessible at `/workflows/executions`. Lists procedural workflow runs from one HotMesh namespace.
197
214
 
198
- - **Tier filter** (top) switch between All, Certified, and Durable to focus on specific workflow types.
199
- - **Columns:** Workflow name, workflow ID, status (running/completed/failed), start time, and duration.
200
- - **Click any row** to see the full execution detail: the swimlane timeline and events fill the main column, and a full-height side panel carries the record's facts. The main header stays quiet — just the title and a panel toggle; status, the refresh/copy toolbar, and the **Actions** menu (a small caret anchor at the right of the panel's icon row) all live in the panel. The panel and main column share the width as a flex set — the main column narrows as the panel expands.
201
- - **Details** status, workflow identity (type, ID, parent), task queue, start/end times, duration, history size, and activity counts.
202
- - **Escalations** every escalation the workflow raised, each a row with type, role, age, and status badge, so multiple escalations across the run's lifecycle read as a table rather than running on; related child tasks list the same way below.
203
- - **Duration** is computed from start to completion useful for identifying slow workflows or comparing performance across versions.
215
+ - **Namespace** — the `?namespace=` query param names the Postgres schema (HotMesh `app_id`) the list reads from; procedural runs live in `durable`, so the page defaults to `?namespace=durable`. Graph runs live in their own per-app namespaces and are browsed at [Graph Executions](#graph-executions) (`/mcp/executions`).
216
+ - **Tier filter** — All, Certified, or Durable. Certified narrows to workflows with a certified config; Durable narrows to those with none.
217
+ - **Type / Status / Search** filter by workflow type, run status (running / completed / failed), or a workflow-id keyword.
218
+ - **Columns:** Workflow ID (status dot + id), Type (pill with tier variant), Created, Updated, Duration, and hover Actions (filter-by-type, filter-by-status, and for superadmins — a jump to the workflow's registry entry).
219
+ - **Click any row** to open the execution detail (`/workflows/executions/:workflowId`). A tabbed main column — **Details** (input/output envelopes), **Execution Timeline** (the swimlane of activities, signals, timers, and child workflows), **Events** (the raw event stream) sits beside a full-height side panel that carries the record's facts. The panel and main column share the width as a flex set.
220
+ - **Details** (panel) status, workflow identity (type, id, parent), task queue, start/end times, duration, history size, and activity counts.
221
+ - **Escalations** (panel) — every escalation the workflow raised as a row (status dot, type, role, age), each linking to its detail page; related child tasks list below. Empty reads "This workflow has not escalated."
222
+ - **Actions** menu — Restart (prefills a fresh invoke from the start event), Terminate (running runs only), Compile into Pipeline (runs with tool calls), and jumps to worker / engine stream messages.
204
223
 
205
- **API:** `GET /api/workflows/executions` lists runs with tier, status, and pagination filters.
224
+ **API:** `GET /api/workflow-states/jobs?namespace=durable` lists runs (params `entity` for type, `status`, `search`, `registered` for tier, `sort_by`, `order`, `limit`, `offset`). `GET /api/workflow-states/:workflowId/execution` returns the detail. `POST /api/workflows/:workflowId/terminate` stops a running one.
206
225
 
207
226
  ### Accounts
208
227
 
@@ -219,11 +238,11 @@ User Accounts and Service Accounts live on the same page, separated by a tab tog
219
238
 
220
239
  Roles are the system's central organizer and gatekeeper: every hand-off between the digital side (running workflows) and the outside world crosses a role. When a workflow needs something only a person or external actor can provide, it raises an escalation into exactly one role's queue and pauses. Membership in the role grants access to that queue and every escalation it contains; each member's scopes determine which specific combinations of activities they may perform via those escalations — which items appear, which they can claim, resolve, or forward. Resolving an item resumes the workflow exactly where it paused.
221
240
 
222
- Because roles carry the queue, the form schema, the membership, and the capacity targets in one place, they are also the unit everything else is built on: [Accounts](#accounts) grant them, escalation views filter by them, and the [Pace Board](#pace-board) renders them as stations.
241
+ Because roles carry the queue, the form schema, the membership, and the capacity targets in one place, they are also the unit everything else is built on: [Accounts](#accounts) grant them, escalation views filter by them, and the [Pace Board](#pace-board) charts them.
223
242
 
224
243
  **Master list** (`/admin/roles`) — one row per role:
225
244
 
226
- - **Role** — an ops status dot (green marks roles that appear as stations on the [Pace Board](#pace-board)) beside the display name: the user-set title, else Title Case derived from the key.
245
+ - **Role** — an ops status dot (green marks roles shown on the [Pace Board](#pace-board)) beside the display name: the user-set title, else Title Case derived from the key.
227
246
  - **Key** — the exact technical role id.
228
247
  - **Description** — the role's human-facing summary.
229
248
  - **Preceded By** — the role's prior step (`parent_role`), linked. Prior steps compose the Pace Board's sequences.
@@ -238,15 +257,15 @@ Search filters by key, label, or description. **+ Add Role** creates a role here
238
257
 
239
258
  Accessible at `/admin/roles/:role`. One page per role — a quiet header carrying the role's identity, with the configuration organized into a five-section left sub-nav: **Identity · Pace Board · Schemas · Members · Pins**. The active section rides the URL (`?section=pace-board`), so deep links land on the right concern. One draft spans every section — switching sections never loses edits — and the **Save** button sits in the sub-nav footer, visible from every section. (Members, Pins, and Upstream Inputs save live.)
240
259
 
241
- **Identity** — display name and description, shown on role rows and station labels. The danger zone lives here too: a role nothing references can be deleted.
260
+ **Identity** — display name and description, shown on role rows and Pace Board labels. The danger zone lives here too: a role nothing references can be deleted.
242
261
 
243
262
  **Pace Board** — everything the board consumes about this role, in one column:
244
263
 
245
- - **Station** — the toggle that shows the role as a station on the [Pace Board](#pace-board).
264
+ - **Show this role on the Pace Board** — the visibility toggle (`ops_visible`) that charts the role on the [Pace Board](#pace-board).
246
265
  - **Capacity** — `sla_minutes`, `target_per_hour`, and `worker_count`; enter any two and the section hints the derived third (`throughput = workers / (sla / 60)`).
247
- - **Priority** — `priority_threshold_minutes` and the age facet (`priority_facet`) driving the Pace Board priority count and the jeopardy filter (blank = `sla_minutes` and `created_at`). The dials stay editable while the station is hidden — they also drive jeopardy in the queues.
248
- - **Entity** — the metadata key naming what moves through this station (`entity_facet`, e.g. `serialNumber`, `orderId`). Roles sharing a key form that entity's system: the [entity lens](operations.md#entity-lens), per-entity dwell, and timelines all derive from it. Once a key is set, the **States from** picker (`entity_state_source`) chooses how the station names the entity's state — **Station** (being here is one state, e.g. a servicing queue) or **Subtypes** (this one role holds several states named by each escalation's subtype, e.g. a fleet role parking `idle` / `printing`).
249
- - **Sequence** — board geometry, shown while the station is visible: **Prior Step** (places the role in one Pace Board sequence; a role with no prior step starts its own), **Upstream Inputs** (the roles this station also draws from in other sequences — mixin-like, many allowed, saved live, rendered as a merge glyph on the station, never a bend in the line), and **Home Segment** (lead the home Pace Board with this role's sequence; one role holds this).
266
+ - **Priority** — `priority_threshold_minutes` and the age facet (`priority_facet`) driving the Pace Board jeopardy count and the jeopardy filter (blank = `sla_minutes` and `created_at`). The dials stay editable while the role is hidden — they also drive jeopardy in the queues.
267
+ - **Entity** — the metadata key naming what moves through this role (`entity_facet`, e.g. `serialNumber`, `orderId`). Roles sharing a key form that entity's system: the [Trend Board](#trend-board) lens, per-entity dwell, and timelines all derive from it. Once a key is set, the **States from** picker (`entity_state_source`) chooses how the role names the entity's state — **Role** (being here is one state, e.g. a servicing queue) or **Subtypes** (this one role holds several states named by each escalation's subtype, e.g. a fleet role parking `idle` / `printing`).
268
+ - **Sequence** — board geometry, shown while the role is visible: **Prior Step** (places the role in one Pace Board segment; a role with no prior step starts its own), **Upstream Inputs** (the roles this role also draws from in other segments — mixin-like, many allowed, saved live, rendered as a merge glyph, never a bend in the line), and **Home Segment** (lead the home Pace Board with this role's segment; one role holds this).
250
269
 
251
270
  **Schemas** — the role's contracts:
252
271
 
@@ -293,6 +312,26 @@ Accessible at `/admin/personas` for admins, superadmins, and engineers — the s
293
312
 
294
313
  **API:** `GET /api/personas` lists. `POST /api/personas` creates; `PATCH`/`DELETE /api/personas/:key` edit and remove. `PUT`/`DELETE /api/personas/:key/roles/:role` manage links. `POST /api/personas/seed` applies a declarative spec set idempotently.
295
314
 
315
+ ### Scan Codes
316
+
317
+ Scan codes turn a barcode into an action on an escalation — a floor operator scans a printed code and an item is claimed, resolved, escalated, or opened, with no keyboard. The dashboard captures scans globally: an HID scanner types like a keyboard, and a pattern-anchored wedge accumulates keystrokes and fires on a terminator or a quiet period (scanner-speed keys plus a short silence auto-fire; hand-typed codes need Enter). The matched code is stripped byte-exact from whatever field had focus, so a scan never leaks into a form. The whole surface is gated by `features.scanCodes` in `start()` config (a per-browser toggle in the Features panel flips it for testing); the execute and config APIs work regardless.
318
+
319
+ A code encodes **`version:category:target`** (e.g. `10:1:SN-12345`):
320
+
321
+ - **version** (two digits) selects the **scheme** — how the target is parsed and which metadata facet it matches (`serialNumber`, `assetTag`).
322
+ - **category** (one digit) selects the **rule** under that scheme.
323
+ - **target** is the value matched against the scheme's facet.
324
+
325
+ **Rules are ECA over escalations.** A rule is an ordered list of event-condition-action steps, first match wins: the event is the scanned code, the condition is a query against the escalation (role, status, availability, metadata facets), and the action is a canonical verb (`show-detail`, `show-list`, `claim`, `claim-show-detail`, `release`, `resolve`, `escalate`, `cancel`, `present`). Mutations ride single-statement by-metadata operations under the caller's own role scope, and stamp provenance facets (`scanScheme`, `scanCategory`, `scanActionName`, `scannedAt`) on every transition. Ordering is the design: put the expected state first and a broad fallback last, since a scan is also a state query.
326
+
327
+ **Admin config** lives at `/admin/scan-codes` (scheme list) and `/admin/scan-codes/:version` (a scheme with its rules) — builder-gated (admin, engineer, superadmin). A scheme carries a name, its target facet, encoding, and — for identity schemes — a grant policy.
328
+
329
+ **Station surface.** `/scan/station` is a full-screen scan surface with an idle prompt, an info/choice screen (the current reality plus labeled choices when a rule presents options), and a badge prompt when identity is required. In **kiosk mode** — a login holding exactly one role whose `properties.kiosk` is set — the chrome falls away and only the role's list, escalation detail, and this scan screen are reachable. The header **scan** panel offers the same manual entry and capture settings on any page.
330
+
331
+ **Badge scanning grants acting identity.** A scheme of kind `identity` binds a badge value to a person (via a facet on `lt_users.metadata`). Scanning a badge primes an **ephemeral acting grant** minted through the internal keystore; it rides subsequent scans as the `X-LT-Acting-Token` header, and verbs then run **as the badged person under their own RBAC** — attribution, never privilege escalation. A badge on a work form belongs to the submission: the form names who will submit, holds if the wrong badge scans, and consumes the single-use grant on the write. A dead grant (expired, exhausted, revoked) fails loudly rather than executing silently.
332
+
333
+ **API:** `POST /api/scan-codes/execute` runs a code (`{ code, actingToken?, previousActingToken? }`, returning a structured outcome); `POST /api/scan-codes/execute-choice` runs a presented choice (the server re-validates config, row, identity, and RBAC). `GET/PUT/DELETE /api/scan-codes/schemes/:version` and `/schemes/:version/actions/:category` manage schemes and rules. See [Scan Codes](scan-codes.md) for the full concept doc and the four-corner printer demo.
334
+
296
335
  ### DB Maintenance
297
336
 
298
337
  Database housekeeping tools for keeping PostgreSQL healthy under sustained workflow load.
@@ -331,20 +370,39 @@ Messages are read-only. Status is derived from timestamps: pending (no timestamp
331
370
 
332
371
  ### All Escalations
333
372
 
334
- The central queue for all escalation activity across every workflow.
373
+ Accessible at `/escalations/available`. The central queue for escalation activity across every workflow — every place a running workflow paused to wait on a person or external actor.
374
+
375
+ - **Title = queue selector** — the page title reads as the selected role's friendly title, or "All Escalations" when unfiltered; clicking it switches queues over the same `?role=` param the filter bar mirrors.
376
+ - **Filter bar** — status (available / claimed / resolved / cancelled / expired), role, workflow type, priority, and time window. `available` means pending and either unclaimed or past its claim expiry.
377
+ - **Columns:** a **Summary** cell (status dot + description or type), **Assignee** (claimant, or `—`), **Role** pill, **Priority**, and **Created**. The status dot distinguishes pending, claimed, resolved, cancelled, and expired; notification escalations show a bell.
378
+ - **Faceted query** — beyond the filter bar, the queue takes a structured metadata query, all URL-backed so a query copy-pastes: `?facets={"orderId":"..."}` (equality, AND-ed), `?block=` (exclude), `?range=` (numeric bounds), `?exists=` (key present), `?roles=` (union of role queues), and `?orderBy=` (JSON sort). `?jeopardy=1` narrows to rows past the role's age threshold. List rows omit the envelope by default; `?include=envelope` pulls the full envelope and payload columns for rich views.
379
+ - **Metadata value affordances** — each metadata key/value row carries a three-icon triad on hover: **filter** (scope the current role's queue to `key = value`), **search** (the same facet match across all roles), and **history** (open the entity's cross-queue interval timeline in the right panel — every role this value moved through, with durations and gaps). Filter-present, search-present, history-past. History renders for string values, since the timeline's GIN containment match serves JSON-string facets.
380
+ - **View modes** (`?view=`) — `table` (the columns above), `timeline` (100 rows per page of cross-queue movement), and `rich`. When a role owns a [list schema](#escalations-list-schema), the list defaults to the role-authored `rich` view (the live item as a card, plus a load-on-demand history) with a **Table view** toggle back.
381
+ - **Claim** — the claim action locks an item to your user for a claim window (default 30 min). Only members of the role see its pending items; a member scoped `read_self` lands directly on their own assigned item rather than browsing the queue. Re-claiming your own expired item extends it rather than failing.
382
+ - **Bulk actions (admin)** — row checkboxes surface a selection bar: **Claim**, **Assign**, **Unassign**, **Escalate** (move to another role), and **Cancel**. Assign opens a modal for the target user and claim duration; rows under a live claim are skipped unless the modal's takeover checkbox (shown when the selection includes live claims) reassigns them — admin/superadmin only. Unassign returns claimed rows to the pool. Each response reports how many rows were skipped and why. Scoped to a single role, the Cancel verb and its confirmation speak the role form's `x-lt-labels.cancel` vocabulary (`false` hides it). Assign also accepts a faceted query instead of an id list, so "claim everything matching this facet" is one call.
335
383
 
336
- - **Title = queue selector** the page title reads as the selected role's friendly title, or "All Escalations" when unfiltered; clicking it switches queues over the same `?role=` param the filter bar mirrors. The **My Escalations** personal inbox (`/escalations/queue`) carries the identical control, titling itself "My Escalations" or the filtered role's title.
337
- - **Filter bar** — filter by status (pending/claimed/resolved), role, workflow type, priority, and time window.
338
- - **Columns:** Escalation ID, workflow type, role, status, priority, created time, and claimed-by user.
339
- - **Metadata value affordances** — each metadata key/value row in the list carries a three-icon triad on hover: **filter** (filter the current role's queue to rows where the key equals this value), **search** (the same facet match across all roles), and **history** (open the entity's cross-queue interval timeline in the right panel — every station this value has moved through, with durations and gaps). Filter-present, search-present, history-past. The history affordance renders for string values, since the timeline's GIN containment match serves JSON-string facets.
340
- - **Rich list view** — when the list is scoped to exactly one role (`?role=<role>`) and that role owns a [list schema](#escalations-list-schema), a role-authored view renders in place of the table (the live item as a card, plus a load-on-demand history), with a **Table view** toggle back to the columns.
341
- - **Claim** — click the claim action to lock an escalation to your user. Only users with matching roles see pending escalations. The queue list and aggregate stats reflect `read_all` memberships — a member scoped to `read_self` lands directly on their own assigned item in user mode rather than browsing the full queue.
342
- - **Bulk actions (admin)** — row checkboxes surface a selection bar with **Claim**, **Assign**, **Unassign**, **Escalate** (move to another role), and **Cancel**. Assign opens a modal to pick the target user and claim duration; rows under a live claim are skipped unless the modal's takeover checkbox (shown when the selection includes live claims) is checked, which reassigns them — admin/superadmin only. Unassign returns claimed rows to the available pool. The response reports how many rows were skipped. When the list is scoped to a single role, the Cancel button and its confirmation speak the role form's `x-lt-labels.cancel` vocabulary (`false` hides the verb).
343
- - **Admin claim override (detail page)** — when an item is claimed by someone else, admins and superadmins see **Reassign…** (hand the claim to another user via the assign modal, takeover implied) and **Return to queue** (unassign) in the action bar.
344
- - **Resolve** — after claiming, submit a resolver payload. The form is pre-filled from the role's versioned `form_schema` field defaults and from the workflow's seeded `envelope.formDefaults` (reverse-mapped through each field's `x-lt-bind`). The dashboard maps the flat form to the nested payload via `x-lt-bind` and the submitted payload is stored as-is. Resolution triggers a workflow re-run with the resolver data injected. A `member` whose `write_scope` is `self` can resolve only items already assigned to them; `write_scope=none` is read-only.
345
- - **Side panel** — a slide-in beside the resolve form with switchable views, selected by the icon set at its top: **Help** (the form's `x-lt-help` markdown, `{{domain.path}}`-interpolated against the live record, or a state-aware hint such as "Claim this escalation to enable the form"), **Details** (status, role, priority, claim provenance, timestamps, and — for builders — identifier links), **AI Analysis** (what triage diagnosed and corrected — shown when AI is enabled and triage data is present), **Metadata** (the row's metadata values), **Context** (input envelope, escalation context, resolver payload), and **Record** (the raw escalation JSON, builders only). The panel and form share the width as a flex set — the form column narrows as the panel expands. It opens expanded on Help when the form carries `x-lt-help`, stays hidden otherwise, and the page-header panel button toggles it either way.
384
+ **API:** `GET /api/escalations` lists with filters; `POST /api/escalations/search-by-facets` backs the faceted query. `POST /api/escalations/bulk-claim`, `/bulk-assign`, `/bulk-unassign`, `PATCH /api/escalations/bulk-escalate`, `POST /api/escalations/bulk-cancel` drive the selection bar.
346
385
 
347
- **API:** `GET /api/escalations` lists with filters. `POST /api/escalations/:id/claim` claims. `POST /api/escalations/:id/resolve` resolves.
386
+ ### My Escalations
387
+
388
+ Accessible at `/escalations/queue`. The personal inbox — the items currently assigned to you across your roles. It carries the same title-as-role-selector, the same rich/table views, and the same metadata affordances as All Escalations, minus the bulk selection bar (every row is already yours). A **Claim expiry** column counts down each item's remaining window. The shell also auto-navigates here on hand-off: when a workflow assigns a follow-on escalation to you (`assigned_to = you`), the dashboard opens that item's detail directly.
389
+
390
+ ### Escalation Detail & Resolve Form
391
+
392
+ Accessible at `/escalations/detail/:id`. Where a person completes the work the workflow is waiting on. The main column is the resolve form; a switchable side panel carries context.
393
+
394
+ - **Lifecycle** — pending → claimed → resolved (or cancelled / expired). The sticky action bar shows the verbs valid for the current state: **Claim**, **Submit** (resolve), **Release**, **Cancel**. The form is read-only until you hold a live claim.
395
+ - **Form contract** — the form is the target role's versioned [`form_schema`](#escalation-schema), rendered flat. Fields pre-fill from two sources: the workflow's `envelope.formDefaults` (reverse-mapped through each field's `x-lt-bind` path) and the schema's own field defaults. On submit, the dashboard rebuilds the nested payload from the flat form via `x-lt-bind` and posts it as the resolver payload; password fields are redacted to short-lived ephemeral tokens before storage.
396
+ - **Validation** — one isomorphic pass runs on the client (to gate submit) and again on the server when the role sets `enforce_schema`, returning a canonical `422` with field-level errors. It covers required/type/enum/bounds, dynamic `x-lt-minimum`/`x-lt-maximum` (with `{{token}}` interpolation), and the root-level `x-lt-require-any` / `x-lt-require-sum` groups. Fields hidden by `x-lt-showIf` never block submission.
397
+ - **Resumption** — resolving resumes the paused workflow. When the escalation was created by `conditional()` (a HotMesh Leg1 write), the resolve is the signal: the run resumes in place with the payload injected, in one atomic Postgres statement — no re-run. A **notification** escalation (no `workflowType`) has no run to resume; **Acknowledge** still validates and submits the full form payload, resolving it atomically.
398
+ - **Canned actions** — a form may declare `x-lt-actions`: extra action-bar buttons that submit a preset payload in one click (Approve / Reject / Skip), bypassing field editing.
399
+ - **Submit guard** — `x-lt-submit-guard` gates a parent on a child query (e.g. "every item in this batch must resolve first"): the dashboard disables Submit with a live count while rows remain, and the server re-checks the guard atomically inside the resolving `UPDATE` so no race slips through. `autoResolveWhenEmpty` submits the parent the moment the query empties.
400
+ - **Transition hand-off** — `x-lt-transition` replaces the return-to-previous-page jump with a short wait screen: the workflow assigns a follow-on escalation back to the submitter, the screen detects it and navigates onward (or falls back to `x-lt-transition-done` after a bounded wait). This is the submit → side-effect → next-step chain (submit, a label prints, the harvest task opens).
401
+ - **Batch resolution** — a `conditional({ batch })` escalation accumulates N items on one row; `POST /api/escalations/:id/resolve-batch-item` submits each (claim-agnostic by default, `assertClaim` to require your own live claim), and the last item wakes the workflow with the full collection.
402
+ - **Side panel** — switchable views selected by the icon strip: **Help** (the form's `x-lt-help` markdown, `{{domain.path}}`-interpolated against the live record — escalation, metadata, envelope, payload, resolver, and `lookup.*` — or a state-aware hint like "Claim this escalation to enable the form"), **Details** (status, role, priority, claim provenance, timestamps, and for builders the identifier links), **AI Analysis** (what triage diagnosed and corrected, when AI is on), **Metadata** (the row's facets, with the same triad), **Context** (input envelope, escalation context, resolver payload), **Record** (the raw JSON, builders only), and **Errors** (the last submit's validation failures). The panel and form share the width as a flex set; it opens expanded on Help when the form carries `x-lt-help`.
403
+ - **Admin claim override** — when an item is held by someone else, admins and superadmins see **Reassign…** (hand the claim to another user, takeover implied) and **Return to queue** (unassign) in the action bar.
404
+
405
+ **API:** `POST /api/escalations/:id/claim` claims, `/release` releases, `/resolve` resolves; `/resolve-batch-item` submits one batch item; `/escalate` moves; `/cancel` cancels. The role's form and its versions come from `GET /api/roles/:role/schema`.
348
406
 
349
407
  ### Escalations Overview
350
408
 
@@ -352,28 +410,51 @@ Accessible at `/escalations`. A statistics dashboard for escalation health acros
352
410
 
353
411
  - **Time window selector** — toggle between 1h, 24h, 7d, and 30d views.
354
412
  - **Summary cards** — open (pending), claimed (in progress), created (new), and resolved counts for the selected window.
355
- - **Role breakdown table** — groups escalations by role so you can see which teams have the most pending work. Useful for identifying bottlenecks and rebalancing workload.
413
+ - **Role breakdown table** — groups escalations by role so you can see which teams have the most pending work; cells link into the filtered queue. Useful for identifying bottlenecks and rebalancing workload.
356
414
 
357
415
  ### Pace Board
358
416
 
359
- Accessible at `/operations`. The COO shop-floor view of the roles system: actual-vs-target flow across every station, rendered as a pace chart with a station table and detail panel below.
417
+ Accessible at `/pace`. The live picture of how work flows across a segment of roles: actual-vs-target throughput, rendered as a pace chart over a role table with a role detail panel. It answers the question a COO actually asks — *are we keeping up, and where is work backing up?*
418
+
419
+ The board is [Roles](#roles) end-to-end. Every role on it has **Visible in Operations** on; segments are composed from each role's **Prior Step** (`parent_role`); cross-segment feeds come from **Upstream Inputs**; the target line comes from each role's capacity settings. Configuring the board *is* configuring roles — the **Configure** action opens [Role Detail](#role-detail). See [Operations](operations.md) for the full concept doc.
420
+
421
+ **Segments.** A segment is a sequence of roles. A role with no prior step (or whose prior step lies outside the visible set) starts a segment, followed by its `parent_role` descendants in dependency order; the longest segment leads. The table is the ground truth of the queues; the chart is the line drawn through them.
422
+
423
+ **The combined board selector.** One menu switches views. Its **Pace Board** group lists the segments (each row: the segment title, its role count, pending total, and a jeopardy count); its **Trend Board** group lists the entity [lenses](#trend-board) (each `by <facet>`, with a live in-queue count). Choosing a segment stays on `/pace`; choosing a lens navigates to `/trends`. The collapsed button reads the active segment title, or `by <facet>` on a lens.
360
424
 
361
- The board is [Roles](#roles) end-to-end. Every station is a role with its **Ops** toggle on; sequences are composed from each role's Prior Step (`parent_role`); cross-sequence feeds come from Upstream Inputs; the red target line comes from each role's capacity settings. Configuring the board *is* configuring roles the **Configure** button in the header goes straight there.
425
+ - **Segment deep link** the active segment rides `?fragment=<origin role>`; each switch is a history entry, so a shared URL opens the same segment and back/forward walk them.
426
+ - **Period** — `15m`, `1h`, `24h`, `7d`, `30d`, default `1h`. It is deep-linked as `?period=` (the default stays out of the URL for clean links) and carries across a Pace↔Trend switch. It bounds resolved counts and the percentile metrics; `pending` is always the live count.
427
+ - **Pace chart** — the segment's roles on the X axis in dependency order. A muted-gray dashed **target** polyline sits at each role's expected count (`target_per_hour × window hours`); a green **actual** (resolved) curve with a faint area fill reads against it. The live queue shows as two faint stacked bands beneath — **claimed/worked** (orange) and **waiting/unclaimed** (sky). Each role is a circle on the resolved curve whose radius grows with volume; the selected role gets a ring. A `lin | log` toggle switches the Y axis (log by default, so small and large queues stay legible together).
428
+ - **Jeopardy** — a role with unclaimed items past its age threshold carries a jeopardy count (warning triangle) in its row and on the segment menu. Age is measured from the role's **priority facet** (a metadata timestamp such as an order's authorized date; `created_at` when unset) against its **priority threshold** (`sla_minutes` when unset). Clicking it opens the jeopardy deep link — `/escalations/available?role=<r>&jeopardy=1&view=table&orderBy=[{…,"direction":"asc"}]` — the table filtered to exactly the counted items (one server-side predicate feeds both the count and the list, so totals always match), oldest-first by the same facet. A jeopardy pill above the list names the filter; clearing it widens back to the full queue.
429
+ - **Merge glyph** — a role that declares upstream inputs shows a small dashed merge glyph at its floor position: deliberately a symbol, never a bend in the line, because the upstream is an input, not a descendant. Hover names the feeding roles; click jumps to their segment.
430
+ - **Role table** — columns in order: **NAME**, **ROLE** (the id, wide viewports), **TARGET/H** and **SLA/M** (edit inline), **WORKERS** (derived — `workers = target ÷ (60 ÷ sla)`), then **PENDING**, **CLAIMED**, **RESOLVED** tinted with the chart's hues (each cell links into the queue filtered by that status), **P99 WAIT** and **P99 WORK** (wide viewports), a **MIX** time-in-state bar, a **TREND** mini-bar, and **ACTIONS** (view queue, configure, jeopardy). The TREND bar reads pending-to-target ratio: amber above 1.0 (backlog), gray below 0.2 (idle), green between.
431
+ - **Role detail panel** — opens on a row or circle click: the role's identity, its own independent period toggle, the full wait/work percentile breakdown, SLA target and worker count, the time-in-state mix and per-entity timelines inline, and links into the queue.
432
+ - **Scope pill** — when your roles declare [link variables](faceted-routing.md#link-variables), a scope pill sits in the header. Your device binding (`facility = north`) narrows the whole board — counts, mix, and every timeline — to that facet, or reads `All` when unbound. It opens the Link variables modal; the value picker's choices come from `GET /api/escalations/facet-values?key=<facet>`.
433
+ - **Live updates** — escalation events invalidate the metrics and analytics through the shared realtime scheduler (SUMMARY tier, coalesced); the refresh button forces a reload. Every login may read the board while `features.publicPaceBoard` (default on) stands; turning it off narrows metrics to role membership and the board to admins and superadmins.
362
434
 
363
- Execution is a graph; this page tells its story as **sequences**. Each station role with no prior step (or whose prior step is outside the station set) starts a sequence, followed by its `parent_role` descendants in dependency order. The longest sequence leads. The table is always the ground truth of the queues; the SVG is the narrative line drawn through them.
435
+ A role joins the board from its [Role Detail Pace Board](#role-detail) section: the visibility toggle, the capacity settings (`sla_minutes`, `target_per_hour`, `worker_count`), the priority dials (`priority_threshold_minutes`, `priority_facet`), and the sequence placement (`parent_role`, upstream inputs).
364
436
 
365
- - **Sequence picker** — when more than one sequence exists, tabs appear above the chart, one per sequence, named by its origin role with the station count alongside. The active sequence is deep-linked (`?fragment=<origin role>`) and each switch is a browser-history entry, so a shared URL opens the same sequence and back/forward walks between them.
366
- - **Period selector** — `15m`, `1h`, `24h`, `7d`, `30d`. Controls the lookback window for resolved counts, percentile metrics, and throughput.
367
- - **Pace chart** — connects the active sequence's stations in process dependency order and plots absolute counts for the selected window: a straight red target polyline (`target_per_hour × window hours`) against a smooth actual (resolved) curve with a light area fill. The queue splits into two stacked bands — claimed-and-worked (indigo) and waiting-unclaimed (sky). Station circles are colored by pace ratio (green ≥ 100%, amber ≥ 60%, red below).
368
- - **Priority badge (jeopardy)** — a station with unclaimed items past its age threshold carries a powder-blue circle with the count. Age is measured from the role's priority facet (a metadata timestamp such as the order's authorized date; `created_at` when unset) against its priority threshold (`sla_minutes` when unset). Clicking the badge opens the **jeopardy deep link**: `/escalations/available?role=<r>&jeopardy=1&view=table&orderBy=[{"field":…,"direction":"asc"}]` — the table view filtered to exactly the counted items (the same server-side predicate produces the count and the list, so the totals always match), ordered oldest-first by the same facet. A red `in jeopardy · > <limit>` pill above the list names the active filter; its ✕ widens back to the full queue. The operator home's Task Queue cards carry the same pill and link.
369
- - **Merge affordance** — a station that declares upstream inputs shows a small dashed merge glyph at its floor position. It is deliberately a symbol, never a bend in the line: the upstream is an input, not a descendant. Hover names the feeding roles; click jumps to their sequence.
370
- - **Station table** — one row per station: ROLE (with a merge icon marking cross-sequence inputs), TARGET/H, then PENDING, ACTIVE, RESOLVED in column bands carrying the chart's hues, P99 WAIT, P99 WORK, and a TREND mini-bar. TREND shows the live backlog-to-target ratio while the queue has items; when the queue is idle it shows the period's throughput efficiency, marked with `↩`. Stations with priority items show a powder-blue sub-row linking to the queue ordered oldest-first by the priority facet.
371
- - **Station detail panel** — opens on row or circle click. Shows the role's identity, an independent period toggle, and the full metric breakdown (wait/work percentiles, SLA target, worker count, links to the queue).
372
- - **Live updates** — escalation events refresh the metrics push-driven and debounced; the header's refresh button forces a reload.
437
+ **API:** `GET /api/escalations/station-metrics?period=<window>&facets=<json>` returns the per-role live counts and windowed percentiles.
373
438
 
374
- A role joins the board via the **Ops** toggle on its [Role Detail](#role-detail) page; the capacity settings (`sla_minutes`, `target_per_hour`, `worker_count`) and the priority dials (`priority_threshold_minutes`, `priority_facet`) drive the computed metrics. See [Operations](operations.md) for the full concept doc.
439
+ ### Trend Board
375
440
 
376
- **API:** `GET /api/escalations/station-metrics?period=24h`
441
+ Accessible at `/trends`. The entity lens — *where did the time go?* Instead of role-by-role throughput, it follows one **entity** (a `serialNumber`, an `orderId`) through every role that handles it and shows how its time splits across states. It shares the [combined board selector](#pace-board), the period, and the scope pill with the Pace Board.
442
+
443
+ The lens is driven by two dials on each role's [Role Detail → Pace Board](#role-detail) section:
444
+
445
+ - **`entity_facet`** — the `metadata` key naming what moves through the role (`serialNumber`, `orderId`). Roles sharing a key form that entity's **system**. Each distinct facet becomes one lens, deep-linked as `?lens=<facet>` (default: the first facet).
446
+ - **`entity_state_source`** — how a role names the entity's state: **Station** (being in this role is one state, e.g. a servicing queue) or **Subtypes** (one role holds several states named by each escalation's subtype, e.g. a fleet role parking `idle` / `printing`).
447
+
448
+ Three tiers, aggregate to individual:
449
+
450
+ - **Where the time went** — a ranked-bar legend of states by total dwell over the window (top rows, with a "+N more stages" fold), beside an insight panel: the leader's share as a headline percentage, `<state> is the biggest time sink`, and `across N stages · M <entity> in queue now` for the selected period.
451
+ - **Slice by** — `?slice=<facet>` splits the system into small-multiple columns, one per value of that key (e.g. `model` → `p1s` vs `h2s`), ranked by dwell. `?sliceValue=<value>` focuses one value with a paginated entity list.
452
+ - **Entity table** — one row per entity, ranked by tracked time: the entity value, a current-state dot, its own dwell band, its total tracked time, and a history action. A **find** box (`?find=<prefix>`) prefix-filters the entities.
453
+ - **Per-entity timeline** — `?entity=<value>` opens that entity's cross-queue interval timeline in the right panel: every role it moved through, with durations and gaps. The panel's copy-link emits a shareable `/trends?lens=<facet>&entity=<value>` URL.
454
+
455
+ The aggregate bands are counts-only and readable by any login while the public board flag stands; the slice and per-entity tiers group by facet values and require full (`read_all`) access to the system's queues.
456
+
457
+ **API:** `POST /api/escalations/aggregate-by-facets` (the state-mix and rankings) and `POST /api/escalations/timeline-by-facet` (the per-entity intervals). See [Escalation Analytics](escalation-analytics.md) for the query contract.
377
458
 
378
459
  ### Processes Overview
379
460
 
@@ -398,28 +479,30 @@ This is the primary view for understanding how a multi-step workflow progresses
398
479
 
399
480
  ### Files
400
481
 
401
- Browse and manage files in connected storage backends (MinIO locally, S3/GCS in production).
482
+ Accessible at `/files`. Browse and manage files in the connected storage backend (MinIO locally, S3/GCS in production).
402
483
 
403
- - **File browser** — navigate directories with breadcrumbs. View files in a list with name, size, type, and last modified date.
404
- - **Preview panel** — click a file to preview it in the side panel. Supports images, text, JSON, and PDF.
405
- - **Upload** — drag and drop or click to upload files to the current directory.
406
- - **Sidebar** — collapsible file tree for quick navigation across the storage hierarchy.
484
+ - **File browser** — navigate directories with breadcrumbs; the list shows name, size, and last-modified. Paginated via a continuation token.
485
+ - **Preview panel** — click a file to preview it in the side panel: images inline, text and code inline, JSON, and a PDF open-in-tab. Everything else downloads.
486
+ - **Upload** — drag and drop, or the file input, into the current prefix; a confirmation names the target path.
487
+ - **Signed URLs** — generate a time-boxed download link for a file (expiry from a fixed set, 1h to 30d).
407
488
 
408
- Storage backend is selected by the `STORAGE_BACKEND` env var. The same interface works against MinIO (local dev), S3, or GCS no code changes needed.
489
+ The backend is selected by the `LT_STORAGE_BACKEND` env var `local` (filesystem) or `s3` (S3-compatible, including MinIO). The same interface serves every backend; only env changes.
409
490
 
410
- **API:** `GET /api/files` lists files. `POST /api/files/upload` uploads. `GET /api/files/download/:path` downloads.
491
+ **API:** `GET /api/file-browser/browse?prefix=<path>` lists. `POST /api/file-browser/upload?path=<path>` uploads raw bytes. `GET /api/file-browser/download/<path>` downloads; `DELETE /api/file-browser/delete/<path>` removes; `POST /api/file-browser/signed-url` mints a link. Token-scoped serving is at `GET /api/files/<path>?token=<jwt>`.
411
492
 
412
493
  ### Knowledge
413
494
 
414
- Knowledge base for storing and retrieving domain-specific information used by workflows and MCP tools.
495
+ Accessible at `/knowledge` (builder-only superadmin or the `engineer` role). A **versioned** store of domain data that workflows and resolver forms read at runtime: product catalogs, reference tables, cascade option sets. An entry is keyed by `domain` + `key` and holds a JSON `data` value plus `tags`.
415
496
 
416
- - **Entry list** browse knowledge entries by domain and key. Each entry stores structured data that workflows can query at runtime.
417
- - **Create/Edit** — add or update knowledge entries with a domain, key, and JSON value.
418
- - **Search** — filter entries by domain or key prefix.
497
+ **Versioning is the defining property.** Every write that changes an entry's `data` mints an immutable edition and advances `current_version`; writes that leave the data identical (or touch only tags) are no-ops that never bump the version. The live entry is the current edition; past editions are retained and read-only.
419
498
 
420
- Knowledge entries are accessed by workflows via the `get_knowledge` MCP tool. This is how workflows retrieve domain context (product catalogs, configuration data, reference tables) without hardcoding values.
499
+ - **Browse** All Domains a domain's entries (each row shows key, tags, field count, and current version) an entry detail with a full JSON editor.
500
+ - **Version rail** — the entry detail's version dropdown lists every edition, newest first, the current one marked. Selecting a past edition sets `?version=<N>` and shows an immutable, read-only snapshot with a "back to current" affordance.
501
+ - **Field-level edits** — set or remove a value at a dot-path (`jsonb_set` / path delete) without clobbering sibling keys; each still mints an edition when the data changes.
421
502
 
422
- **API:** `GET /api/knowledge` lists entries. `PUT /api/knowledge/:domain/:key` creates or updates. `DELETE /api/knowledge/:domain/:key` removes.
503
+ **How workflows and forms consume it.** A workflow pins knowledge onto an escalation as versioned lookup refs on the reserved `envelope.lookups` key — `{ domain, key, version, as? }`. Because the ref names an exact edition, the resolver always sees the data the workflow was written against, even as the entry later evolves. Resolving those refs (`GET /api/escalations/:id/lookups`, in-process LRU cached) exposes a `lookup.<key>` domain to the form: a field's `x-lt-options` reads `lookup.materials.items`, and cascades interpolate the live form — `lookup.geo.regions.{{resolver.country}}` narrows the second select from the first. The reader needs no separate knowledge grant; the ref on an escalation they can read *is* the grant. Agents reach the same data through the `get_knowledge` MCP tool (with an optional `version`).
504
+
505
+ **API:** `GET /api/knowledge/domains` and `/entries?domain=` list; `GET /api/knowledge/entry?domain=&key=&version=` fetches the live entry or a pinned edition; `GET /api/knowledge/entry/versions?domain=&key=` returns the lineage. `POST /api/knowledge/entry` creates or merges, `PUT /api/knowledge/field` sets a path, `DELETE /api/knowledge/field` removes one, `DELETE /api/knowledge/entry` deletes (cascading its editions). See [Knowledge HTTP API](api/http/knowledge.md).
423
506
 
424
507
  ### Topic Catalog
425
508
 
@@ -480,15 +563,18 @@ markdown body. Each user dismisses per-browser.
480
563
  Role targeting scopes display, never access: the live event reaches every
481
564
  authenticated subscriber, so announcement bodies must never carry secrets.
482
565
 
483
- ### Global search
566
+ ### Search and run
567
+
568
+ The header bar is one input with two verbs, chosen by the type chip that
569
+ trails it. **Find** modes look an escalation up; **Run** modes execute a scan
570
+ code composed from a chosen rule plus the typed target. The bar appears when
571
+ either verb is enabled and offers whichever modes the deployment provides.
484
572
 
485
- An opt-in header search bar for one-gesture lookups across every escalation
486
- of any status. Configure it in the `start()` config —
573
+ **Find** is the opt-in global search. Configure it in the `start()` config
487
574
  `search: { enabled: true, facets: ['orderId', 'po'] }` — or by env
488
- (`LT_SEARCH_BAR=true`, `LT_SEARCH_FACETS=orderId,po`; env wins). The picklist
575
+ (`LT_SEARCH_BAR=true`, `LT_SEARCH_FACETS=orderId,po`; env wins). The chip
489
576
  always offers `escalationId` and `workflowId` (long-tail-owned lookups) ahead
490
- of the configured metadata facets, and remembers the last-used facet per
491
- device.
577
+ of the configured metadata facets.
492
578
 
493
579
  - A metadata facet lands on the escalation list filtered by that facet across
494
580
  all statuses, newest first (the same deep link as clicking a facet value).
@@ -497,10 +583,23 @@ device.
497
583
  - `workflowId` opens the workflow's single escalation, lists several to pick
498
584
  from, or links straight to the workflow execution when none exist.
499
585
 
500
- Kiosk sessions see the bar too a station can dump a PO or order id and jump
501
- straight to it; RBAC read scope bounds what any search can return. See
502
- [Faceted Routing](faceted-routing.md) a search is a one-gesture facet deep
503
- link.
586
+ **Run** appears when `features.scanCodes` is on. Every enabled rule of every
587
+ enabled action scheme is a mode, grouped by scheme; pick one and the bar
588
+ shows its code head (`10:1:`) ahead of the input and names the target facet
589
+ in the placeholder. Type the target and press Enter: the bar composes
590
+ `10:1:<target>` and executes it through the same pipeline a scanner uses, so
591
+ the outcome navigates, confirms, or answers exactly as a physical scan would.
592
+ An outcome that answers in place (a fallback, a conflict, a closed row) is
593
+ narrated right under the bar. A whole code pasted into a Run mode executes
594
+ as-is; fixed-encoding rules accept digits only and say so inline. The menu
595
+ footer opens the scan panel for scanner settings and the barcode preview.
596
+ See [Scan codes](scan-codes.md).
597
+
598
+ The chip remembers the last-used mode per device. Kiosk sessions see the bar
599
+ too — a station can dump a PO or order id and jump straight to it, or run a
600
+ rule against a serial it can read but not scan; RBAC bounds what any search or
601
+ run can reach. See [Faceted Routing](faceted-routing.md) — a search is a
602
+ one-gesture facet deep link.
504
603
 
505
604
  ### Inbox
506
605
 
package/docs/events.md CHANGED
@@ -30,6 +30,8 @@ await start({
30
30
 
31
31
  The dashboard auto-detects its event transport via `GET /api/settings`. By default, Socket.IO is reported — it works in-process with no additional infrastructure.
32
32
 
33
+ The connection follows the session. It opens the moment a login lands, whether the person signed in with credentials or arrived through the host's SSO exchange, closes on logout, and presents the token current at each reconnect, so a refreshed token never strands the socket. The footer's event indicator turns green while connected and amber while not; clicking it while amber reloads the page.
34
+
33
35
  For multi-container deployments where the API and workers run as separate processes, set `EVENT_TRANSPORT=nats` to tell the dashboard to connect via NATS WebSocket instead of Socket.IO:
34
36
 
35
37
  ```yaml
@@ -69,7 +71,7 @@ System families declare the fields they populate. Which fields exist is a functi
69
71
 
70
72
  | Family | Subject | Adds |
71
73
  |---|---|---|
72
- | workflow | `system.workflow.{id}.{started\|completed\|failed}` | `workflowId`, `workflowName`, `taskQueue`, `status` |
74
+ | workflow | `system.workflow.{id}.{started\|completed\|failed}` | `workflowId`, `workflowName`, `taskQueue`, `status`; `completed` carries the workflow's returned `data` |
73
75
  | task | `system.task.{taskId}.{created\|started\|completed\|escalated\|failed}` | + `taskId` |
74
76
  | escalation | `system.escalation.{role}.{id}.{created\|resolved\|claimed\|released\|cancelled\|reassigned\|expired}` | `escalationId`, `role` (+ workflow context), `status` |
75
77
  | activity | `system.activity.{wfId}.{activity}.{started\|completed\|failed}` | `activityName` (+ workflow context) |
@@ -121,7 +121,11 @@ Values are strings — jsonb containment is type-sensitive, so numeric facets
121
121
  are not templatable. Link variables are a view convenience: RBAC read scope
122
122
  still governs what any query can return.
123
123
 
124
- The dashboard's [global search bar](dashboard.md#global-search) is the
124
+ A bound link variable also **scopes the Pace Board** — the same device value
125
+ narrows the board's station counts, time-in-state mix, and entity timelines,
126
+ not just the pins. See [Operations — Scoping the board](operations.md#scoping-the-board).
127
+
128
+ The dashboard's [global search bar](dashboard.md#search-and-run) is the
125
129
  one-gesture sibling of this surface: a configured facet name plus a value
126
130
  lands on the same all-status faceted list a deep link produces.
127
131
 
@@ -20,21 +20,47 @@ When `x-lt-viewport` is present, the dashboard renders an iframe instead of the
20
20
 
21
21
  ---
22
22
 
23
+ ## Stage URLs
24
+
25
+ `src` is the editor: it renders full-bleed once the viewer holds the claim. Two optional URLs show the embedded app at the other stages of the item's life, inside the standard page with its title and action bar:
26
+
27
+ ```json
28
+ {
29
+ "x-lt-viewport": {
30
+ "type": "iframe",
31
+ "src": "https://your-app.example.com/design?workbenchId={workbenchId}",
32
+ "onPending": "https://your-app.example.com/preview?workbenchId={workbenchId}",
33
+ "onResolved": "https://your-app.example.com/summary?stl={stl_url}"
34
+ }
35
+ }
36
+ ```
37
+
38
+ | Stage | When | URL | The embed |
39
+ |-------|------|-----|-----------|
40
+ | `pending` | Pending and not workable by the viewer: available, or claimed by someone else | `onPending` | Shows; the action bar carries Claim |
41
+ | `claimed` | Pending and workable by the viewer | `src` | Works; `lt:submit` resolves |
42
+ | `resolved` | Resolved | `onResolved` | Shows the outcome |
43
+
44
+ Pending and resolved embeds are read-only: the parent drops `lt:submit` and `lt:escalate` from them and never sends `lt:validate`. Every `lt:init` carries `stage`, so one app can serve all three URLs and branch on it. Leave a stage URL out to keep the default surface — the claim affordance while pending, the submitted values once resolved. A cancelled item always renders the default surface.
45
+
46
+ ---
47
+
23
48
  ## URL Token Substitution
24
49
 
25
- The `src` value supports `{key}` tokens — single-brace, flat key lookup. The dashboard expands them at render time using values merged from three sources, in priority order:
50
+ Every stage URL supports `{key}` tokens — single-brace, flat key lookup. The dashboard expands them at render time using values merged from four sources, in priority order:
26
51
 
27
52
  | Priority | Source | Field |
28
53
  |----------|--------|-------|
29
- | 1 (highest) | `escalation_payload` | Per-escalation context set by the workflow |
30
- | 2 | `envelope` | Input envelope set by the workflow |
31
- | 3 (lowest) | `metadata` | Row metadata |
54
+ | 1 (highest) | `resolver_payload` | The submitted resolution populated once resolved, so `onResolved` can point at the outcome |
55
+ | 2 | `escalation_payload` | Per-escalation context set by the workflow |
56
+ | 3 | `envelope` | Input envelope set by the workflow |
57
+ | 4 (lowest) | `metadata` | Row metadata |
32
58
 
33
59
  Keys present in multiple sources resolve from the highest-priority one. A key with no match is left as `{key}` in the final URL.
34
60
 
35
61
  ### Example — CAD designer workbench
36
62
 
37
- The `cad-designer` role embeds a WebGL editor. Each escalation carries `workbenchId` and `companyId` in its payload; the dashboard injects them into the iframe URL at render time:
63
+ The `cad-designer` role embeds a WebGL editor at `/design` while claimed and its QC view at `/qc` before the claim and after the resolve. Each escalation carries `workbenchId` and `companyId` in its payload; the dashboard injects them into every stage URL at render time:
38
64
 
39
65
  ```typescript
40
66
  // Form schema on the role
@@ -42,6 +68,8 @@ const WORKBENCH_FORM_SCHEMA = {
42
68
  'x-lt-viewport': {
43
69
  type: 'iframe',
44
70
  src: `${BASE_URL}/design?workbenchId={workbenchId}&companyId={companyId}`,
71
+ onPending: `${BASE_URL}/qc?workbenchId={workbenchId}&companyId={companyId}`,
72
+ onResolved: `${BASE_URL}/qc?workbenchId={workbenchId}&companyId={companyId}`,
45
73
  },
46
74
  properties: {
47
75
  stl_url: {
@@ -68,7 +96,7 @@ At render time the dashboard produces:
68
96
  https://editor.internal/design?workbenchId=wb-123&companyId=co-456
69
97
  ```
70
98
 
71
- `{workbenchId}` and `{companyId}` are resolved from `escalation_payload`, which has the highest priority — so even if the same keys appear in `envelope` or `metadata`, the payload values win.
99
+ `{workbenchId}` and `{companyId}` are resolved from `escalation_payload`, which outranks `envelope` and `metadata` — so even if the same keys appear there, the payload values win.
72
100
 
73
101
  ### Common token patterns
74
102
 
@@ -104,6 +132,7 @@ Communication happens via `window.postMessage`.
104
132
  workflow_type: string | null,
105
133
  },
106
134
  schema: Record<string, unknown>, // The full form schema
135
+ stage: 'pending' | 'claimed' | 'resolved', // Which stage URL this embed serves
107
136
  }
108
137
 
109
138
  // Optional: parent requests the iframe to submit