@hotmeshio/long-tail 0.18.0 → 0.19.0
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/settings.js +2 -0
- package/build/api/users.d.ts +11 -0
- package/build/api/users.js +19 -0
- package/build/index.js +6 -0
- package/build/lib/uuid.d.ts +11 -0
- package/build/lib/uuid.js +20 -0
- package/build/modules/search.d.ts +21 -0
- package/build/modules/search.js +55 -0
- package/build/routes/escalations/list.js +10 -1
- package/build/routes/users.js +9 -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/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/role/types.d.ts +18 -0
- package/build/services/role/types.js +6 -0
- 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/start/adapters.js +2 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/startup.d.ts +12 -1
- package/dashboard/dist/assets/{AdminDashboard-mULAQuHV.js → AdminDashboard-DPkWtPc1.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-mULAQuHV.js.map → AdminDashboard-DPkWtPc1.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-DHdzgXcr.js → AgentConfigPage-DRfnLiQS.js} +6 -6
- package/dashboard/dist/assets/{AgentConfigPage-DHdzgXcr.js.map → AgentConfigPage-DRfnLiQS.js.map} +1 -1
- package/dashboard/dist/assets/{AgentDetailPage-Du_33U2u.js → AgentDetailPage-CufRZXnT.js} +3 -3
- package/dashboard/dist/assets/{AgentDetailPage-Du_33U2u.js.map → AgentDetailPage-CufRZXnT.js.map} +1 -1
- package/dashboard/dist/assets/{AgentsPage-4KkQO2Hl.js → AgentsPage-CoCWGhUd.js} +2 -2
- package/dashboard/dist/assets/{AgentsPage-4KkQO2Hl.js.map → AgentsPage-CoCWGhUd.js.map} +1 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-BSqOctep.js +2 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-BSqOctep.js.map +1 -0
- package/dashboard/dist/assets/{BotPicker-BaFiLa-H.js → BotPicker-DMojDLlV.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-BaFiLa-H.js.map → BotPicker-DMojDLlV.js.map} +1 -1
- package/dashboard/dist/assets/BulkAssignModal-DYbM_6Or.js +2 -0
- package/dashboard/dist/assets/BulkAssignModal-DYbM_6Or.js.map +1 -0
- package/dashboard/dist/assets/CapabilitiesPage-CDQ5JHNP.js +2 -0
- package/dashboard/dist/assets/{CapabilitiesPage-Cewd6Uku.js.map → CapabilitiesPage-CDQ5JHNP.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-D1WIbyDa.js → CollapsibleSection-D4VlCEbD.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-D1WIbyDa.js.map → CollapsibleSection-D4VlCEbD.js.map} +1 -1
- package/dashboard/dist/assets/{ConfirmDeleteModal-CQsJUzEa.js → ConfirmDeleteModal-DnNHjJtD.js} +2 -2
- package/dashboard/dist/assets/{ConfirmDeleteModal-CQsJUzEa.js.map → ConfirmDeleteModal-DnNHjJtD.js.map} +1 -1
- package/dashboard/dist/assets/CountdownTimer-DPHMBmgz.js +2 -0
- package/dashboard/dist/assets/CountdownTimer-DPHMBmgz.js.map +1 -0
- package/dashboard/dist/assets/CredentialsPage-CSYZw-rL.js +2 -0
- package/dashboard/dist/assets/{CredentialsPage-CwtkQUG3.js.map → CredentialsPage-CSYZw-rL.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-CRC_uNuC.js → CronLabel-CpmqDrlJ.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-CRC_uNuC.js.map → CronLabel-CpmqDrlJ.js.map} +1 -1
- package/dashboard/dist/assets/{DropZone-Pv-v5RAs.js → DropZone-BbXlQnYB.js} +2 -2
- package/dashboard/dist/assets/{DropZone-Pv-v5RAs.js.map → DropZone-BbXlQnYB.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-CxAi0zN0.js → ElapsedCell-Cul22qBS.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-CxAi0zN0.js.map → ElapsedCell-Cul22qBS.js.map} +1 -1
- package/dashboard/dist/assets/EntityTimelinePanel-CtzpJ-j2.js +2 -0
- package/dashboard/dist/assets/EntityTimelinePanel-CtzpJ-j2.js.map +1 -0
- package/dashboard/dist/assets/{EscalationListSchemaPage--yZfsE8s.js → EscalationListSchemaPage-BZQ8Y_UH.js} +3 -3
- package/dashboard/dist/assets/{EscalationListSchemaPage--yZfsE8s.js.map → EscalationListSchemaPage-BZQ8Y_UH.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationSchemaPage-CmoVqaS7.js → EscalationSchemaPage-CydTtq_A.js} +3 -3
- package/dashboard/dist/assets/{EscalationSchemaPage-CmoVqaS7.js.map → EscalationSchemaPage-CydTtq_A.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-DDQTW6Jo.js → EscalationsOverview-DSYC0nI7.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-DDQTW6Jo.js.map → EscalationsOverview-DSYC0nI7.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-C5jN7XT7.js → EventTable-s48cTXIw.js} +2 -2
- package/dashboard/dist/assets/{EventTable-C5jN7XT7.js.map → EventTable-s48cTXIw.js.map} +1 -1
- package/dashboard/dist/assets/{FilterBar-CcIgQH9h.js → FilterBar-B61qKmFD.js} +2 -2
- package/dashboard/dist/assets/{FilterBar-CcIgQH9h.js.map → FilterBar-B61qKmFD.js.map} +1 -1
- package/dashboard/dist/assets/GraphInvokePage-DxZv_KC2.js +2 -0
- package/dashboard/dist/assets/{GraphInvokePage-LACIxaiH.js.map → GraphInvokePage-DxZv_KC2.js.map} +1 -1
- package/dashboard/dist/assets/HomePage-zK3VP_9g.js +2 -0
- package/dashboard/dist/assets/HomePage-zK3VP_9g.js.map +1 -0
- package/dashboard/dist/assets/{ListToolbar-CLwSkmUH.js → ListToolbar-DaxloPQc.js} +2 -2
- package/dashboard/dist/assets/{ListToolbar-CLwSkmUH.js.map → ListToolbar-DaxloPQc.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-C2U1XfQw.js → McpOverview-Ciq8r6-p.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-C2U1XfQw.js.map → McpOverview-Ciq8r6-p.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-DYOK8A4D.js → McpQueryDetailPage-DIC0FEEb.js} +3 -3
- package/dashboard/dist/assets/{McpQueryDetailPage-DYOK8A4D.js.map → McpQueryDetailPage-DIC0FEEb.js.map} +1 -1
- package/dashboard/dist/assets/McpQueryPage-BBy0NSNg.js +2 -0
- package/dashboard/dist/assets/{McpQueryPage-Ck75ikSd.js.map → McpQueryPage-BBy0NSNg.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunDetailPage-CA9gWQ4L.js → McpRunDetailPage-BqKNmIyW.js} +2 -2
- package/dashboard/dist/assets/{McpRunDetailPage-CA9gWQ4L.js.map → McpRunDetailPage-BqKNmIyW.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunsPage-BhPshset.js → McpRunsPage-D0RlOpzR.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-BhPshset.js.map → McpRunsPage-D0RlOpzR.js.map} +1 -1
- package/dashboard/dist/assets/NamespacePill-Dbtm9R2T.js +2 -0
- package/dashboard/dist/assets/{NamespacePill-DONj6eUU.js.map → NamespacePill-Dbtm9R2T.js.map} +1 -1
- package/dashboard/dist/assets/OperationsPage-BX0SFizr.js +3 -0
- package/dashboard/dist/assets/OperationsPage-BX0SFizr.js.map +1 -0
- package/dashboard/dist/assets/OperatorDashboard-vXG0TjAQ.js +2 -0
- package/dashboard/dist/assets/{OperatorDashboard-DZOfHRE7.js.map → OperatorDashboard-vXG0TjAQ.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeader-CFVw67M6.js → PageHeader-xB92ELs3.js} +2 -2
- package/dashboard/dist/assets/{PageHeader-CFVw67M6.js.map → PageHeader-xB92ELs3.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeaderWithStats-D9tQxILm.js → PageHeaderWithStats-DVo_qqn1.js} +2 -2
- package/dashboard/dist/assets/{PageHeaderWithStats-D9tQxILm.js.map → PageHeaderWithStats-DVo_qqn1.js.map} +1 -1
- package/dashboard/dist/assets/PersonaDetailPage-w5TXcn4Q.js +2 -0
- package/dashboard/dist/assets/{PersonaDetailPage-eyHDupL0.js.map → PersonaDetailPage-w5TXcn4Q.js.map} +1 -1
- package/dashboard/dist/assets/PersonasPage-Dzcinl1L.js +2 -0
- package/dashboard/dist/assets/PersonasPage-Dzcinl1L.js.map +1 -0
- package/dashboard/dist/assets/{ProcessDetailPage-CKiAlpOc.js → ProcessDetailPage-BVXieFX1.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-CKiAlpOc.js.map → ProcessDetailPage-BVXieFX1.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-C4ueZJxU.js → ProcessesListPage-CKKNpk4z.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-C4ueZJxU.js.map → ProcessesListPage-CKKNpk4z.js.map} +1 -1
- package/dashboard/dist/assets/RoleDetailPage-BytSo-YE.js +8 -0
- package/dashboard/dist/assets/RoleDetailPage-BytSo-YE.js.map +1 -0
- package/dashboard/dist/assets/{RolePill-Di_5hfvw.js → RolePill-CYpL3a2d.js} +2 -2
- package/dashboard/dist/assets/{RolePill-Di_5hfvw.js.map → RolePill-CYpL3a2d.js.map} +1 -1
- package/dashboard/dist/assets/RolesPage-eayR9H4f.js +2 -0
- package/dashboard/dist/assets/{RolesPage-B3-9Ek0F.js.map → RolesPage-eayR9H4f.js.map} +1 -1
- package/dashboard/dist/assets/{RunAsSelector-1k1J1_cv.js → RunAsSelector-D7RPwaAl.js} +2 -2
- package/dashboard/dist/assets/{RunAsSelector-1k1J1_cv.js.map → RunAsSelector-D7RPwaAl.js.map} +1 -1
- package/dashboard/dist/assets/{ScanCodesPage-CeuVKnpu.js → ScanCodesPage-C8-ZBXF5.js} +2 -2
- package/dashboard/dist/assets/{ScanCodesPage-CeuVKnpu.js.map → ScanCodesPage-C8-ZBXF5.js.map} +1 -1
- package/dashboard/dist/assets/ScanSchemeDetailPage-tQhEgFKf.js +2 -0
- package/dashboard/dist/assets/{ScanSchemeDetailPage-BnivPUJY.js.map → ScanSchemeDetailPage-tQhEgFKf.js.map} +1 -1
- package/dashboard/dist/assets/ScanStationPage-CuhqsBKo.js +2 -0
- package/dashboard/dist/assets/{ScanStationPage-DCSA7pcV.js.map → ScanStationPage-CuhqsBKo.js.map} +1 -1
- package/dashboard/dist/assets/{ScopeBadge-CqIIeWjK.js → ScopeBadge-BmNzZlSj.js} +2 -2
- package/dashboard/dist/assets/{ScopeBadge-CqIIeWjK.js.map → ScopeBadge-BmNzZlSj.js.map} +1 -1
- package/dashboard/dist/assets/{StickyPagination-BWuKz8uB.js → StickyPagination-BIVTAMnP.js} +2 -2
- package/dashboard/dist/assets/{StickyPagination-BWuKz8uB.js.map → StickyPagination-BIVTAMnP.js.map} +1 -1
- package/dashboard/dist/assets/{StreamMessageDetail-Cq6AGhgJ.js → StreamMessageDetail-CYSC2cO1.js} +2 -2
- package/dashboard/dist/assets/{StreamMessageDetail-Cq6AGhgJ.js.map → StreamMessageDetail-CYSC2cO1.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-DM88avgt.js → SwimlaneTimeline-BMZAs_2K.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-DM88avgt.js.map → SwimlaneTimeline-BMZAs_2K.js.map} +1 -1
- package/dashboard/dist/assets/{TagInput-BLwxTNsI.js → TagInput-D1uszNd2.js} +2 -2
- package/dashboard/dist/assets/{TagInput-BLwxTNsI.js.map → TagInput-D1uszNd2.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-Caqe3fG8.js → TaskDetailPage-CVUDUU5Q.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-Caqe3fG8.js.map → TaskDetailPage-CVUDUU5Q.js.map} +1 -1
- package/dashboard/dist/assets/{TaskQueuePill-DVgcqd-h.js → TaskQueuePill-C2H8oRfn.js} +2 -2
- package/dashboard/dist/assets/{TaskQueuePill-DVgcqd-h.js.map → TaskQueuePill-C2H8oRfn.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-DlQwpXOL.js → TasksListPage-DEQymbXC.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-DlQwpXOL.js.map → TasksListPage-DEQymbXC.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-BinTjiVC.js → TimeAgo-CCxP67Rb.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-BinTjiVC.js.map → TimeAgo-CCxP67Rb.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-DDcfSVeQ.js → TimestampCell-CE5ZUYhV.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-DDcfSVeQ.js.map → TimestampCell-CE5ZUYhV.js.map} +1 -1
- package/dashboard/dist/assets/{ToolPill-C6hkOqPx.js → ToolPill-D-h8AoB8.js} +2 -2
- package/dashboard/dist/assets/{ToolPill-C6hkOqPx.js.map → ToolPill-D-h8AoB8.js.map} +1 -1
- package/dashboard/dist/assets/ToolTestPanel-BHuQTyBY.js +2 -0
- package/dashboard/dist/assets/{ToolTestPanel-CZvegH_w.js.map → ToolTestPanel-BHuQTyBY.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-CShiCwz8.js → TopicDetailPage-SFuiludx.js} +3 -3
- package/dashboard/dist/assets/{TopicDetailPage-CShiCwz8.js.map → TopicDetailPage-SFuiludx.js.map} +1 -1
- package/dashboard/dist/assets/{TopicsPage-CAYxRRbI.js → TopicsPage-DO8pUmng.js} +2 -2
- package/dashboard/dist/assets/{TopicsPage-CAYxRRbI.js.map → TopicsPage-DO8pUmng.js.map} +1 -1
- package/dashboard/dist/assets/UserName-CsLnT75E.js +2 -0
- package/dashboard/dist/assets/UserName-CsLnT75E.js.map +1 -0
- package/dashboard/dist/assets/{WorkflowExecutionPage-DNdXEFcB.js → WorkflowExecutionPage-BttnEvOd.js} +2 -2
- package/dashboard/dist/assets/{WorkflowExecutionPage-DNdXEFcB.js.map → WorkflowExecutionPage-BttnEvOd.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowPill-CN-952iI.js → WorkflowPill-CZvBbkn4.js} +2 -2
- package/dashboard/dist/assets/{WorkflowPill-CN-952iI.js.map → WorkflowPill-CZvBbkn4.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsDashboard-D7DPDeuQ.js → WorkflowsDashboard-BRboBhlK.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsDashboard-D7DPDeuQ.js.map → WorkflowsDashboard-BRboBhlK.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-C7Z0SpDH.js → WorkflowsOverview-CDFTYeQo.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-C7Z0SpDH.js.map → WorkflowsOverview-CDFTYeQo.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-Bxklswko.js → YamlWorkflowDetailPage-CmnZFevp.js} +11 -11
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-Bxklswko.js.map → YamlWorkflowDetailPage-CmnZFevp.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-CkArsPbi.js → YamlWorkflowsPage-Qq1jfcrH.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-CkArsPbi.js.map → YamlWorkflowsPage-Qq1jfcrH.js.map} +1 -1
- package/dashboard/dist/assets/{agents-Ca0hOp2Y.js → agents-C7vUIO7e.js} +2 -2
- package/dashboard/dist/assets/{agents-Ca0hOp2Y.js.map → agents-C7vUIO7e.js.map} +1 -1
- package/dashboard/dist/assets/{bots-9GtbGtH9.js → bots-BaOblnW4.js} +2 -2
- package/dashboard/dist/assets/{bots-9GtbGtH9.js.map → bots-BaOblnW4.js.map} +1 -1
- package/dashboard/dist/assets/{capabilities-C0RpEvyY.js → capabilities-RZpmDWoy.js} +2 -2
- package/dashboard/dist/assets/{capabilities-C0RpEvyY.js.map → capabilities-RZpmDWoy.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-ClQiKGyQ.js → controlplane-BwGdcwXR.js} +2 -2
- package/dashboard/dist/assets/{controlplane-ClQiKGyQ.js.map → controlplane-BwGdcwXR.js.map} +1 -1
- package/dashboard/dist/assets/escalation-columns-B4DG_5v8.js +2 -0
- package/dashboard/dist/assets/escalation-columns-B4DG_5v8.js.map +1 -0
- package/dashboard/dist/assets/index-8LIpvj2e.js +3 -0
- package/dashboard/dist/assets/index-8LIpvj2e.js.map +1 -0
- package/dashboard/dist/assets/index-BICLHYJJ.js +2 -0
- package/dashboard/dist/assets/{index-BLLK2mzy.js.map → index-BICLHYJJ.js.map} +1 -1
- package/dashboard/dist/assets/{index-BLCFN3YR.js → index-BQPUlmmz.js} +2 -2
- package/dashboard/dist/assets/{index-BLCFN3YR.js.map → index-BQPUlmmz.js.map} +1 -1
- package/dashboard/dist/assets/index-BaC3sbJH.js +67 -0
- package/dashboard/dist/assets/index-BaC3sbJH.js.map +1 -0
- package/dashboard/dist/assets/index-BuqpU6ow.js +2 -0
- package/dashboard/dist/assets/{index-DEr9QjVv.js.map → index-BuqpU6ow.js.map} +1 -1
- package/dashboard/dist/assets/index-CD6jtND1.css +1 -0
- package/dashboard/dist/assets/{index-Ba3UsZzz.js → index-CsIw-ZLk.js} +4 -4
- package/dashboard/dist/assets/{index-Ba3UsZzz.js.map → index-CsIw-ZLk.js.map} +1 -1
- package/dashboard/dist/assets/index-CtJkvX9e.js +5 -0
- package/dashboard/dist/assets/{index-Ct_8Tpec.js.map → index-CtJkvX9e.js.map} +1 -1
- package/dashboard/dist/assets/index-D9NtDRtp.js +2 -0
- package/dashboard/dist/assets/{index-BoQirCxt.js.map → index-D9NtDRtp.js.map} +1 -1
- package/dashboard/dist/assets/{index-CeHOGQM4.js → index-DU9-flEo.js} +2 -2
- package/dashboard/dist/assets/{index-CeHOGQM4.js.map → index-DU9-flEo.js.map} +1 -1
- package/dashboard/dist/assets/{index-P1g8r09L.js → index-G3NmdMjt.js} +2 -2
- package/dashboard/dist/assets/{index-P1g8r09L.js.map → index-G3NmdMjt.js.map} +1 -1
- package/dashboard/dist/assets/index-IWufkcH9.js +2 -0
- package/dashboard/dist/assets/{index-DlzW75L4.js.map → index-IWufkcH9.js.map} +1 -1
- package/dashboard/dist/assets/index-iegzyfMv.js +2 -0
- package/dashboard/dist/assets/{index-B-Qpr29t.js.map → index-iegzyfMv.js.map} +1 -1
- package/dashboard/dist/assets/index-xNFTmmsv.js +2 -0
- package/dashboard/dist/assets/{index-CG8oLNHo.js.map → index-xNFTmmsv.js.map} +1 -1
- package/dashboard/dist/assets/{knowledge-DzueDGkw.js → knowledge-CjwCbm3E.js} +2 -2
- package/dashboard/dist/assets/{knowledge-DzueDGkw.js.map → knowledge-CjwCbm3E.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-CCOzN8dH.js → mcp-C-DMktCT.js} +2 -2
- package/dashboard/dist/assets/{mcp-CCOzN8dH.js.map → mcp-C-DMktCT.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-UeMDiM6I.js → mcp-query-KBeBNHb4.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-UeMDiM6I.js.map → mcp-query-KBeBNHb4.js.map} +1 -1
- package/dashboard/dist/assets/{personas-CKcrzaQX.js → personas-B32sSCxm.js} +2 -2
- package/dashboard/dist/assets/{personas-CKcrzaQX.js.map → personas-B32sSCxm.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-orE-1gpj.js → pipelines-DNLwDumm.js} +2 -2
- package/dashboard/dist/assets/{pipelines-orE-1gpj.js.map → pipelines-DNLwDumm.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-ZOmzxynB.js → tasks-DrWSrjmj.js} +2 -2
- package/dashboard/dist/assets/{tasks-ZOmzxynB.js.map → tasks-DrWSrjmj.js.map} +1 -1
- package/dashboard/dist/assets/{topics-CdYaOCi_.js → topics-DgzGU-vR.js} +2 -2
- package/dashboard/dist/assets/{topics-CdYaOCi_.js.map → topics-DgzGU-vR.js.map} +1 -1
- package/dashboard/dist/assets/{useDebouncedValue-D-uGnSj5.js → useDebouncedValue-DG5JOj1F.js} +2 -2
- package/dashboard/dist/assets/{useDebouncedValue-D-uGnSj5.js.map → useDebouncedValue-DG5JOj1F.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-DMrjlrJJ.js → useNamespace-CdgF7Jxo.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-DMrjlrJJ.js.map → useNamespace-CdgF7Jxo.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-ByyQgu8S.js → useYamlActivityEvents-BEaOtgSZ.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-ByyQgu8S.js.map → useYamlActivityEvents-BEaOtgSZ.js.map} +1 -1
- package/dashboard/dist/assets/{vendor-icons-h-jlRSb9.js → vendor-icons-BCWsItQ7.js} +138 -133
- package/dashboard/dist/assets/{vendor-icons-h-jlRSb9.js.map → vendor-icons-BCWsItQ7.js.map} +1 -1
- package/dashboard/dist/assets/{workflows-CLn05FRV.js → workflows-CX_oarls.js} +2 -2
- package/dashboard/dist/assets/{workflows-CLn05FRV.js.map → workflows-CX_oarls.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-DFD_HO__.js → yaml-workflows-YASUDPo0.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-DFD_HO__.js.map → yaml-workflows-YASUDPo0.js.map} +1 -1
- package/dashboard/dist/index.html +3 -3
- package/docs/dashboard.md +164 -69
- package/docs/faceted-routing.md +38 -0
- package/docs/hitl/x-lt-list-schema.md +3 -0
- package/docs/operations.md +61 -60
- package/docs/scan-codes.md +18 -0
- package/package.json +1 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-CjcAIm-d.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-CjcAIm-d.js.map +0 -1
- package/dashboard/dist/assets/BulkAssignModal-DNmji4l-.js +0 -2
- package/dashboard/dist/assets/BulkAssignModal-DNmji4l-.js.map +0 -1
- package/dashboard/dist/assets/CapabilitiesPage-Cewd6Uku.js +0 -2
- package/dashboard/dist/assets/CountdownTimer-BKMmFie6.js +0 -2
- package/dashboard/dist/assets/CountdownTimer-BKMmFie6.js.map +0 -1
- package/dashboard/dist/assets/CredentialsPage-CwtkQUG3.js +0 -2
- package/dashboard/dist/assets/EntityTimelinePanel-DzMuV7ZX.js +0 -2
- package/dashboard/dist/assets/EntityTimelinePanel-DzMuV7ZX.js.map +0 -1
- package/dashboard/dist/assets/GraphInvokePage-LACIxaiH.js +0 -2
- package/dashboard/dist/assets/HomePage-DP9Q-x4a.js +0 -2
- package/dashboard/dist/assets/HomePage-DP9Q-x4a.js.map +0 -1
- package/dashboard/dist/assets/McpQueryPage-Ck75ikSd.js +0 -2
- package/dashboard/dist/assets/Modal-BU7No9NH.js +0 -2
- package/dashboard/dist/assets/Modal-BU7No9NH.js.map +0 -1
- package/dashboard/dist/assets/NamespacePill-DONj6eUU.js +0 -2
- package/dashboard/dist/assets/OperationsPage-MlMzl0Bf.js +0 -3
- package/dashboard/dist/assets/OperationsPage-MlMzl0Bf.js.map +0 -1
- package/dashboard/dist/assets/OperatorDashboard-DZOfHRE7.js +0 -2
- package/dashboard/dist/assets/PersonaDetailPage-eyHDupL0.js +0 -2
- package/dashboard/dist/assets/PersonasPage-CCtRkWiP.js +0 -2
- package/dashboard/dist/assets/PersonasPage-CCtRkWiP.js.map +0 -1
- package/dashboard/dist/assets/RoleDetailPage-BOccQCkx.js +0 -8
- package/dashboard/dist/assets/RoleDetailPage-BOccQCkx.js.map +0 -1
- package/dashboard/dist/assets/RolesPage-B3-9Ek0F.js +0 -2
- package/dashboard/dist/assets/ScanSchemeDetailPage-BnivPUJY.js +0 -2
- package/dashboard/dist/assets/ScanStationPage-DCSA7pcV.js +0 -2
- package/dashboard/dist/assets/ToolTestPanel-CZvegH_w.js +0 -2
- package/dashboard/dist/assets/UserName-BovQxdSY.js +0 -2
- package/dashboard/dist/assets/UserName-BovQxdSY.js.map +0 -1
- package/dashboard/dist/assets/escalation-columns-CZ12_oQF.js +0 -2
- package/dashboard/dist/assets/escalation-columns-CZ12_oQF.js.map +0 -1
- package/dashboard/dist/assets/index-B-Qpr29t.js +0 -2
- package/dashboard/dist/assets/index-BLLK2mzy.js +0 -2
- package/dashboard/dist/assets/index-BoQirCxt.js +0 -2
- package/dashboard/dist/assets/index-C1oOLjdF.js +0 -3
- package/dashboard/dist/assets/index-C1oOLjdF.js.map +0 -1
- package/dashboard/dist/assets/index-CG8oLNHo.js +0 -2
- package/dashboard/dist/assets/index-CdavRyaX.js +0 -67
- package/dashboard/dist/assets/index-CdavRyaX.js.map +0 -1
- package/dashboard/dist/assets/index-Ct_8Tpec.js +0 -5
- package/dashboard/dist/assets/index-DEr9QjVv.js +0 -2
- package/dashboard/dist/assets/index-DlzW75L4.js +0 -2
- package/dashboard/dist/assets/index-fUqvRHOe.css +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
|
|
|
@@ -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,6 +61,7 @@ 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
|
|
|
@@ -74,11 +78,13 @@ Builder-only.
|
|
|
74
78
|
The top navigation bar contains:
|
|
75
79
|
|
|
76
80
|
- **Home logo** — links to the home page (`/`), Recent Activity.
|
|
81
|
+
- **Search bar** — the opt-in global lookup: type an id or facet value, pick a facet, and land on the matching escalation or a filtered queue. Off by default; enable via `search` in `start()` config or `LT_SEARCH_BAR`. See [Global search](#global-search).
|
|
77
82
|
- **all** — links to `/escalations/available` with a live count of unclaimed escalations.
|
|
78
83
|
- **mine** — links to `/escalations/queue` with a live count of escalations assigned to you.
|
|
84
|
+
- **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
85
|
- **events** — toggles the live event feed (builders and ops; doubles as the connection indicator).
|
|
80
86
|
- **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
|
|
87
|
+
- **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
88
|
|
|
83
89
|
## Home — Recent Activity
|
|
84
90
|
|
|
@@ -121,15 +127,16 @@ workers: [
|
|
|
121
127
|
|
|
122
128
|
### Invoke Workflow
|
|
123
129
|
|
|
124
|
-
A two-panel page for starting any invocable procedural workflow. The left panel lists invocable workflows grouped by task queue, with a queue select and search in the filter bar
|
|
130
|
+
Accessible at `/workflows/durable/invoke`. A two-panel page for starting any invocable procedural workflow. The left panel lists invocable workflows grouped by task queue, with a queue select and search in the filter bar; each row carries its tier badge, and workflows with an active cron schedule show a clock icon. Selecting a workflow syncs `?type=<WorkflowType>` to the URL and opens the invocation form on the right:
|
|
125
131
|
|
|
126
|
-
- **Identity summary** —
|
|
127
|
-
- **
|
|
128
|
-
- **
|
|
132
|
+
- **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"). The invocation runs under that identity's `_scope`.
|
|
133
|
+
- **Certification checkbox** — for a certified workflow, stamps `metadata.certified` on this one run.
|
|
134
|
+
- **Envelope editor** — the envelope is `{ data, metadata }`: `data` holds the workflow input, `metadata` optional context. Two modes that stay in sync: a **Form view** that auto-generates a field per `envelope_schema.data` key (type inferred — text, number, boolean, object) and a raw **JSON view**. When a workflow has no `envelope_schema`, a banner links to the registry to add one.
|
|
135
|
+
- **Start Workflow** button — calls the invoke endpoint and navigates to the executions list.
|
|
129
136
|
|
|
130
|
-
Recurring (cron) execution is owned by Automations — schedule workflows from the Agents page.
|
|
137
|
+
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 from the same shape.
|
|
131
138
|
|
|
132
|
-
**API:** `POST /api/workflows/:type/invoke` starts a workflow.
|
|
139
|
+
**API:** `POST /api/workflows/:type/invoke` starts a workflow (body `{ data, metadata?, execute_as? }`, returns `202` with the workflow id). `GET /api/workflows/discovered` backs the list; `GET /api/workflows/:type/config` supplies the envelope schema and identity.
|
|
133
140
|
|
|
134
141
|
### MCP Tool Designer
|
|
135
142
|
|
|
@@ -193,16 +200,18 @@ Execution history for all graph runs — both dynamic (agentic LLM loops) and co
|
|
|
193
200
|
|
|
194
201
|
### Procedural Executions
|
|
195
202
|
|
|
196
|
-
Lists
|
|
203
|
+
Accessible at `/workflows/executions`. Lists procedural workflow runs from one HotMesh namespace.
|
|
197
204
|
|
|
198
|
-
- **
|
|
199
|
-
- **
|
|
200
|
-
- **
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
- **
|
|
205
|
+
- **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`).
|
|
206
|
+
- **Tier filter** — All, Certified, or Durable. Certified narrows to workflows with a certified config; Durable narrows to those with none.
|
|
207
|
+
- **Type / Status / Search** — filter by workflow type, run status (running / completed / failed), or a workflow-id keyword.
|
|
208
|
+
- **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).
|
|
209
|
+
- **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.
|
|
210
|
+
- **Details** (panel) — status, workflow identity (type, id, parent), task queue, start/end times, duration, history size, and activity counts.
|
|
211
|
+
- **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."
|
|
212
|
+
- **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
213
|
|
|
205
|
-
**API:** `GET /api/
|
|
214
|
+
**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
215
|
|
|
207
216
|
### Accounts
|
|
208
217
|
|
|
@@ -219,11 +228,11 @@ User Accounts and Service Accounts live on the same page, separated by a tab tog
|
|
|
219
228
|
|
|
220
229
|
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
230
|
|
|
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)
|
|
231
|
+
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
232
|
|
|
224
233
|
**Master list** (`/admin/roles`) — one row per role:
|
|
225
234
|
|
|
226
|
-
- **Role** — an ops status dot (green marks roles
|
|
235
|
+
- **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
236
|
- **Key** — the exact technical role id.
|
|
228
237
|
- **Description** — the role's human-facing summary.
|
|
229
238
|
- **Preceded By** — the role's prior step (`parent_role`), linked. Prior steps compose the Pace Board's sequences.
|
|
@@ -238,15 +247,15 @@ Search filters by key, label, or description. **+ Add Role** creates a role here
|
|
|
238
247
|
|
|
239
248
|
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
249
|
|
|
241
|
-
**Identity** — display name and description, shown on role rows and
|
|
250
|
+
**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
251
|
|
|
243
252
|
**Pace Board** — everything the board consumes about this role, in one column:
|
|
244
253
|
|
|
245
|
-
- **
|
|
254
|
+
- **Show this role on the Pace Board** — the visibility toggle (`ops_visible`) that charts the role on the [Pace Board](#pace-board).
|
|
246
255
|
- **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
|
|
256
|
+
- **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.
|
|
257
|
+
- **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`).
|
|
258
|
+
- **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
259
|
|
|
251
260
|
**Schemas** — the role's contracts:
|
|
252
261
|
|
|
@@ -257,7 +266,7 @@ Accessible at `/admin/roles/:role`. One page per role — a quiet header carryin
|
|
|
257
266
|
|
|
258
267
|
**Members** — who holds the role: admins manage it; members work its queue according to their read/write work-surface scope (read = which items appear; write = which they can claim and resolve). A `member` grant carries a scope chosen from five named profiles: full worker (`all`/`all`, default), see-all-act-own (`all`/`self`), own-items-only (`self`/`self`), read-only auditor (`all`/`none`), and read-only own (`self`/`none`). The picker enforces **write ⊆ read**. `admin` and `superadmin` grants always work the whole queue.
|
|
259
268
|
|
|
260
|
-
**Pins** — the pinned-view seeds this role hands its members (`default_pins`): label, dashboard-relative URL, optional badge. Members see them in their Pinned nav section from first login (marked role-provided) and may promote, hide, or reorder them via preferences.
|
|
269
|
+
**Pins** — two live-save groups. **Link Variables** leads: the facet names members bind per device (`properties.link_variables` — name, optional label, optional default). A pin below may reference a variable as a facet value, `facets={"facility":"{lt:facility}"}`; each member device opens it scoped to its own binding (set from the user menu → Link variables), falling back to the declared default or, unbound, applying no filter. A caption under each templated pin previews the binding on the viewing device (`facility = 'soleful'` / `facility = <empty>`). **Default Pins** follows — the pinned-view seeds this role hands its members (`default_pins`): label, dashboard-relative URL, optional badge. Members see them in their Pinned nav section from first login (marked role-provided) and may promote, hide, or reorder them via preferences; promoting a templated pin copies the template, so it keeps following the device's bindings. See [Faceted Routing — Link variables](faceted-routing.md#link-variables).
|
|
261
270
|
|
|
262
271
|
**API:** `PATCH /api/roles/:role` updates identity, Pace Board dials, placement, schemas, and pins.
|
|
263
272
|
|
|
@@ -293,6 +302,26 @@ Accessible at `/admin/personas` for admins, superadmins, and engineers — the s
|
|
|
293
302
|
|
|
294
303
|
**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
304
|
|
|
305
|
+
### Scan Codes
|
|
306
|
+
|
|
307
|
+
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.
|
|
308
|
+
|
|
309
|
+
A code encodes **`version:category:target`** (e.g. `10:1:SN-12345`):
|
|
310
|
+
|
|
311
|
+
- **version** (two digits) selects the **scheme** — how the target is parsed and which metadata facet it matches (`serialNumber`, `assetTag`).
|
|
312
|
+
- **category** (one digit) selects the **rule** under that scheme.
|
|
313
|
+
- **target** is the value matched against the scheme's facet.
|
|
314
|
+
|
|
315
|
+
**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.
|
|
316
|
+
|
|
317
|
+
**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.
|
|
318
|
+
|
|
319
|
+
**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.
|
|
320
|
+
|
|
321
|
+
**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.
|
|
322
|
+
|
|
323
|
+
**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.
|
|
324
|
+
|
|
296
325
|
### DB Maintenance
|
|
297
326
|
|
|
298
327
|
Database housekeeping tools for keeping PostgreSQL healthy under sustained workflow load.
|
|
@@ -331,20 +360,39 @@ Messages are read-only. Status is derived from timestamps: pending (no timestamp
|
|
|
331
360
|
|
|
332
361
|
### All Escalations
|
|
333
362
|
|
|
334
|
-
The central queue for
|
|
363
|
+
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.
|
|
364
|
+
|
|
365
|
+
- **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.
|
|
366
|
+
- **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.
|
|
367
|
+
- **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.
|
|
368
|
+
- **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.
|
|
369
|
+
- **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.
|
|
370
|
+
- **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.
|
|
371
|
+
- **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.
|
|
372
|
+
- **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.
|
|
373
|
+
|
|
374
|
+
**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.
|
|
375
|
+
|
|
376
|
+
### My Escalations
|
|
335
377
|
|
|
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.
|
|
378
|
+
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.
|
|
346
379
|
|
|
347
|
-
|
|
380
|
+
### Escalation Detail & Resolve Form
|
|
381
|
+
|
|
382
|
+
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.
|
|
383
|
+
|
|
384
|
+
- **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.
|
|
385
|
+
- **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.
|
|
386
|
+
- **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.
|
|
387
|
+
- **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.
|
|
388
|
+
- **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.
|
|
389
|
+
- **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.
|
|
390
|
+
- **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).
|
|
391
|
+
- **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.
|
|
392
|
+
- **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`.
|
|
393
|
+
- **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.
|
|
394
|
+
|
|
395
|
+
**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
396
|
|
|
349
397
|
### Escalations Overview
|
|
350
398
|
|
|
@@ -352,28 +400,51 @@ Accessible at `/escalations`. A statistics dashboard for escalation health acros
|
|
|
352
400
|
|
|
353
401
|
- **Time window selector** — toggle between 1h, 24h, 7d, and 30d views.
|
|
354
402
|
- **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.
|
|
403
|
+
- **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
404
|
|
|
357
405
|
### Pace Board
|
|
358
406
|
|
|
359
|
-
Accessible at `/
|
|
407
|
+
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?*
|
|
408
|
+
|
|
409
|
+
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.
|
|
410
|
+
|
|
411
|
+
**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.
|
|
412
|
+
|
|
413
|
+
**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.
|
|
414
|
+
|
|
415
|
+
- **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.
|
|
416
|
+
- **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.
|
|
417
|
+
- **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).
|
|
418
|
+
- **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.
|
|
419
|
+
- **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.
|
|
420
|
+
- **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.
|
|
421
|
+
- **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.
|
|
422
|
+
- **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>`.
|
|
423
|
+
- **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.
|
|
360
424
|
|
|
361
|
-
|
|
425
|
+
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).
|
|
362
426
|
|
|
363
|
-
|
|
427
|
+
**API:** `GET /api/escalations/station-metrics?period=<window>&facets=<json>` returns the per-role live counts and windowed percentiles.
|
|
364
428
|
|
|
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.
|
|
429
|
+
### Trend Board
|
|
373
430
|
|
|
374
|
-
|
|
431
|
+
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.
|
|
375
432
|
|
|
376
|
-
|
|
433
|
+
The lens is driven by two dials on each role's [Role Detail → Pace Board](#role-detail) section:
|
|
434
|
+
|
|
435
|
+
- **`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).
|
|
436
|
+
- **`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`).
|
|
437
|
+
|
|
438
|
+
Three tiers, aggregate to individual:
|
|
439
|
+
|
|
440
|
+
- **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.
|
|
441
|
+
- **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.
|
|
442
|
+
- **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.
|
|
443
|
+
- **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.
|
|
444
|
+
|
|
445
|
+
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.
|
|
446
|
+
|
|
447
|
+
**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
448
|
|
|
378
449
|
### Processes Overview
|
|
379
450
|
|
|
@@ -398,28 +469,30 @@ This is the primary view for understanding how a multi-step workflow progresses
|
|
|
398
469
|
|
|
399
470
|
### Files
|
|
400
471
|
|
|
401
|
-
Browse and manage files in connected storage
|
|
472
|
+
Accessible at `/files`. Browse and manage files in the connected storage backend (MinIO locally, S3/GCS in production).
|
|
402
473
|
|
|
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
|
-
- **
|
|
474
|
+
- **File browser** — navigate directories with breadcrumbs; the list shows name, size, and last-modified. Paginated via a continuation token.
|
|
475
|
+
- **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.
|
|
476
|
+
- **Upload** — drag and drop, or the file input, into the current prefix; a confirmation names the target path.
|
|
477
|
+
- **Signed URLs** — generate a time-boxed download link for a file (expiry from a fixed set, 1h to 30d).
|
|
407
478
|
|
|
408
|
-
|
|
479
|
+
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
480
|
|
|
410
|
-
**API:** `GET /api/
|
|
481
|
+
**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
482
|
|
|
412
483
|
### Knowledge
|
|
413
484
|
|
|
414
|
-
|
|
485
|
+
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`.
|
|
486
|
+
|
|
487
|
+
**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.
|
|
415
488
|
|
|
416
|
-
- **
|
|
417
|
-
- **
|
|
418
|
-
- **
|
|
489
|
+
- **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.
|
|
490
|
+
- **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.
|
|
491
|
+
- **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.
|
|
419
492
|
|
|
420
|
-
|
|
493
|
+
**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`).
|
|
421
494
|
|
|
422
|
-
**API:** `GET /api/knowledge`
|
|
495
|
+
**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
496
|
|
|
424
497
|
### Topic Catalog
|
|
425
498
|
|
|
@@ -480,6 +553,28 @@ markdown body. Each user dismisses per-browser.
|
|
|
480
553
|
Role targeting scopes display, never access: the live event reaches every
|
|
481
554
|
authenticated subscriber, so announcement bodies must never carry secrets.
|
|
482
555
|
|
|
556
|
+
### Global search
|
|
557
|
+
|
|
558
|
+
An opt-in header search bar for one-gesture lookups across every escalation
|
|
559
|
+
of any status. Configure it in the `start()` config —
|
|
560
|
+
`search: { enabled: true, facets: ['orderId', 'po'] }` — or by env
|
|
561
|
+
(`LT_SEARCH_BAR=true`, `LT_SEARCH_FACETS=orderId,po`; env wins). The picklist
|
|
562
|
+
always offers `escalationId` and `workflowId` (long-tail-owned lookups) ahead
|
|
563
|
+
of the configured metadata facets, and remembers the last-used facet per
|
|
564
|
+
device.
|
|
565
|
+
|
|
566
|
+
- A metadata facet lands on the escalation list filtered by that facet across
|
|
567
|
+
all statuses, newest first (the same deep link as clicking a facet value).
|
|
568
|
+
- `escalationId` opens the escalation detail directly; an unknown id shows an
|
|
569
|
+
inline notice without navigating.
|
|
570
|
+
- `workflowId` opens the workflow's single escalation, lists several to pick
|
|
571
|
+
from, or links straight to the workflow execution when none exist.
|
|
572
|
+
|
|
573
|
+
Kiosk sessions see the bar too — a station can dump a PO or order id and jump
|
|
574
|
+
straight to it; RBAC read scope bounds what any search can return. See
|
|
575
|
+
[Faceted Routing](faceted-routing.md) — a search is a one-gesture facet deep
|
|
576
|
+
link.
|
|
577
|
+
|
|
483
578
|
### Inbox
|
|
484
579
|
|
|
485
580
|
The Inbox icon in the header shows a badge count when the current user has pending escalations assigned to their roles. The count updates live via NATS — no polling needed.
|
package/docs/faceted-routing.md
CHANGED
|
@@ -91,6 +91,44 @@ const groups = await searchGroups(query, { sizeFacet: 'orderSize', limit: 10 });
|
|
|
91
91
|
const inFlight = await countByFacets({ role: 'print-farm-diabetic', available: false });
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
+
## Link variables
|
|
95
|
+
|
|
96
|
+
A role may declare **link variables** — facet names its members bind per
|
|
97
|
+
device — in `properties.link_variables`:
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{ "link_variables": [ { "name": "facility", "label": "Facility", "default": "main" } ] }
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
A pinned URL references a variable as the ENTIRE value of a facet inside the
|
|
104
|
+
`facets` param:
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
/escalations/available?role=gluer&facets={"facility":"{lt:facility}"}&status=available
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The dashboard substitutes at render time, so every link in the DOM is already
|
|
111
|
+
concrete. Resolution order per variable: the device's bound value → the
|
|
112
|
+
declared `default` → **drop the facet** (unbound means no filter, never an
|
|
113
|
+
empty-string match). Members bind values from the user menu ("Link
|
|
114
|
+
variables"); bindings live in device localStorage (`lt:station:link-vars:{userId}`),
|
|
115
|
+
so a shared workstation keeps its scope across reloads and account switches.
|
|
116
|
+
A shared URL still carrying a raw `{lt:name}` placeholder is resolved against
|
|
117
|
+
the opening device the moment the list page mounts (address bar rewritten in
|
|
118
|
+
place).
|
|
119
|
+
|
|
120
|
+
Values are strings — jsonb containment is type-sensitive, so numeric facets
|
|
121
|
+
are not templatable. Link variables are a view convenience: RBAC read scope
|
|
122
|
+
still governs what any query can return.
|
|
123
|
+
|
|
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#global-search) is the
|
|
129
|
+
one-gesture sibling of this surface: a configured facet name plus a value
|
|
130
|
+
lands on the same all-status faceted list a deep link produces.
|
|
131
|
+
|
|
94
132
|
## Atomic claims
|
|
95
133
|
|
|
96
134
|
Two claim primitives, both `FOR UPDATE SKIP LOCKED` so many consumers run without
|
|
@@ -23,6 +23,8 @@ Every string is a markdown/text template run through the same `{{domain.path}}`
|
|
|
23
23
|
|
|
24
24
|
`format: "age"` on a `facet-table` column or `facet-board` field renders a timestamp as a compact age (`12m`, `3h`, `2d`) with the absolute time as its tooltip, repainted each minute — aging interim states are scannable at a glance.
|
|
25
25
|
|
|
26
|
+
`format: "user"` resolves a user-id token — `{{escalation.assigned_to}}` — to that person's display name. Resolution is batched: every id on a page is coalesced into one `POST /api/users/names` request returning display fields only (no secrets, scopes, or metadata), cached by id for the session and not refetched on window focus; a user edit invalidates the cache. It truncates with an ellipsis and carries the full name on hover; an unassigned row shows a quiet em dash. `assigned_to` is the claimant while the item is worked and stays as the resolver once it is resolved, so one column reads correctly across the row's whole life. The built-in table and timeline views carry this user column by default; authored templates opt in per column.
|
|
27
|
+
|
|
26
28
|
The **active** item is the first non-terminal escalation. The **history** column is lazy-loaded — a "Load full history" link fetches resolved items on demand (`status` defaults to `"resolved"`, `limit` to 25). Unknown or absent `x-lt-layout` is a safe no-op that falls back to the table.
|
|
27
29
|
|
|
28
30
|
### Row action (`x-lt-row-action`)
|
|
@@ -94,6 +96,7 @@ Use when the queue contains many concurrent rows and the role's context is best
|
|
|
94
96
|
{ "label": "Heel cup", "value": "{{metadata.heelCup}}" },
|
|
95
97
|
{ "label": "PDAC", "value": "{{metadata.pdac}}" },
|
|
96
98
|
{ "label": "Station", "value": "{{metadata.station}}" },
|
|
99
|
+
{ "label": "Assignee", "value": "{{escalation.assigned_to}}", "format": "user" },
|
|
97
100
|
{ "label": "Priority", "value": "{{escalation.priority}}" },
|
|
98
101
|
{ "label": "Created", "value": "{{escalation.created_at}}" }
|
|
99
102
|
]
|