@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.
- package/build/api/escalations/index.d.ts +1 -1
- package/build/api/escalations/index.js +2 -1
- package/build/api/escalations/list.d.ts +11 -0
- package/build/api/escalations/list.js +31 -2
- package/build/api/users.d.ts +11 -0
- package/build/api/users.js +19 -0
- package/build/api/workflows/config.d.ts +5 -1
- package/build/api/workflows/config.js +9 -1
- package/build/api/workflows/index.d.ts +3 -0
- package/build/api/workflows/index.js +7 -1
- package/build/api/workflows/input-validation.d.ts +13 -0
- package/build/api/workflows/input-validation.js +38 -0
- package/build/api/workflows/invocable.d.ts +19 -0
- package/build/api/workflows/invocable.js +106 -0
- package/build/api/workflows/invocation.js +6 -0
- package/build/lib/db/schemas/034_workflow_input_schema.sql +13 -0
- package/build/lib/uuid.d.ts +11 -0
- package/build/lib/uuid.js +20 -0
- package/build/routes/escalations/list.js +10 -1
- package/build/routes/users.js +9 -0
- package/build/routes/workflows/config.js +2 -0
- package/build/routes/workflows/discovery.js +14 -0
- package/build/scripts/ortho-populate.d.ts +3 -1
- package/build/scripts/ortho-populate.js +15 -5
- package/build/sdk/index.d.ts +1 -0
- package/build/services/config/cache.js +2 -0
- package/build/services/config/read.js +4 -0
- package/build/services/config/sql.d.ts +2 -2
- package/build/services/config/sql.js +7 -5
- package/build/services/config/write.js +11 -1
- package/build/services/escalation/batch.js +3 -0
- package/build/services/escalation/bulk.js +8 -0
- package/build/services/escalation/crud.js +26 -0
- package/build/services/escalation/queries.d.ts +14 -1
- package/build/services/escalation/queries.js +49 -7
- package/build/services/escalation/sql.d.ts +4 -2
- package/build/services/escalation/sql.js +4 -0
- package/build/services/interceptor/index.js +7 -0
- package/build/services/invocation-access.d.ts +18 -0
- package/build/services/invocation-access.js +32 -0
- package/build/services/role/types.d.ts +1 -1
- package/build/services/role/types.js +1 -1
- package/build/services/user/auth.js +2 -2
- package/build/services/user/crud.d.ts +13 -0
- package/build/services/user/crud.js +26 -3
- package/build/services/user/index.d.ts +1 -1
- package/build/services/user/index.js +2 -1
- package/build/services/user/sql.d.ts +11 -5
- package/build/services/user/sql.js +18 -6
- package/build/services/workflow-invocation.js +4 -9
- package/build/shared/form-validation/index.d.ts +1 -0
- package/build/shared/form-validation/index.js +1 -0
- package/build/shared/form-validation/invoke-context.d.ts +7 -0
- package/build/shared/form-validation/invoke-context.js +11 -0
- package/build/shared/form-validation/validate-resolver-payload.d.ts +1 -1
- package/build/shared/form-validation/validate-resolver-payload.js +1 -0
- package/build/shared/form-validation/x-lt-help.d.ts +2 -2
- package/build/shared/form-validation/x-lt-help.js +2 -2
- package/build/shared/form-validation/x-lt-show-if.d.ts +1 -0
- package/build/shared/form-validation/x-lt-show-if.js +3 -0
- package/build/start/workers.js +2 -0
- package/build/system/mcp-servers/admin/workflows.js +15 -2
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/config.d.ts +6 -0
- package/build/types/index.d.ts +2 -0
- package/build/types/index.js +4 -1
- package/build/types/startup.d.ts +10 -0
- package/build/types/validation.d.ts +4 -2
- package/build/types/workflow-icons.d.ts +86 -0
- package/build/types/workflow-icons.js +92 -0
- package/dashboard/dist/assets/{AdminDashboard-DDY8bceq.js → AdminDashboard-Drs-9TRM.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-DDY8bceq.js.map → AdminDashboard-Drs-9TRM.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-D47sHnup.js → AgentConfigPage-BDPDZ7-v.js} +3 -3
- package/dashboard/dist/assets/{AgentConfigPage-D47sHnup.js.map → AgentConfigPage-BDPDZ7-v.js.map} +1 -1
- package/dashboard/dist/assets/{AgentDetailPage-DwrfByA2.js → AgentDetailPage-CGBulM4_.js} +3 -3
- package/dashboard/dist/assets/{AgentDetailPage-DwrfByA2.js.map → AgentDetailPage-CGBulM4_.js.map} +1 -1
- package/dashboard/dist/assets/AgentsPage-D77JeViQ.js +2 -0
- package/dashboard/dist/assets/{AgentsPage-CLSjlyXy.js.map → AgentsPage-D77JeViQ.js.map} +1 -1
- package/dashboard/dist/assets/{AutoGrowTextarea-DgEQeRQw.js → AutoGrowTextarea-7X3T9La8.js} +2 -2
- package/dashboard/dist/assets/{AutoGrowTextarea-DgEQeRQw.js.map → AutoGrowTextarea-7X3T9La8.js.map} +1 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-CqM_MOMy.js +2 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-CqM_MOMy.js.map +1 -0
- package/dashboard/dist/assets/{BotPicker-CbL7gQFq.js → BotPicker-CC5vI_Sv.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-CbL7gQFq.js.map → BotPicker-CC5vI_Sv.js.map} +1 -1
- package/dashboard/dist/assets/BulkAssignModal-BXiJnlqt.js +2 -0
- package/dashboard/dist/assets/BulkAssignModal-BXiJnlqt.js.map +1 -0
- package/dashboard/dist/assets/{CapabilitiesPage-Q9Pe0y_p.js → CapabilitiesPage-D9TC2iKy.js} +2 -2
- package/dashboard/dist/assets/{CapabilitiesPage-Q9Pe0y_p.js.map → CapabilitiesPage-D9TC2iKy.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-BZOtaWPR.js → CollapsibleSection-D10lGw_u.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-BZOtaWPR.js.map → CollapsibleSection-D10lGw_u.js.map} +1 -1
- package/dashboard/dist/assets/{ConfirmDeleteModal-6CidwpPx.js → ConfirmDeleteModal-BJdLN6No.js} +2 -2
- package/dashboard/dist/assets/{ConfirmDeleteModal-6CidwpPx.js.map → ConfirmDeleteModal-BJdLN6No.js.map} +1 -1
- package/dashboard/dist/assets/{CopyableId-4Lqlc5cU.js → CopyableId-Bn02zYxt.js} +2 -2
- package/dashboard/dist/assets/{CopyableId-4Lqlc5cU.js.map → CopyableId-Bn02zYxt.js.map} +1 -1
- package/dashboard/dist/assets/CountdownTimer-D9B-BjQ7.js +2 -0
- package/dashboard/dist/assets/CountdownTimer-D9B-BjQ7.js.map +1 -0
- package/dashboard/dist/assets/{CredentialsPage-dxjrUEyO.js → CredentialsPage-hniUAFBK.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-dxjrUEyO.js.map → CredentialsPage-hniUAFBK.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-D8oo8eaA.js → CronLabel-v9DCD2FX.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-D8oo8eaA.js.map → CronLabel-v9DCD2FX.js.map} +1 -1
- package/dashboard/dist/assets/{DataTable-DFPMeqyQ.js → DataTable-7C7riavf.js} +2 -2
- package/dashboard/dist/assets/{DataTable-DFPMeqyQ.js.map → DataTable-7C7riavf.js.map} +1 -1
- package/dashboard/dist/assets/{DropZone-CWCh5ItZ.js → DropZone-XlcY8hEH.js} +2 -2
- package/dashboard/dist/assets/{DropZone-CWCh5ItZ.js.map → DropZone-XlcY8hEH.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-FHHQ69G3.js → ElapsedCell-_xvVTK8N.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-FHHQ69G3.js.map → ElapsedCell-_xvVTK8N.js.map} +1 -1
- package/dashboard/dist/assets/{EmptyState-lpUFYfun.js → EmptyState-DgC_6njU.js} +2 -2
- package/dashboard/dist/assets/{EmptyState-lpUFYfun.js.map → EmptyState-DgC_6njU.js.map} +1 -1
- package/dashboard/dist/assets/EntityTimelinePanel-1lT3jkCM.js +2 -0
- package/dashboard/dist/assets/EntityTimelinePanel-1lT3jkCM.js.map +1 -0
- package/dashboard/dist/assets/{EscalationListSchemaPage-D5L5lDNb.js → EscalationListSchemaPage-BeQUTUEO.js} +3 -3
- package/dashboard/dist/assets/{EscalationListSchemaPage-D5L5lDNb.js.map → EscalationListSchemaPage-BeQUTUEO.js.map} +1 -1
- package/dashboard/dist/assets/EscalationListView-uuZRMSLB.js +2 -0
- package/dashboard/dist/assets/EscalationListView-uuZRMSLB.js.map +1 -0
- package/dashboard/dist/assets/{EscalationSchemaPage-bYbfEpSB.js → EscalationSchemaPage-D4VlEwyS.js} +3 -3
- package/dashboard/dist/assets/{EscalationSchemaPage-bYbfEpSB.js.map → EscalationSchemaPage-D4VlEwyS.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-DjtIjL1b.js → EscalationsOverview-Bx6Zrzm-.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-DjtIjL1b.js.map → EscalationsOverview-Bx6Zrzm-.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-CihVTREC.js → EventTable-KEC3YWBf.js} +2 -2
- package/dashboard/dist/assets/{EventTable-CihVTREC.js.map → EventTable-KEC3YWBf.js.map} +1 -1
- package/dashboard/dist/assets/{FilterBar-Fin1IqHc.js → FilterBar-xwN_b5fj.js} +2 -2
- package/dashboard/dist/assets/{FilterBar-Fin1IqHc.js.map → FilterBar-xwN_b5fj.js.map} +1 -1
- package/dashboard/dist/assets/{GraphInvokePage-D77ILPd3.js → GraphInvokePage-DV7Y0fe2.js} +2 -2
- package/dashboard/dist/assets/{GraphInvokePage-D77ILPd3.js.map → GraphInvokePage-DV7Y0fe2.js.map} +1 -1
- package/dashboard/dist/assets/HomePage-G785SWcW.js +2 -0
- package/dashboard/dist/assets/HomePage-G785SWcW.js.map +1 -0
- package/dashboard/dist/assets/ListToolbar-zPVYPg3W.js +2 -0
- package/dashboard/dist/assets/{ListToolbar-DyHaJBTk.js.map → ListToolbar-zPVYPg3W.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-qz7Ivf0G.js → McpOverview-CMGUq8hx.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-qz7Ivf0G.js.map → McpOverview-CMGUq8hx.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-D6MB4CZM.js → McpQueryDetailPage-CdhLhaKn.js} +3 -3
- package/dashboard/dist/assets/{McpQueryDetailPage-D6MB4CZM.js.map → McpQueryDetailPage-CdhLhaKn.js.map} +1 -1
- package/dashboard/dist/assets/McpQueryPage-CNSNMagY.js +2 -0
- package/dashboard/dist/assets/{McpQueryPage-BqqRF5cQ.js.map → McpQueryPage-CNSNMagY.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunDetailPage-DxLEhzhn.js → McpRunDetailPage-DBm8hUaW.js} +2 -2
- package/dashboard/dist/assets/{McpRunDetailPage-DxLEhzhn.js.map → McpRunDetailPage-DBm8hUaW.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunsPage-BOsS4Hdf.js → McpRunsPage-B6rb5QBf.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-BOsS4Hdf.js.map → McpRunsPage-B6rb5QBf.js.map} +1 -1
- package/dashboard/dist/assets/NamespacePill-Bb3revqD.js +2 -0
- package/dashboard/dist/assets/{NamespacePill-CJikgGzE.js.map → NamespacePill-Bb3revqD.js.map} +1 -1
- package/dashboard/dist/assets/OperationsPage-Dxk1fAMg.js +3 -0
- package/dashboard/dist/assets/OperationsPage-Dxk1fAMg.js.map +1 -0
- package/dashboard/dist/assets/OperatorDashboard-DBOscDnf.js +2 -0
- package/dashboard/dist/assets/{OperatorDashboard-C5rlijvh.js.map → OperatorDashboard-DBOscDnf.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeader-DZmymFBS.js → PageHeader-Di5Sz3an.js} +2 -2
- package/dashboard/dist/assets/{PageHeader-DZmymFBS.js.map → PageHeader-Di5Sz3an.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeaderWithStats-DuAm6YK2.js → PageHeaderWithStats-DutKJ-h2.js} +2 -2
- package/dashboard/dist/assets/{PageHeaderWithStats-DuAm6YK2.js.map → PageHeaderWithStats-DutKJ-h2.js.map} +1 -1
- package/dashboard/dist/assets/{PersonaDetailPage-JBdVvT5g.js → PersonaDetailPage-BGRdGpeR.js} +2 -2
- package/dashboard/dist/assets/{PersonaDetailPage-JBdVvT5g.js.map → PersonaDetailPage-BGRdGpeR.js.map} +1 -1
- package/dashboard/dist/assets/PersonasPage-DRzOZcVa.js +2 -0
- package/dashboard/dist/assets/PersonasPage-DRzOZcVa.js.map +1 -0
- package/dashboard/dist/assets/{PriorityBadge-BETdj4M-.js → PriorityBadge-r33mpDju.js} +2 -2
- package/dashboard/dist/assets/{PriorityBadge-BETdj4M-.js.map → PriorityBadge-r33mpDju.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessDetailPage-CHnzOzQn.js → ProcessDetailPage-BLGjZXpM.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-CHnzOzQn.js.map → ProcessDetailPage-BLGjZXpM.js.map} +1 -1
- package/dashboard/dist/assets/ProcessesListPage-5NnysKFC.js +2 -0
- package/dashboard/dist/assets/{ProcessesListPage-DuXTS5-v.js.map → ProcessesListPage-5NnysKFC.js.map} +1 -1
- package/dashboard/dist/assets/ResolverForm-Dy0yirtP.js +2 -0
- package/dashboard/dist/assets/ResolverForm-Dy0yirtP.js.map +1 -0
- package/dashboard/dist/assets/RoleDetailPage-C_LIoe1j.js +8 -0
- package/dashboard/dist/assets/RoleDetailPage-C_LIoe1j.js.map +1 -0
- package/dashboard/dist/assets/{RolePill-C9LSs1aK.js → RolePill-DhnVPK4q.js} +2 -2
- package/dashboard/dist/assets/{RolePill-C9LSs1aK.js.map → RolePill-DhnVPK4q.js.map} +1 -1
- package/dashboard/dist/assets/RolesPage-BmW1LzkU.js +2 -0
- package/dashboard/dist/assets/{RolesPage-h77-JOLd.js.map → RolesPage-BmW1LzkU.js.map} +1 -1
- package/dashboard/dist/assets/{RowActions-eYrzgQSj.js → RowActions-Bdd7z4eB.js} +2 -2
- package/dashboard/dist/assets/{RowActions-eYrzgQSj.js.map → RowActions-Bdd7z4eB.js.map} +1 -1
- package/dashboard/dist/assets/RunAsSelector-BwZTJ4Yj.js +2 -0
- package/dashboard/dist/assets/{RunAsSelector-DoLFJ0mZ.js.map → RunAsSelector-BwZTJ4Yj.js.map} +1 -1
- package/dashboard/dist/assets/ScanCodesPage-FiYIYB-m.js +2 -0
- package/dashboard/dist/assets/{ScanCodesPage-b5NZM_-f.js.map → ScanCodesPage-FiYIYB-m.js.map} +1 -1
- package/dashboard/dist/assets/{ScanSchemeDetailPage-t7X6ekaW.js → ScanSchemeDetailPage-BiAqtD-a.js} +2 -2
- package/dashboard/dist/assets/{ScanSchemeDetailPage-t7X6ekaW.js.map → ScanSchemeDetailPage-BiAqtD-a.js.map} +1 -1
- package/dashboard/dist/assets/ScanStationPage-8CdOtbCD.js +2 -0
- package/dashboard/dist/assets/{ScanStationPage-r02hkTAG.js.map → ScanStationPage-8CdOtbCD.js.map} +1 -1
- package/dashboard/dist/assets/{ScopeBadge-CjyC7iyO.js → ScopeBadge-CXUg0FHo.js} +2 -2
- package/dashboard/dist/assets/{ScopeBadge-CjyC7iyO.js.map → ScopeBadge-CXUg0FHo.js.map} +1 -1
- package/dashboard/dist/assets/{SegmentedTabs-DXNzY05q.js → SegmentedTabs-BpJh4Hwn.js} +2 -2
- package/dashboard/dist/assets/{SegmentedTabs-DXNzY05q.js.map → SegmentedTabs-BpJh4Hwn.js.map} +1 -1
- package/dashboard/dist/assets/{StatCard-BXQ8dD9Y.js → StatCard-nWY4sndl.js} +2 -2
- package/dashboard/dist/assets/{StatCard-BXQ8dD9Y.js.map → StatCard-nWY4sndl.js.map} +1 -1
- package/dashboard/dist/assets/{StatusBadge-BDeUwPtb.js → StatusBadge-DHVwxre2.js} +2 -2
- package/dashboard/dist/assets/{StatusBadge-BDeUwPtb.js.map → StatusBadge-DHVwxre2.js.map} +1 -1
- package/dashboard/dist/assets/{StickyPagination-BGHQbp0_.js → StickyPagination-DRuqKhZt.js} +2 -2
- package/dashboard/dist/assets/{StickyPagination-BGHQbp0_.js.map → StickyPagination-DRuqKhZt.js.map} +1 -1
- package/dashboard/dist/assets/{StreamMessageDetail-C-wfdbLJ.js → StreamMessageDetail-JwlbRiMn.js} +2 -2
- package/dashboard/dist/assets/{StreamMessageDetail-C-wfdbLJ.js.map → StreamMessageDetail-JwlbRiMn.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-Dnoz-me3.js → SwimlaneTimeline-hu3c4bCJ.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-Dnoz-me3.js.map → SwimlaneTimeline-hu3c4bCJ.js.map} +1 -1
- package/dashboard/dist/assets/{TagInput-D-vLSMQC.js → TagInput-BZ8vb2oW.js} +2 -2
- package/dashboard/dist/assets/{TagInput-D-vLSMQC.js.map → TagInput-BZ8vb2oW.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-c9IiAhVJ.js → TaskDetailPage-NqVZF3jW.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-c9IiAhVJ.js.map → TaskDetailPage-NqVZF3jW.js.map} +1 -1
- package/dashboard/dist/assets/{TaskQueuePill-sFRMrQ5D.js → TaskQueuePill-Bxir8WRl.js} +2 -2
- package/dashboard/dist/assets/{TaskQueuePill-sFRMrQ5D.js.map → TaskQueuePill-Bxir8WRl.js.map} +1 -1
- package/dashboard/dist/assets/TasksListPage-BQNaSzCn.js +2 -0
- package/dashboard/dist/assets/{TasksListPage-Dor8LSc5.js.map → TasksListPage-BQNaSzCn.js.map} +1 -1
- package/dashboard/dist/assets/TimeAgo-Cv35DOiI.js +2 -0
- package/dashboard/dist/assets/{TimeAgo-Dd8vmRnM.js.map → TimeAgo-Cv35DOiI.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-qJfC6-iT.js → TimestampCell-DHUXey9s.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-qJfC6-iT.js.map → TimestampCell-DHUXey9s.js.map} +1 -1
- package/dashboard/dist/assets/{ToolPill-OQ3mC11d.js → ToolPill-FkgTXIhq.js} +2 -2
- package/dashboard/dist/assets/{ToolPill-OQ3mC11d.js.map → ToolPill-FkgTXIhq.js.map} +1 -1
- package/dashboard/dist/assets/ToolTestPanel-DvuLFWqQ.js +2 -0
- package/dashboard/dist/assets/{ToolTestPanel-CEGY5jss.js.map → ToolTestPanel-DvuLFWqQ.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-SI80ZTVv.js → TopicDetailPage-TXX3QNSo.js} +3 -3
- package/dashboard/dist/assets/{TopicDetailPage-SI80ZTVv.js.map → TopicDetailPage-TXX3QNSo.js.map} +1 -1
- package/dashboard/dist/assets/{TopicsPage-DiVB8tNr.js → TopicsPage-h-oyTeEJ.js} +2 -2
- package/dashboard/dist/assets/{TopicsPage-DiVB8tNr.js.map → TopicsPage-h-oyTeEJ.js.map} +1 -1
- package/dashboard/dist/assets/UserName-BceRCeDF.js +2 -0
- package/dashboard/dist/assets/UserName-BceRCeDF.js.map +1 -0
- package/dashboard/dist/assets/WorkflowExecutionPage-CmHGcaN9.js +2 -0
- package/dashboard/dist/assets/{WorkflowExecutionPage-e-Q57yiJ.js.map → WorkflowExecutionPage-CmHGcaN9.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowPill-Dh3hIW1l.js → WorkflowPill--AYMW7u_.js} +2 -2
- package/dashboard/dist/assets/WorkflowPill--AYMW7u_.js.map +1 -0
- package/dashboard/dist/assets/WorkflowsDashboard-B0r2lQXk.js +2 -0
- package/dashboard/dist/assets/{WorkflowsDashboard-DLKzN80h.js.map → WorkflowsDashboard-B0r2lQXk.js.map} +1 -1
- package/dashboard/dist/assets/WorkflowsOverview-BTMc2PJ_.js +2 -0
- package/dashboard/dist/assets/{WorkflowsOverview-J-ITQiO5.js.map → WorkflowsOverview-BTMc2PJ_.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-B_-ahhf1.js → YamlWorkflowDetailPage-DUiCVnZp.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-B_-ahhf1.js.map → YamlWorkflowDetailPage-DUiCVnZp.js.map} +1 -1
- package/dashboard/dist/assets/YamlWorkflowsPage-BAuXfwi0.js +2 -0
- package/dashboard/dist/assets/{YamlWorkflowsPage-DQNeERXU.js.map → YamlWorkflowsPage-BAuXfwi0.js.map} +1 -1
- package/dashboard/dist/assets/{agents-BsJ-MizQ.js → agents-BvmLCk4h.js} +2 -2
- package/dashboard/dist/assets/{agents-BsJ-MizQ.js.map → agents-BvmLCk4h.js.map} +1 -1
- package/dashboard/dist/assets/{bots-q1UYqaIh.js → bots-f_tyhl4I.js} +2 -2
- package/dashboard/dist/assets/{bots-q1UYqaIh.js.map → bots-f_tyhl4I.js.map} +1 -1
- package/dashboard/dist/assets/capabilities-DNo5bqLx.js +2 -0
- package/dashboard/dist/assets/{capabilities-BvjPXUcJ.js.map → capabilities-DNo5bqLx.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-BwlNpoJ_.js → controlplane-Dyyj2Irb.js} +2 -2
- package/dashboard/dist/assets/{controlplane-BwlNpoJ_.js.map → controlplane-Dyyj2Irb.js.map} +1 -1
- package/dashboard/dist/assets/escalation-columns-DIigeTOK.js +2 -0
- package/dashboard/dist/assets/escalation-columns-DIigeTOK.js.map +1 -0
- package/dashboard/dist/assets/index-BMlQYOE1.js +67 -0
- package/dashboard/dist/assets/index-BMlQYOE1.js.map +1 -0
- package/dashboard/dist/assets/index-BNZknxFY.js +2 -0
- package/dashboard/dist/assets/{index-cvmt1plS.js.map → index-BNZknxFY.js.map} +1 -1
- package/dashboard/dist/assets/index-Bpvfr4OW.css +1 -0
- package/dashboard/dist/assets/{index-CldVY0UQ.js → index-C-Xr2V3F.js} +2 -2
- package/dashboard/dist/assets/{index-CldVY0UQ.js.map → index-C-Xr2V3F.js.map} +1 -1
- package/dashboard/dist/assets/index-CegPUKpc.js +3 -0
- package/dashboard/dist/assets/index-CegPUKpc.js.map +1 -0
- package/dashboard/dist/assets/{index-O9uuK2yO.js → index-CsKiRyhy.js} +2 -2
- package/dashboard/dist/assets/{index-O9uuK2yO.js.map → index-CsKiRyhy.js.map} +1 -1
- package/dashboard/dist/assets/index-D7ESqn22.js +2 -0
- package/dashboard/dist/assets/{index-BhzsR_gZ.js.map → index-D7ESqn22.js.map} +1 -1
- package/dashboard/dist/assets/{index-Hbbox1A4.js → index-DL83W7Gu.js} +2 -2
- package/dashboard/dist/assets/{index-Hbbox1A4.js.map → index-DL83W7Gu.js.map} +1 -1
- package/dashboard/dist/assets/index-DPLssnMW.js +6 -0
- package/dashboard/dist/assets/index-DPLssnMW.js.map +1 -0
- package/dashboard/dist/assets/{index-DsKqDMs4.js → index-DWse7ERF.js} +2 -2
- package/dashboard/dist/assets/{index-DsKqDMs4.js.map → index-DWse7ERF.js.map} +1 -1
- package/dashboard/dist/assets/index-DoETPlJP.js +11 -0
- package/dashboard/dist/assets/index-DoETPlJP.js.map +1 -0
- package/dashboard/dist/assets/{index-8Gmtvd8e.js → index-DvxxrFBo.js} +2 -2
- package/dashboard/dist/assets/{index-8Gmtvd8e.js.map → index-DvxxrFBo.js.map} +1 -1
- package/dashboard/dist/assets/{index-DDHoDZRg.js → index-PcyGjmBm.js} +2 -2
- package/dashboard/dist/assets/{index-DDHoDZRg.js.map → index-PcyGjmBm.js.map} +1 -1
- package/dashboard/dist/assets/{index-BvGIyZUt.js → index-e2X-s0EQ.js} +2 -2
- package/dashboard/dist/assets/{index-BvGIyZUt.js.map → index-e2X-s0EQ.js.map} +1 -1
- package/dashboard/dist/assets/{knowledge-DM0tNphj.js → knowledge-Coo6CKzo.js} +2 -2
- package/dashboard/dist/assets/{knowledge-DM0tNphj.js.map → knowledge-Coo6CKzo.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-CQ_TD2P9.js → mcp-Cz5eFnP_.js} +2 -2
- package/dashboard/dist/assets/{mcp-CQ_TD2P9.js.map → mcp-Cz5eFnP_.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-BP7KSGhc.js → mcp-query-BtnuO4LF.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-BP7KSGhc.js.map → mcp-query-BtnuO4LF.js.map} +1 -1
- package/dashboard/dist/assets/{personas-DclHdHKK.js → personas-C2-LgpHQ.js} +2 -2
- package/dashboard/dist/assets/{personas-DclHdHKK.js.map → personas-C2-LgpHQ.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-LwuKJfj-.js → pipelines-TuXQvUA-.js} +2 -2
- package/dashboard/dist/assets/{pipelines-LwuKJfj-.js.map → pipelines-TuXQvUA-.js.map} +1 -1
- package/dashboard/dist/assets/resolver-payload-C1dXB3n6.js +2 -0
- package/dashboard/dist/assets/resolver-payload-C1dXB3n6.js.map +1 -0
- package/dashboard/dist/assets/seed-form-json-DvPsISxD.js +2 -0
- package/dashboard/dist/assets/seed-form-json-DvPsISxD.js.map +1 -0
- package/dashboard/dist/assets/{tasks-CDs-Li2v.js → tasks-OUzpu__z.js} +2 -2
- package/dashboard/dist/assets/{tasks-CDs-Li2v.js.map → tasks-OUzpu__z.js.map} +1 -1
- package/dashboard/dist/assets/{topics-BeOSelBX.js → topics-B8bdrBaW.js} +2 -2
- package/dashboard/dist/assets/{topics-BeOSelBX.js.map → topics-B8bdrBaW.js.map} +1 -1
- package/dashboard/dist/assets/useDebouncedValue-Cp5ZOtVE.js +2 -0
- package/dashboard/dist/assets/{useDebouncedValue-DhhAKjDC.js.map → useDebouncedValue-Cp5ZOtVE.js.map} +1 -1
- package/dashboard/dist/assets/{useFilterParams-crmlBw_l.js → useFilterParams-DBRw1Hcv.js} +2 -2
- package/dashboard/dist/assets/{useFilterParams-crmlBw_l.js.map → useFilterParams-DBRw1Hcv.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-C-E2gNEK.js → useNamespace-DtQe3wZ5.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-C-E2gNEK.js.map → useNamespace-DtQe3wZ5.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-CmLzX3YU.js → useYamlActivityEvents-CS22c15X.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-CmLzX3YU.js.map → useYamlActivityEvents-CS22c15X.js.map} +1 -1
- package/dashboard/dist/assets/{vendor-icons-R9b9gQ04.js → vendor-icons-DeBdvyKJ.js} +368 -143
- package/dashboard/dist/assets/vendor-icons-DeBdvyKJ.js.map +1 -0
- package/dashboard/dist/assets/vendor-query-DvWWuNIB.js +18 -0
- package/dashboard/dist/assets/vendor-query-DvWWuNIB.js.map +1 -0
- package/dashboard/dist/assets/{vendor-react-CdqEC2zv.js → vendor-react-_9U4mKFr.js} +2 -2
- package/dashboard/dist/assets/{vendor-react-CdqEC2zv.js.map → vendor-react-_9U4mKFr.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-BgAsc6-I.js → yaml-workflows-HQIki3hI.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-BgAsc6-I.js.map → yaml-workflows-HQIki3hI.js.map} +1 -1
- package/dashboard/dist/index.html +5 -5
- package/docs/api/http/workflows.md +25 -1
- package/docs/code-owned-configuration.md +1 -1
- package/docs/dashboard.md +179 -80
- package/docs/events.md +3 -1
- package/docs/faceted-routing.md +5 -1
- package/docs/hitl/iframe.md +35 -6
- package/docs/hitl/invoke-form.md +98 -0
- package/docs/hitl/x-lt-list-schema.md +3 -0
- package/docs/hitl/x-lt-show-if.md +11 -0
- package/docs/hitl-guide.md +4 -2
- package/docs/iam.md +1 -1
- package/docs/operations.md +61 -60
- package/docs/scan-codes.md +30 -3
- package/docs/workflows.md +1 -1
- package/package.json +1 -1
- package/dashboard/dist/assets/AgentsPage-CLSjlyXy.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-D2ukvuQh.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-D2ukvuQh.js.map +0 -1
- package/dashboard/dist/assets/BulkAssignModal-CMSgvHIs.js +0 -2
- package/dashboard/dist/assets/BulkAssignModal-CMSgvHIs.js.map +0 -1
- package/dashboard/dist/assets/CountdownTimer-B5lM7PfE.js +0 -2
- package/dashboard/dist/assets/CountdownTimer-B5lM7PfE.js.map +0 -1
- package/dashboard/dist/assets/EntityTimelinePanel-B5NuHnPF.js +0 -2
- package/dashboard/dist/assets/EntityTimelinePanel-B5NuHnPF.js.map +0 -1
- package/dashboard/dist/assets/HomePage-8XsQFqKL.js +0 -2
- package/dashboard/dist/assets/HomePage-8XsQFqKL.js.map +0 -1
- package/dashboard/dist/assets/ListToolbar-DyHaJBTk.js +0 -2
- package/dashboard/dist/assets/McpQueryPage-BqqRF5cQ.js +0 -2
- package/dashboard/dist/assets/NamespacePill-CJikgGzE.js +0 -2
- package/dashboard/dist/assets/OperationsPage-Cj1mJLMC.js +0 -3
- package/dashboard/dist/assets/OperationsPage-Cj1mJLMC.js.map +0 -1
- package/dashboard/dist/assets/OperatorDashboard-C5rlijvh.js +0 -2
- package/dashboard/dist/assets/PersonasPage-Xp_Gt6El.js +0 -2
- package/dashboard/dist/assets/PersonasPage-Xp_Gt6El.js.map +0 -1
- package/dashboard/dist/assets/ProcessesListPage-DuXTS5-v.js +0 -2
- package/dashboard/dist/assets/RoleDetailPage-C9GGHNf4.js +0 -8
- package/dashboard/dist/assets/RoleDetailPage-C9GGHNf4.js.map +0 -1
- package/dashboard/dist/assets/RolesPage-h77-JOLd.js +0 -2
- package/dashboard/dist/assets/RunAsSelector-DoLFJ0mZ.js +0 -2
- package/dashboard/dist/assets/ScanCodesPage-b5NZM_-f.js +0 -2
- package/dashboard/dist/assets/ScanStationPage-r02hkTAG.js +0 -2
- package/dashboard/dist/assets/TasksListPage-Dor8LSc5.js +0 -2
- package/dashboard/dist/assets/TimeAgo-Dd8vmRnM.js +0 -2
- package/dashboard/dist/assets/ToolTestPanel-CEGY5jss.js +0 -2
- package/dashboard/dist/assets/UserName-Dmm456xT.js +0 -2
- package/dashboard/dist/assets/UserName-Dmm456xT.js.map +0 -1
- package/dashboard/dist/assets/WorkflowExecutionPage-e-Q57yiJ.js +0 -2
- package/dashboard/dist/assets/WorkflowPill-Dh3hIW1l.js.map +0 -1
- package/dashboard/dist/assets/WorkflowsDashboard-DLKzN80h.js +0 -2
- package/dashboard/dist/assets/WorkflowsOverview-J-ITQiO5.js +0 -2
- package/dashboard/dist/assets/YamlWorkflowsPage-DQNeERXU.js +0 -2
- package/dashboard/dist/assets/capabilities-BvjPXUcJ.js +0 -2
- package/dashboard/dist/assets/escalation-columns-Bo3PfIZj.js +0 -2
- package/dashboard/dist/assets/escalation-columns-Bo3PfIZj.js.map +0 -1
- package/dashboard/dist/assets/index-B8JfGtQB.js +0 -3
- package/dashboard/dist/assets/index-B8JfGtQB.js.map +0 -1
- package/dashboard/dist/assets/index-Bb9ZZUyB.js +0 -5
- package/dashboard/dist/assets/index-Bb9ZZUyB.js.map +0 -1
- package/dashboard/dist/assets/index-BhzsR_gZ.js +0 -2
- package/dashboard/dist/assets/index-D88VWEVp.js +0 -67
- package/dashboard/dist/assets/index-D88VWEVp.js.map +0 -1
- package/dashboard/dist/assets/index-DBvkaa-B.js +0 -6
- package/dashboard/dist/assets/index-DBvkaa-B.js.map +0 -1
- package/dashboard/dist/assets/index-cvmt1plS.js +0 -2
- package/dashboard/dist/assets/index-qU_dXqP4.css +0 -1
- package/dashboard/dist/assets/useDebouncedValue-DhhAKjDC.js +0 -2
- package/dashboard/dist/assets/vendor-icons-R9b9gQ04.js.map +0 -1
- package/dashboard/dist/assets/vendor-query-RUZdMvN6.js +0 -18
- package/dashboard/dist/assets/vendor-query-RUZdMvN6.js.map +0 -1
- package/dashboard/dist/assets/workflows-CYv1CTU0.js +0 -2
- 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** | `/
|
|
16
|
-
| **
|
|
17
|
-
| **
|
|
18
|
-
| **
|
|
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
|
|
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` |
|
|
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
|
|
133
|
+
### Invoke Tool
|
|
123
134
|
|
|
124
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
213
|
+
Accessible at `/workflows/executions`. Lists procedural workflow runs from one HotMesh namespace.
|
|
197
214
|
|
|
198
|
-
- **
|
|
199
|
-
- **
|
|
200
|
-
- **
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
- **
|
|
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/
|
|
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)
|
|
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
|
|
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
|
|
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
|
-
- **
|
|
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
|
|
248
|
-
- **Entity** — the metadata key naming what moves through this
|
|
249
|
-
- **Sequence** — board geometry, shown while the
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 `/
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
439
|
+
### Trend Board
|
|
375
440
|
|
|
376
|
-
**
|
|
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
|
|
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
|
|
404
|
-
- **Preview panel** — click a file to preview it in the side panel
|
|
405
|
-
- **Upload** — drag and drop or
|
|
406
|
-
- **
|
|
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
|
-
|
|
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/
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
**
|
|
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
|
-
###
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
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) |
|
package/docs/faceted-routing.md
CHANGED
|
@@ -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
|
-
|
|
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
|
|
package/docs/hitl/iframe.md
CHANGED
|
@@ -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
|
-
|
|
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) | `
|
|
30
|
-
| 2 | `
|
|
31
|
-
| 3
|
|
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
|
|
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
|
|
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
|