@hotmeshio/long-tail 0.10.13 → 0.11.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/README.md +2 -4
- package/build/api/escalations/analytics.d.ts +6 -0
- package/build/api/escalations/analytics.js +123 -0
- package/build/api/escalations/index.d.ts +1 -0
- package/build/api/escalations/index.js +4 -1
- package/build/api/escalations/metadata.js +1 -1
- package/build/api/escalations/resolve.js +4 -4
- package/build/api/roles.d.ts +4 -0
- package/build/api/roles.js +8 -0
- package/build/api/scan-codes/choice.d.ts +11 -0
- package/build/api/scan-codes/choice.js +136 -0
- package/build/api/scan-codes/context.d.ts +24 -0
- package/build/api/scan-codes/context.js +86 -0
- package/build/api/scan-codes/execute.d.ts +1 -0
- package/build/api/scan-codes/execute.js +49 -244
- package/build/api/scan-codes/identity.d.ts +18 -0
- package/build/api/scan-codes/identity.js +116 -0
- package/build/api/scan-codes/index.d.ts +2 -0
- package/build/api/scan-codes/index.js +6 -1
- package/build/api/scan-codes/locate.d.ts +14 -0
- package/build/api/scan-codes/locate.js +127 -0
- package/build/api/scan-codes/present.d.ts +9 -0
- package/build/api/scan-codes/present.js +59 -0
- package/build/api/scan-codes/verbs.d.ts +15 -0
- package/build/api/scan-codes/verbs.js +205 -0
- package/build/bin/ltc.js +33 -0
- package/build/lib/cli/commands/escalations.d.ts +31 -0
- package/build/lib/cli/commands/escalations.js +88 -0
- package/build/lib/db/schemas/028_role_entity_facet.sql +18 -0
- package/build/lib/db/schemas/029_scan_scheme_kind.sql +31 -0
- package/build/modules/config.d.ts +5 -0
- package/build/modules/config.js +12 -0
- package/build/routes/escalations/acting.d.ts +12 -0
- package/build/routes/escalations/acting.js +25 -0
- package/build/routes/escalations/analytics.d.ts +8 -0
- package/build/routes/escalations/analytics.js +63 -0
- package/build/routes/escalations/index.js +3 -0
- package/build/routes/escalations/resolve.js +8 -1
- package/build/routes/escalations/single.js +13 -4
- package/build/routes/roles.js +1 -1
- package/build/routes/scan-codes.js +28 -2
- package/build/scripts/ortho-populate.js +14 -0
- package/build/scripts/printer-cycle.d.ts +25 -0
- package/build/scripts/printer-cycle.js +169 -0
- package/build/scripts/twin-farm.js +19 -5
- package/build/sdk/index.d.ts +2 -0
- package/build/sdk/index.js +3 -0
- package/build/services/escalation/aggregate-sql.d.ts +36 -0
- package/build/services/escalation/aggregate-sql.js +253 -0
- package/build/services/escalation/aggregate-validate.d.ts +57 -0
- package/build/services/escalation/aggregate-validate.js +218 -0
- package/build/services/escalation/aggregates.d.ts +26 -0
- package/build/services/escalation/aggregates.js +204 -0
- package/build/services/escalation/client.js +57 -21
- package/build/services/escalation/facet-sql.js +17 -0
- package/build/services/escalation/index.d.ts +2 -0
- package/build/services/escalation/index.js +8 -1
- package/build/services/escalation/metrics-cache.d.ts +18 -0
- package/build/services/escalation/metrics-cache.js +47 -1
- package/build/services/escalation/queries.js +2 -0
- package/build/services/escalation/resolver-validation.d.ts +1 -1
- package/build/services/escalation/resolver-validation.js +12 -5
- package/build/services/escalation/submit-guard.d.ts +29 -0
- package/build/services/escalation/submit-guard.js +57 -0
- package/build/services/iam/acting-identity.d.ts +16 -0
- package/build/services/iam/acting-identity.js +60 -0
- package/build/services/role/index.js +2 -0
- package/build/services/role/sql.d.ts +10 -2
- package/build/services/role/sql.js +14 -1
- package/build/services/role/types.d.ts +16 -0
- package/build/services/scan-code/read.js +4 -0
- package/build/services/scan-code/sql.d.ts +4 -4
- package/build/services/scan-code/sql.js +14 -8
- package/build/services/scan-code/validate.d.ts +2 -0
- package/build/services/scan-code/validate.js +86 -0
- package/build/services/scan-code/write.d.ts +4 -0
- package/build/services/scan-code/write.js +16 -2
- package/build/services/user/crud.d.ts +6 -0
- package/build/services/user/crud.js +16 -0
- package/build/services/user/index.d.ts +1 -1
- package/build/services/user/index.js +2 -1
- package/build/services/user/sql.d.ts +8 -0
- package/build/services/user/sql.js +9 -1
- package/build/shared/form-validation/index.d.ts +4 -0
- package/build/shared/form-validation/index.js +4 -0
- package/build/shared/form-validation/x-lt-labels.d.ts +28 -0
- package/build/shared/form-validation/x-lt-labels.js +42 -0
- package/build/shared/form-validation/x-lt-query.d.ts +56 -0
- package/build/shared/form-validation/x-lt-query.js +49 -0
- package/build/shared/form-validation/x-lt-submit-guard.d.ts +46 -0
- package/build/shared/form-validation/x-lt-submit-guard.js +25 -0
- package/build/shared/form-validation/x-lt-submit-on-claim.d.ts +22 -0
- package/build/shared/form-validation/x-lt-submit-on-claim.js +28 -0
- package/build/system/mcp-servers/admin/escalations.js +34 -0
- package/build/system/mcp-servers/admin/scan-codes.js +17 -1
- package/build/system/mcp-servers/admin/schemas.d.ts +643 -25
- package/build/system/mcp-servers/admin/schemas.js +127 -19
- package/build/system/mcp-servers/admin/users.js +3 -1
- package/build/system/mcp-servers/human-queue-schemas.d.ts +2 -2
- package/build/system/seed/tool-manifests-admin.d.ts +2283 -0
- package/build/system/seed/tool-manifests-admin.js +8 -5
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/analytics.d.ts +154 -0
- package/build/types/analytics.js +14 -0
- package/build/types/facets.d.ts +13 -0
- package/build/types/index.d.ts +5 -2
- package/build/types/index.js +5 -1
- package/build/types/scan-choice.d.ts +49 -0
- package/build/types/scan-choice.js +9 -0
- package/build/types/scan-code.d.ts +72 -1
- package/build/types/scan-code.js +30 -1
- package/dashboard/dist/assets/{AdminDashboard-Bv3pHBzn.js → AdminDashboard-DJ3LJQ09.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-Bv3pHBzn.js.map → AdminDashboard-DJ3LJQ09.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-Dk2zlvDb.js → AgentConfigPage-BMMvaD0z.js} +2 -2
- package/dashboard/dist/assets/{AgentConfigPage-Dk2zlvDb.js.map → AgentConfigPage-BMMvaD0z.js.map} +1 -1
- package/dashboard/dist/assets/{AgentDetailPage-DUno6EGs.js → AgentDetailPage-C6wfsnRe.js} +2 -2
- package/dashboard/dist/assets/{AgentDetailPage-DUno6EGs.js.map → AgentDetailPage-C6wfsnRe.js.map} +1 -1
- package/dashboard/dist/assets/AgentsPage-Cwqka7nE.js +2 -0
- package/dashboard/dist/assets/{AgentsPage-CToVu1qj.js.map → AgentsPage-Cwqka7nE.js.map} +1 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-CVlG_AVC.js +2 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-CVlG_AVC.js.map +1 -0
- package/dashboard/dist/assets/{BotPicker-DQOpVB4M.js → BotPicker-BBkJBXZB.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-DQOpVB4M.js.map → BotPicker-BBkJBXZB.js.map} +1 -1
- package/dashboard/dist/assets/{CapabilitiesPage-Dmf46PUJ.js → CapabilitiesPage-wxmE4917.js} +2 -2
- package/dashboard/dist/assets/{CapabilitiesPage-Dmf46PUJ.js.map → CapabilitiesPage-wxmE4917.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-CDADefUY.js → CollapsibleSection-B3FsrHwH.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-CDADefUY.js.map → CollapsibleSection-B3FsrHwH.js.map} +1 -1
- package/dashboard/dist/assets/CountdownTimer-B3nCqJdC.js +2 -0
- package/dashboard/dist/assets/CountdownTimer-B3nCqJdC.js.map +1 -0
- package/dashboard/dist/assets/{CredentialsPage-8LIEvomi.js → CredentialsPage-Bv875IHe.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-8LIEvomi.js.map → CredentialsPage-Bv875IHe.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-CK3WSsM5.js → CronLabel-CSzjWuwj.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-CK3WSsM5.js.map → CronLabel-CSzjWuwj.js.map} +1 -1
- package/dashboard/dist/assets/{CustomDurationPicker-FPmlA6cE.js → CustomDurationPicker-CCpqf3e4.js} +2 -2
- package/dashboard/dist/assets/{CustomDurationPicker-FPmlA6cE.js.map → CustomDurationPicker-CCpqf3e4.js.map} +1 -1
- package/dashboard/dist/assets/{DropZone-CL9gA7pu.js → DropZone-CJkk-U3o.js} +2 -2
- package/dashboard/dist/assets/{DropZone-CL9gA7pu.js.map → DropZone-CJkk-U3o.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-BZVkVxE0.js → ElapsedCell-WV6pUaKo.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-BZVkVxE0.js.map → ElapsedCell-WV6pUaKo.js.map} +1 -1
- package/dashboard/dist/assets/EntityTimelinePanel-DrL86Yrf.js +2 -0
- package/dashboard/dist/assets/EntityTimelinePanel-DrL86Yrf.js.map +1 -0
- package/dashboard/dist/assets/{EscalationListSchemaPage-kYLdUtZX.js → EscalationListSchemaPage-CTQCALw4.js} +3 -3
- package/dashboard/dist/assets/{EscalationListSchemaPage-kYLdUtZX.js.map → EscalationListSchemaPage-CTQCALw4.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationSchemaPage-kUmCDivo.js → EscalationSchemaPage-D_UgP0pv.js} +3 -3
- package/dashboard/dist/assets/{EscalationSchemaPage-kUmCDivo.js.map → EscalationSchemaPage-D_UgP0pv.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-D17auO-m.js → EscalationsOverview-DuIUyxr0.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-D17auO-m.js.map → EscalationsOverview-DuIUyxr0.js.map} +1 -1
- package/dashboard/dist/assets/EventTable-BV6unphX.js +2 -0
- package/dashboard/dist/assets/EventTable-BV6unphX.js.map +1 -0
- package/dashboard/dist/assets/FilterBar-DYFgzXNB.js +2 -0
- package/dashboard/dist/assets/FilterBar-DYFgzXNB.js.map +1 -0
- package/dashboard/dist/assets/{GraphInvokePage-D11uOnZi.js → GraphInvokePage-BGHVUipJ.js} +2 -2
- package/dashboard/dist/assets/{GraphInvokePage-D11uOnZi.js.map → GraphInvokePage-BGHVUipJ.js.map} +1 -1
- package/dashboard/dist/assets/HomePage-B-S2cK3E.js +2 -0
- package/dashboard/dist/assets/{HomePage-Bcq10Bxx.js.map → HomePage-B-S2cK3E.js.map} +1 -1
- package/dashboard/dist/assets/ListToolbar-Bl7F3A28.js +2 -0
- package/dashboard/dist/assets/{ListToolbar-IPXsTb3Q.js.map → ListToolbar-Bl7F3A28.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-Dx2j0KkF.js → McpOverview-_T9IsCD_.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-Dx2j0KkF.js.map → McpOverview-_T9IsCD_.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-CzF_bna8.js → McpQueryDetailPage-DtsVqKPw.js} +2 -2
- package/dashboard/dist/assets/{McpQueryDetailPage-CzF_bna8.js.map → McpQueryDetailPage-DtsVqKPw.js.map} +1 -1
- package/dashboard/dist/assets/McpQueryPage-DX5u-1bx.js +2 -0
- package/dashboard/dist/assets/{McpQueryPage-CNsuiw_q.js.map → McpQueryPage-DX5u-1bx.js.map} +1 -1
- package/dashboard/dist/assets/McpRunDetailPage-BhcqqtYS.js +2 -0
- package/dashboard/dist/assets/McpRunDetailPage-BhcqqtYS.js.map +1 -0
- package/dashboard/dist/assets/{McpRunsPage-CK96s3Nt.js → McpRunsPage-L-1y8wnX.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-CK96s3Nt.js.map → McpRunsPage-L-1y8wnX.js.map} +1 -1
- package/dashboard/dist/assets/{NamespacePill-B9Vc_Pz2.js → NamespacePill-in1SLd0W.js} +2 -2
- package/dashboard/dist/assets/{NamespacePill-B9Vc_Pz2.js.map → NamespacePill-in1SLd0W.js.map} +1 -1
- package/dashboard/dist/assets/OperationsPage-i8KcDpKo.js +3 -0
- package/dashboard/dist/assets/OperationsPage-i8KcDpKo.js.map +1 -0
- package/dashboard/dist/assets/OperatorDashboard-CIbDzOlh.js +2 -0
- package/dashboard/dist/assets/{OperatorDashboard-C-jE0IRA.js.map → OperatorDashboard-CIbDzOlh.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeader-Ce4GcIYo.js → PageHeader-BJgWMZtw.js} +2 -2
- package/dashboard/dist/assets/{PageHeader-Ce4GcIYo.js.map → PageHeader-BJgWMZtw.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeaderWithStats-Ck0KZvXD.js → PageHeaderWithStats-Ccio4vyG.js} +2 -2
- package/dashboard/dist/assets/{PageHeaderWithStats-Ck0KZvXD.js.map → PageHeaderWithStats-Ccio4vyG.js.map} +1 -1
- package/dashboard/dist/assets/{PersonaDetailPage-CJ6jqo7h.js → PersonaDetailPage-BOaf0TW1.js} +2 -2
- package/dashboard/dist/assets/{PersonaDetailPage-CJ6jqo7h.js.map → PersonaDetailPage-BOaf0TW1.js.map} +1 -1
- package/dashboard/dist/assets/{PersonasPage-D8hL3kL5.js → PersonasPage-DSdRb90f.js} +2 -2
- package/dashboard/dist/assets/{PersonasPage-D8hL3kL5.js.map → PersonasPage-DSdRb90f.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessDetailPage-Vnx8Z5_y.js → ProcessDetailPage-DN9NqfeP.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-Vnx8Z5_y.js.map → ProcessDetailPage-DN9NqfeP.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-DRYtvS3N.js → ProcessesListPage-CfQEWaD4.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-DRYtvS3N.js.map → ProcessesListPage-CfQEWaD4.js.map} +1 -1
- package/dashboard/dist/assets/RoleDetailPage-BC5HL7ls.js +8 -0
- package/dashboard/dist/assets/RoleDetailPage-BC5HL7ls.js.map +1 -0
- package/dashboard/dist/assets/{RolePill-CnuBPhPw.js → RolePill-BNAP-uK6.js} +2 -2
- package/dashboard/dist/assets/{RolePill-CnuBPhPw.js.map → RolePill-BNAP-uK6.js.map} +1 -1
- package/dashboard/dist/assets/RolesPage-DCnW0VmD.js +2 -0
- package/dashboard/dist/assets/RolesPage-DCnW0VmD.js.map +1 -0
- package/dashboard/dist/assets/RunAsSelector-twbuX7fB.js +2 -0
- package/dashboard/dist/assets/{RunAsSelector-CS3KQ-BF.js.map → RunAsSelector-twbuX7fB.js.map} +1 -1
- package/dashboard/dist/assets/ScanCodesPage-CPNg6K--.js +2 -0
- package/dashboard/dist/assets/ScanCodesPage-CPNg6K--.js.map +1 -0
- package/dashboard/dist/assets/ScanSchemeDetailPage-DyLmCamO.js +2 -0
- package/dashboard/dist/assets/ScanSchemeDetailPage-DyLmCamO.js.map +1 -0
- package/dashboard/dist/assets/ScanStationPage-DoZccU5B.js +2 -0
- package/dashboard/dist/assets/ScanStationPage-DoZccU5B.js.map +1 -0
- package/dashboard/dist/assets/{ScopeBadge-BUpYpS3x.js → ScopeBadge-BG7KkZ8Q.js} +2 -2
- package/dashboard/dist/assets/{ScopeBadge-BUpYpS3x.js.map → ScopeBadge-BG7KkZ8Q.js.map} +1 -1
- package/dashboard/dist/assets/SegmentedTabs-DXNzY05q.js +2 -0
- package/dashboard/dist/assets/SegmentedTabs-DXNzY05q.js.map +1 -0
- package/dashboard/dist/assets/StickyPagination-DIeYMPPd.js +2 -0
- package/dashboard/dist/assets/StickyPagination-DIeYMPPd.js.map +1 -0
- package/dashboard/dist/assets/{StreamMessageDetail-DyJyZwBl.js → StreamMessageDetail-gU4CUeQI.js} +2 -2
- package/dashboard/dist/assets/{StreamMessageDetail-DyJyZwBl.js.map → StreamMessageDetail-gU4CUeQI.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-BlzV74V5.js → SwimlaneTimeline-Br-nBA4l.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-BlzV74V5.js.map → SwimlaneTimeline-Br-nBA4l.js.map} +1 -1
- package/dashboard/dist/assets/{TagInput-BbZ25nNe.js → TagInput-CvM5n6ww.js} +2 -2
- package/dashboard/dist/assets/{TagInput-BbZ25nNe.js.map → TagInput-CvM5n6ww.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-B0p_Z4J_.js → TaskDetailPage-_9cr_R0u.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-B0p_Z4J_.js.map → TaskDetailPage-_9cr_R0u.js.map} +1 -1
- package/dashboard/dist/assets/{TaskQueuePill-D0QKBrN3.js → TaskQueuePill-D4JPHXOq.js} +2 -2
- package/dashboard/dist/assets/{TaskQueuePill-D0QKBrN3.js.map → TaskQueuePill-D4JPHXOq.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-CiGR2QiQ.js → TasksListPage-DVENC9Jp.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-CiGR2QiQ.js.map → TasksListPage-DVENC9Jp.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-D_DA9ucC.js → TimeAgo-C9yYnOui.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-D_DA9ucC.js.map → TimeAgo-C9yYnOui.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-C7rkC9nh.js → TimestampCell-DoB8cYgw.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-C7rkC9nh.js.map → TimestampCell-DoB8cYgw.js.map} +1 -1
- package/dashboard/dist/assets/{ToolPill-8xY9rkoc.js → ToolPill-BUjbC-Tj.js} +2 -2
- package/dashboard/dist/assets/{ToolPill-8xY9rkoc.js.map → ToolPill-BUjbC-Tj.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-E8efYGYs.js → ToolTestPanel-CBtsIP8K.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-E8efYGYs.js.map → ToolTestPanel-CBtsIP8K.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-B455XrRc.js → TopicDetailPage-mR4Jaz83.js} +4 -4
- package/dashboard/dist/assets/{TopicDetailPage-B455XrRc.js.map → TopicDetailPage-mR4Jaz83.js.map} +1 -1
- package/dashboard/dist/assets/TopicsPage-CP7HFxIC.js +2 -0
- package/dashboard/dist/assets/{TopicsPage-1VAc29Qw.js.map → TopicsPage-CP7HFxIC.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-DikzjQHJ.js → UserName-BnLF6yhz.js} +2 -2
- package/dashboard/dist/assets/{UserName-DikzjQHJ.js.map → UserName-BnLF6yhz.js.map} +1 -1
- package/dashboard/dist/assets/WorkflowExecutionPage-xL956RZi.js +2 -0
- package/dashboard/dist/assets/WorkflowExecutionPage-xL956RZi.js.map +1 -0
- package/dashboard/dist/assets/{WorkflowPill-Da406nEj.js → WorkflowPill-tXU0-eXu.js} +2 -2
- package/dashboard/dist/assets/{WorkflowPill-Da406nEj.js.map → WorkflowPill-tXU0-eXu.js.map} +1 -1
- package/dashboard/dist/assets/WorkflowsDashboard-B-u7zFKM.js +2 -0
- package/dashboard/dist/assets/{WorkflowsDashboard-BMQd_0tW.js.map → WorkflowsDashboard-B-u7zFKM.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-CdxjVifp.js → WorkflowsOverview-CqZVarK-.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-CdxjVifp.js.map → WorkflowsOverview-CqZVarK-.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-DNVffIXa.js → YamlWorkflowDetailPage-DiFLavW0.js} +10 -10
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-DNVffIXa.js.map → YamlWorkflowDetailPage-DiFLavW0.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-BGP8wuos.js → YamlWorkflowsPage-BuQM6uZA.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-BGP8wuos.js.map → YamlWorkflowsPage-BuQM6uZA.js.map} +1 -1
- package/dashboard/dist/assets/{agents-BZJLIJ4h.js → agents-DU9GPovp.js} +2 -2
- package/dashboard/dist/assets/{agents-BZJLIJ4h.js.map → agents-DU9GPovp.js.map} +1 -1
- package/dashboard/dist/assets/{bots-ClLcDGJb.js → bots-Cy-Hmg7w.js} +2 -2
- package/dashboard/dist/assets/{bots-ClLcDGJb.js.map → bots-Cy-Hmg7w.js.map} +1 -1
- package/dashboard/dist/assets/capabilities-sSJJqrp-.js +2 -0
- package/dashboard/dist/assets/{capabilities-IaCcS7ge.js.map → capabilities-sSJJqrp-.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-Zi4m_MSx.js → controlplane-TVCjDHQ-.js} +2 -2
- package/dashboard/dist/assets/{controlplane-Zi4m_MSx.js.map → controlplane-TVCjDHQ-.js.map} +1 -1
- package/dashboard/dist/assets/escalation-columns-BpkjKYGd.js +2 -0
- package/dashboard/dist/assets/escalation-columns-BpkjKYGd.js.map +1 -0
- package/dashboard/dist/assets/index-5TblaCXQ.css +1 -0
- package/dashboard/dist/assets/index-7BP6FaCZ.js +2 -0
- package/dashboard/dist/assets/{index-Dldi3Q3M.js.map → index-7BP6FaCZ.js.map} +1 -1
- package/dashboard/dist/assets/{index-BzkE48xJ.js → index-B7IaBPBg.js} +4 -4
- package/dashboard/dist/assets/{index-BzkE48xJ.js.map → index-B7IaBPBg.js.map} +1 -1
- package/dashboard/dist/assets/index-BE0phFw0.js +2 -0
- package/dashboard/dist/assets/{index-CLmZaNTK.js.map → index-BE0phFw0.js.map} +1 -1
- package/dashboard/dist/assets/{index-BqGnhkh0.js → index-BYNN9N1T.js} +2 -2
- package/dashboard/dist/assets/{index-BqGnhkh0.js.map → index-BYNN9N1T.js.map} +1 -1
- package/dashboard/dist/assets/{index-kiKZresb.js → index-CLPVqbVY.js} +2 -2
- package/dashboard/dist/assets/{index-kiKZresb.js.map → index-CLPVqbVY.js.map} +1 -1
- package/dashboard/dist/assets/{index-C3o4OisO.js → index-Cfv86_L9.js} +2 -2
- package/dashboard/dist/assets/{index-C3o4OisO.js.map → index-Cfv86_L9.js.map} +1 -1
- package/dashboard/dist/assets/{index-yGNt29V9.js → index-CtwbQ_-m.js} +2 -2
- package/dashboard/dist/assets/{index-yGNt29V9.js.map → index-CtwbQ_-m.js.map} +1 -1
- package/dashboard/dist/assets/index-DOai950R.js +3 -0
- package/dashboard/dist/assets/index-DOai950R.js.map +1 -0
- package/dashboard/dist/assets/index-DTtHNC-D.js +5 -0
- package/dashboard/dist/assets/{index-CvNBdkB1.js.map → index-DTtHNC-D.js.map} +1 -1
- package/dashboard/dist/assets/{index-CJu2PBNx.js → index-DsWnVyVN.js} +2 -2
- package/dashboard/dist/assets/{index-CJu2PBNx.js.map → index-DsWnVyVN.js.map} +1 -1
- package/dashboard/dist/assets/{index-D0zFReGL.js → index-Dy-MMYti.js} +2 -2
- package/dashboard/dist/assets/{index-D0zFReGL.js.map → index-Dy-MMYti.js.map} +1 -1
- package/dashboard/dist/assets/index-errgQT5x.js +65 -0
- package/dashboard/dist/assets/index-errgQT5x.js.map +1 -0
- package/dashboard/dist/assets/{index-CmnNqpWg.js → index-fgBDhnI6.js} +2 -2
- package/dashboard/dist/assets/{index-CmnNqpWg.js.map → index-fgBDhnI6.js.map} +1 -1
- package/dashboard/dist/assets/{knowledge-DWLxGTib.js → knowledge-arh4NfmL.js} +2 -2
- package/dashboard/dist/assets/{knowledge-DWLxGTib.js.map → knowledge-arh4NfmL.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-B6AARZ2I.js → mcp-QGf5jHCo.js} +2 -2
- package/dashboard/dist/assets/{mcp-B6AARZ2I.js.map → mcp-QGf5jHCo.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-DrkvBU3a.js → mcp-query-BEJN1nMZ.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-DrkvBU3a.js.map → mcp-query-BEJN1nMZ.js.map} +1 -1
- package/dashboard/dist/assets/{personas-Cmz2SAEU.js → personas-DYtw1COf.js} +2 -2
- package/dashboard/dist/assets/{personas-Cmz2SAEU.js.map → personas-DYtw1COf.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-CSgfVCMy.js → pipelines-Byi6PnbE.js} +2 -2
- package/dashboard/dist/assets/{pipelines-CSgfVCMy.js.map → pipelines-Byi6PnbE.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-UOtUkDk-.js → tasks-D0SQdENz.js} +2 -2
- package/dashboard/dist/assets/{tasks-UOtUkDk-.js.map → tasks-D0SQdENz.js.map} +1 -1
- package/dashboard/dist/assets/{topics-Df5hGh6o.js → topics-DgDv5ZXP.js} +2 -2
- package/dashboard/dist/assets/{topics-Df5hGh6o.js.map → topics-DgDv5ZXP.js.map} +1 -1
- package/dashboard/dist/assets/useEventHooks-DZaYzE7s.js +2 -0
- package/dashboard/dist/assets/{useEventHooks-PDbuSJ12.js.map → useEventHooks-DZaYzE7s.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-tZSOlge3.js → useNamespace-C1OqiNXN.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-tZSOlge3.js.map → useNamespace-C1OqiNXN.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-CINLzE32.js → useYamlActivityEvents-CkmXDm-Z.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-CINLzE32.js.map → useYamlActivityEvents-CkmXDm-Z.js.map} +1 -1
- package/dashboard/dist/assets/{users-G8qwyZKS.js → users-D4DCFPH1.js} +2 -2
- package/dashboard/dist/assets/{users-G8qwyZKS.js.map → users-D4DCFPH1.js.map} +1 -1
- package/dashboard/dist/assets/{vendor-icons-BpZiWH2f.js → vendor-icons-BxuyFxH2.js} +156 -136
- package/dashboard/dist/assets/vendor-icons-BxuyFxH2.js.map +1 -0
- package/dashboard/dist/assets/{workflows-DbPqi1TW.js → workflows-qyc0ku_5.js} +2 -2
- package/dashboard/dist/assets/{workflows-DbPqi1TW.js.map → workflows-qyc0ku_5.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-DozD8hpR.js → yaml-workflows-DWFww9Xn.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-DozD8hpR.js.map → yaml-workflows-DWFww9Xn.js.map} +1 -1
- package/dashboard/dist/index.html +3 -3
- package/docs/api/http/escalations.md +133 -0
- package/docs/api/http/roles.md +6 -0
- package/docs/api/mcp/admin.md +53 -0
- package/docs/api/sdk/escalations.md +58 -0
- package/docs/cli.md +11 -0
- package/docs/dashboard.md +22 -20
- package/docs/data.md +12 -4
- package/docs/design-principles.md +9 -7
- package/docs/escalation-analytics.md +251 -0
- package/docs/hitl/roles.md +1 -1
- package/docs/hitl/x-lt-embed.md +8 -4
- package/docs/hitl/x-lt-footer.md +67 -0
- package/docs/hitl/x-lt-list-schema.md +10 -3
- package/docs/hitl-guide.md +11 -2
- package/docs/iam.md +12 -0
- package/docs/operations.md +17 -0
- package/docs/scan-codes.md +112 -0
- package/docs/sdk.md +1 -1
- package/package.json +2 -1
- package/dashboard/dist/assets/AgentsPage-CToVu1qj.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-DldboL4W.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-DldboL4W.js.map +0 -1
- package/dashboard/dist/assets/CountdownTimer-DPjIkerB.js +0 -2
- package/dashboard/dist/assets/CountdownTimer-DPjIkerB.js.map +0 -1
- package/dashboard/dist/assets/EventTable-CwPWl163.js +0 -2
- package/dashboard/dist/assets/EventTable-CwPWl163.js.map +0 -1
- package/dashboard/dist/assets/FilterBar-D7GghUsU.js +0 -2
- package/dashboard/dist/assets/FilterBar-D7GghUsU.js.map +0 -1
- package/dashboard/dist/assets/HomePage-Bcq10Bxx.js +0 -2
- package/dashboard/dist/assets/ListToolbar-IPXsTb3Q.js +0 -2
- package/dashboard/dist/assets/McpQueryPage-CNsuiw_q.js +0 -2
- package/dashboard/dist/assets/McpRunDetailPage-BJguEMS0.js +0 -2
- package/dashboard/dist/assets/McpRunDetailPage-BJguEMS0.js.map +0 -1
- package/dashboard/dist/assets/OperationsPage-DvWrLuH6.js +0 -2
- package/dashboard/dist/assets/OperationsPage-DvWrLuH6.js.map +0 -1
- package/dashboard/dist/assets/OperatorDashboard-C-jE0IRA.js +0 -2
- package/dashboard/dist/assets/RoleDetailPage-BKkUvTO-.js +0 -8
- package/dashboard/dist/assets/RoleDetailPage-BKkUvTO-.js.map +0 -1
- package/dashboard/dist/assets/RolesPage-BHzWK9n6.js +0 -2
- package/dashboard/dist/assets/RolesPage-BHzWK9n6.js.map +0 -1
- package/dashboard/dist/assets/RunAsSelector-CS3KQ-BF.js +0 -2
- package/dashboard/dist/assets/ScanCodesPage-BW4P4zWf.js +0 -2
- package/dashboard/dist/assets/ScanCodesPage-BW4P4zWf.js.map +0 -1
- package/dashboard/dist/assets/ScanSchemeDetailPage-BLykA4j-.js +0 -2
- package/dashboard/dist/assets/ScanSchemeDetailPage-BLykA4j-.js.map +0 -1
- package/dashboard/dist/assets/StickyPagination-hMrnLXde.js +0 -2
- package/dashboard/dist/assets/StickyPagination-hMrnLXde.js.map +0 -1
- package/dashboard/dist/assets/TopicsPage-1VAc29Qw.js +0 -2
- package/dashboard/dist/assets/WorkflowExecutionPage-C15GSAoe.js +0 -2
- package/dashboard/dist/assets/WorkflowExecutionPage-C15GSAoe.js.map +0 -1
- package/dashboard/dist/assets/WorkflowsDashboard-BMQd_0tW.js +0 -2
- package/dashboard/dist/assets/capabilities-IaCcS7ge.js +0 -2
- package/dashboard/dist/assets/escalation-columns-BY2mg9DY.js +0 -2
- package/dashboard/dist/assets/escalation-columns-BY2mg9DY.js.map +0 -1
- package/dashboard/dist/assets/index-CLmZaNTK.js +0 -2
- package/dashboard/dist/assets/index-CfAi1OsI.js +0 -65
- package/dashboard/dist/assets/index-CfAi1OsI.js.map +0 -1
- package/dashboard/dist/assets/index-CvNBdkB1.js +0 -5
- package/dashboard/dist/assets/index-Dldi3Q3M.js +0 -2
- package/dashboard/dist/assets/index-mKcQGi2B.js +0 -3
- package/dashboard/dist/assets/index-mKcQGi2B.js.map +0 -1
- package/dashboard/dist/assets/index-zQiUPfxA.css +0 -1
- package/dashboard/dist/assets/useEventHooks-PDbuSJ12.js +0 -2
- package/dashboard/dist/assets/vendor-icons-BpZiWH2f.js.map +0 -1
|
@@ -1154,3 +1154,136 @@ Batch-claim individual rows matching a facet query (`FOR UPDATE SKIP LOCKED`), a
|
|
|
1154
1154
|
| `allOrNone` | `boolean` | no | Commit only if the full set was acquired |
|
|
1155
1155
|
|
|
1156
1156
|
**Response 200:** `{ "claimed": [...] }`.
|
|
1157
|
+
|
|
1158
|
+
## Aggregate by facets
|
|
1159
|
+
|
|
1160
|
+
```
|
|
1161
|
+
POST /api/escalations/aggregate-by-facets
|
|
1162
|
+
```
|
|
1163
|
+
|
|
1164
|
+
Grouped analytics over the escalation intervals. Every escalation is one open interval `[created_at, ended_at)` — `ended_at` is the instant the row left the live set (resolved / cancelled / expired), `NULL` while live. The aggregate reads that time-series two ways: **membership** (rows — or, with `distinctBy`, distinct entities — whose interval is open at an instant; a past `asOf` reconstructs the live set at that moment) and **dwell** (open-seconds per group within a half-open `[from, to)` window, clipped to it on both ends). One call replaces N per-filter count round-trips. See [escalation-analytics.md](../../escalation-analytics.md) for the full guide.
|
|
1165
|
+
|
|
1166
|
+
**Request body:**
|
|
1167
|
+
|
|
1168
|
+
| Field | Type | Required | Description |
|
|
1169
|
+
|-------|------|----------|-------------|
|
|
1170
|
+
| `query` | `object` | yes | The filter — `role`/`roles` (or `entity`), `facets`, `anyOf` (rows carrying ANY of the given facet sets, max 200 — target an explicit entity set such as one table page), `block`, `range`, `exists`, and `prefix` (case-insensitive prefix match on facet values — the locate affordance, e.g. `{"serialNumber":"PRN-00"}`). `entity` names an entity facet key and resolves server-side to every role declaring it as its `entity_facet` — the entity's **system**; mutually exclusive with `role`/`roles`, and it implies `exists: [entity]` so stray rows without the key stay out |
|
|
1171
|
+
| `groupBy` | `object` | yes | Group keys — `columns` (whitelisted: `role`, `subtype`, `status`), `facets` (metadata keys, projected as text; `NULL` group key when absent), and `state` (group by the derived state label: each role contributes per its `entity_state_source` — its subtypes or itself; mutually exclusive with `states[]`). An empty object yields one total row |
|
|
1172
|
+
| `measure` | `object` | yes | Exactly one kind: `{ "kind": "membership", "asOf"? }` (default anchor: now) or `{ "kind": "dwell", "window": { "from", "to" } }` |
|
|
1173
|
+
| `distinctBy` | `string` | no | Membership only — count DISTINCT of this metadata facet per group (entities, not rows). Omit to count rows |
|
|
1174
|
+
| `states` | `array` | no | Pure labeling: tag each group with the FIRST matching `{ name, match }` entry, evaluated top-to-bottom; `match` compares grouped columns/facets. Grouping is unchanged |
|
|
1175
|
+
| `liveStatuses` | `string[]` | no | Statuses considered live (default `["pending"]`) |
|
|
1176
|
+
| `orderBy` | `array` | no | Order the RESULT groups: `{ field, direction? }` — `count`, `dwellSeconds`, `sampleCount`, a grouped column, or a grouped facet key |
|
|
1177
|
+
| `limit` / `offset` | `integer` | no | Result-group paging, capped at `LT_ANALYTICS_MAX_GROUPS` |
|
|
1178
|
+
|
|
1179
|
+
The filter takes the WHAT fields only. `status`, `available`, and `jeopardy` are rejected — liveness derives from the interval, `liveStatuses`, and the measure anchor. Query-level `orderBy`/`limit`/`offset` are rejected too — order and paginate the result groups with the top-level fields.
|
|
1180
|
+
|
|
1181
|
+
**Example** — how the printer fleet spent a day, one row per state. The three seeded roles declare `entity_facet: "serialNumber"`: `printer-fleet` uses `entity_state_source: "subtype"` (its `idle` / `printing` subtypes are states), while `printer-harvest` and `printer-service` use `"role"` (each role is a state):
|
|
1182
|
+
|
|
1183
|
+
```json
|
|
1184
|
+
{
|
|
1185
|
+
"query": { "entity": "serialNumber" },
|
|
1186
|
+
"groupBy": { "state": true },
|
|
1187
|
+
"measure": {
|
|
1188
|
+
"kind": "dwell",
|
|
1189
|
+
"window": { "from": "2026-08-01T00:00:00Z", "to": "2026-08-02T00:00:00Z" }
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
```
|
|
1193
|
+
|
|
1194
|
+
**Response 200:**
|
|
1195
|
+
|
|
1196
|
+
```json
|
|
1197
|
+
{
|
|
1198
|
+
"groups": [
|
|
1199
|
+
{ "facets": {}, "state": "printing", "dwellSeconds": 214380, "sampleCount": 41 },
|
|
1200
|
+
{ "facets": {}, "state": "idle", "dwellSeconds": 132600, "sampleCount": 38 },
|
|
1201
|
+
{ "facets": {}, "state": "printer-harvest", "dwellSeconds": 21540, "sampleCount": 17 },
|
|
1202
|
+
{ "facets": {}, "state": "printer-service", "dwellSeconds": 9060, "sampleCount": 3 }
|
|
1203
|
+
],
|
|
1204
|
+
"overflow": false
|
|
1205
|
+
}
|
|
1206
|
+
```
|
|
1207
|
+
|
|
1208
|
+
Adding `"facets": ["model"]` to `groupBy` slices the same bands per model value (`p1s` vs `h2s`).
|
|
1209
|
+
|
|
1210
|
+
**Response fields (per group):**
|
|
1211
|
+
|
|
1212
|
+
| Field | Type | Description |
|
|
1213
|
+
|-------|------|-------------|
|
|
1214
|
+
| `role` / `subtype` / `status` | `string` | Present iff requested in `groupBy.columns`. `status` is the row's status NOW — status history is not stored, so a past `asOf` still groups by current status |
|
|
1215
|
+
| `facets` | `object` | One entry per `groupBy.facets` key; `null` when the facet is absent on the underlying rows |
|
|
1216
|
+
| `state` | `string` | Present when `groupBy.state` derived it or a `states[]` entry matched |
|
|
1217
|
+
| `count` | `number` | Membership: entities (with `distinctBy`) or rows (without) |
|
|
1218
|
+
| `dwellSeconds` | `number` | Dwell: summed open-seconds within the window |
|
|
1219
|
+
| `sampleCount` | `number` | Underlying escalation rows contributing to the group (pre-distinct) |
|
|
1220
|
+
|
|
1221
|
+
`overflow: true` means the group cap was hit — more groups exist beyond this page.
|
|
1222
|
+
|
|
1223
|
+
**Response 400** — a caller-input problem: a liveness field (`status`/`available`/`jeopardy`) or `orderBy`/`limit`/`offset` on the filter; `entity` together with `role`/`roles`; `groupBy.state` together with `states[]`; an unknown status in `liveStatuses`; an empty window or one wider than `LT_ANALYTICS_MAX_WINDOW_DAYS`; a future `asOf`; a malformed facet key; a `states[]` match referencing a key the query never groups by.
|
|
1224
|
+
|
|
1225
|
+
**Response 403** — the caller must hold `read_all` on every role in scope (for `entity`, every role in the derived system); a filter with no role scope spans every pond and requires a global principal (superadmin/admin). While `features.publicPaceBoard` stands (default on), counts-only groupings — no `groupBy.facets` keys — are readable by any login, the same data class the Pace Board exposes; a facet-keyed grouping emits facet values (entity ids) as group keys and always takes the full gate.
|
|
1226
|
+
|
|
1227
|
+
## Timeline by facet
|
|
1228
|
+
|
|
1229
|
+
```
|
|
1230
|
+
POST /api/escalations/timeline-by-facet
|
|
1231
|
+
```
|
|
1232
|
+
|
|
1233
|
+
One entity's ordered interval sequence — every escalation the entity facet appeared in, as `[startedAt, endedAt)` spans with durations, in `created_at` order. Open intervals report `endedAt: null`. Gaps between consecutive intervals are untracked time and are preserved, not filled. The facet is matched GIN-served (`metadata @> {key: value}`), so the stored value must be a JSON string.
|
|
1234
|
+
|
|
1235
|
+
**Request body:**
|
|
1236
|
+
|
|
1237
|
+
| Field | Type | Required | Description |
|
|
1238
|
+
|-------|------|----------|-------------|
|
|
1239
|
+
| `facet` | `{ key, value }` | yes | The entity facet to trace |
|
|
1240
|
+
| `query` | `object` | no | Optional extra filter / role scope (or `entity` — the derived system). Same field rules as the aggregate filter |
|
|
1241
|
+
| `window` | `{ from, to }` | no | Only intervals overlapping the window (overlap-filtered, not clipped) |
|
|
1242
|
+
| `select` | `object` | no | `columns` / `facets` to surface per interval (default: all three columns) |
|
|
1243
|
+
| `liveStatuses` | `string[]` | no | Statuses considered live (default `["pending"]`) |
|
|
1244
|
+
| `order` | `string` | no | Interval ordering by start instant — `asc` (default) or `desc`; `desc` + `before` pages a long history recent-first |
|
|
1245
|
+
| `before` | `string` | no | Strict upper bound on `startedAt` (ISO instant) — the "load earlier" cursor |
|
|
1246
|
+
| `limit` | `integer` | no | Max intervals |
|
|
1247
|
+
|
|
1248
|
+
**Example** — one printer's movement across the fleet's queues:
|
|
1249
|
+
|
|
1250
|
+
```json
|
|
1251
|
+
{
|
|
1252
|
+
"facet": { "key": "serialNumber", "value": "PRN-001" },
|
|
1253
|
+
"query": { "entity": "serialNumber" }
|
|
1254
|
+
}
|
|
1255
|
+
```
|
|
1256
|
+
|
|
1257
|
+
**Response 200:**
|
|
1258
|
+
|
|
1259
|
+
```json
|
|
1260
|
+
{
|
|
1261
|
+
"intervals": [
|
|
1262
|
+
{
|
|
1263
|
+
"role": "printer-fleet",
|
|
1264
|
+
"subtype": "printing",
|
|
1265
|
+
"status": "resolved",
|
|
1266
|
+
"facets": {},
|
|
1267
|
+
"startedAt": "2026-08-01T08:00:00.000Z",
|
|
1268
|
+
"endedAt": "2026-08-01T09:30:00.000Z",
|
|
1269
|
+
"durationSeconds": 5400
|
|
1270
|
+
},
|
|
1271
|
+
{
|
|
1272
|
+
"role": "printer-harvest",
|
|
1273
|
+
"subtype": "harvest",
|
|
1274
|
+
"status": "pending",
|
|
1275
|
+
"facets": {},
|
|
1276
|
+
"startedAt": "2026-08-01T09:30:00.000Z",
|
|
1277
|
+
"endedAt": null,
|
|
1278
|
+
"durationSeconds": 1800
|
|
1279
|
+
}
|
|
1280
|
+
],
|
|
1281
|
+
"overflow": false
|
|
1282
|
+
}
|
|
1283
|
+
```
|
|
1284
|
+
|
|
1285
|
+
`durationSeconds` runs to `endedAt`, else to `window.to` clamped at now. `overflow: true` means the interval cap was hit.
|
|
1286
|
+
|
|
1287
|
+
**Response 400** — same validation class as the aggregate: a rejected filter field, an invalid window, a malformed facet key.
|
|
1288
|
+
|
|
1289
|
+
**Response 403** — timelines always take the full gate: an entity's movement history is item-level disclosure. The caller must hold `read_all` on every role in scope (for `entity`, every role in the derived system); a roleless query requires a global principal.
|
package/docs/api/http/roles.md
CHANGED
|
@@ -252,6 +252,8 @@ Returns all roles with metadata and usage counts.
|
|
|
252
252
|
"worker_count": 4,
|
|
253
253
|
"priority_threshold_minutes": null,
|
|
254
254
|
"priority_facet": null,
|
|
255
|
+
"entity_facet": null,
|
|
256
|
+
"entity_state_source": "role",
|
|
255
257
|
"user_count": 5,
|
|
256
258
|
"chain_count": 2,
|
|
257
259
|
"workflow_count": 3
|
|
@@ -275,6 +277,8 @@ Returns all roles with metadata and usage counts.
|
|
|
275
277
|
| `worker_count` | `number \| null` | Capacity at this station — staff or machine count (capacity setting) |
|
|
276
278
|
| `priority_threshold_minutes` | `number \| null` | Max age (minutes) before a pending unclaimed item counts toward `priority_count` in station metrics; falls back to `sla_minutes` |
|
|
277
279
|
| `priority_facet` | `string \| null` | Escalation metadata key holding the age origin as an ISO 8601 UTC timestamp (e.g. `authorized_at`); falls back to `created_at` |
|
|
280
|
+
| `entity_facet` | `string \| null` | Escalation metadata key naming the **entity** that moves through this role (e.g. `serialNumber`, `orderId`). Roles sharing the key form that entity's system; the analytics surfaces (distinct-entity counts, per-entity dwell, entity timelines) derive from it. `null` = the role has no entity notion |
|
|
281
|
+
| `entity_state_source` | `'role' \| 'subtype'` | How the role names its contribution to the entity's state space: `'role'` (the station itself is the state) or `'subtype'` (the role's subtypes are its states). Default `'role'`; only meaningful while `entity_facet` is set |
|
|
278
282
|
| `user_count` | `number` | Number of users assigned this role |
|
|
279
283
|
| `chain_count` | `number` | Number of escalation chain entries referencing this role |
|
|
280
284
|
| `workflow_count` | `number` | Number of workflow configs that reference this role |
|
|
@@ -374,6 +378,8 @@ When the update changes `form_schema` or `metadata_schema`, the new pair is snap
|
|
|
374
378
|
| `worker_count` | `number \| null` | Station capacity |
|
|
375
379
|
| `priority_threshold_minutes` | `number \| null` | Priority age threshold in minutes; falls back to `sla_minutes` |
|
|
376
380
|
| `priority_facet` | `string \| null` | Metadata key for the priority age origin (letters, numbers, underscores); falls back to `created_at` |
|
|
381
|
+
| `entity_facet` | `string \| null` | Metadata key naming the entity that moves through this role (letters, numbers, underscores). Roles sharing the key form the entity's system for the analytics surfaces. `null` clears |
|
|
382
|
+
| `entity_state_source` | `'role' \| 'subtype' \| null` | How the role names the entity's states — the station itself (`'role'`) or its subtypes (`'subtype'`). `null` resets to the `'role'` default |
|
|
377
383
|
| `upstream_roles` | `string[] \| null` | Replace the set of roles this station draws input from across other Operations sequences (omitted = preserve; `null` or `[]` = clear). Every entry must name an existing role other than this one. Distinct from `parent_role`, which places the role in its own sequence. |
|
|
378
384
|
| `list_schema` | `object \| null` | Rich formatting (x-lt-* markup) for this role's escalation LIST page. Versions independently of `form_schema`; the list always renders the latest |
|
|
379
385
|
| `default_pins` | `array \| null` | Pinned-view seeds for members: `[{ label, url, badge? }]` with dashboard-relative `url`s. Members promote, hide, or reorder them via their own preferences (`/api/me/preferences`). `null` clears |
|
package/docs/api/mcp/admin.md
CHANGED
|
@@ -377,6 +377,59 @@ Update the priority of multiple escalations.
|
|
|
377
377
|
| ids | string[] | Yes | Escalation IDs |
|
|
378
378
|
| priority | integer | Yes | New priority value |
|
|
379
379
|
|
|
380
|
+
### aggregate_by_facets
|
|
381
|
+
|
|
382
|
+
Grouped analytics over the escalation intervals. Every escalation is one open
|
|
383
|
+
interval `[created_at, ended_at)`; this tool reads it two ways: **membership**
|
|
384
|
+
(rows or, with `distinctBy`, distinct entities open at an instant — a past
|
|
385
|
+
`asOf` reconstructs the live set then) and **dwell** (open-seconds per group
|
|
386
|
+
within a half-open `[from, to)` window). Group by role/subtype/status columns
|
|
387
|
+
plus metadata facet keys; optional `states[]` label each group. One call
|
|
388
|
+
replaces N per-filter count round-trips. Mirrors
|
|
389
|
+
`POST /api/escalations/aggregate-by-facets`.
|
|
390
|
+
|
|
391
|
+
| | |
|
|
392
|
+
|---|---|
|
|
393
|
+
| Read-safe | Yes |
|
|
394
|
+
|
|
395
|
+
**Parameters:**
|
|
396
|
+
|
|
397
|
+
| Field | Type | Required | Description |
|
|
398
|
+
|-------|------|----------|-------------|
|
|
399
|
+
| query | object | Yes | The filter (WHAT only — no status/available/jeopardy): `role`/`roles` or `entity` (the entity facet key, resolved to every role declaring it — the entity's system), plus `facets`, `block`, `range`, `exists` |
|
|
400
|
+
| groupBy | object | Yes | Group keys: `columns` (`role`, `subtype`, `status`), `facets` (metadata keys), `state` (derived state label per each role's `entity_state_source`; mutually exclusive with `states`). Empty object → one total row |
|
|
401
|
+
| measure | object | Yes | `{ kind: "membership", asOf? }` or `{ kind: "dwell", window: { from, to } }` |
|
|
402
|
+
| distinctBy | string | No | Membership only: count DISTINCT of this metadata facet (entities, not rows) |
|
|
403
|
+
| states | array | No | Pure labeling: tag each group with the FIRST matching state name |
|
|
404
|
+
| liveStatuses | string[] | No | Statuses considered live (default `["pending"]`) |
|
|
405
|
+
| orderBy | array | No | Order the RESULT groups: `{ field, direction? }` |
|
|
406
|
+
| limit | integer | No | Max result groups (server-capped; `overflow` flag when more exist) |
|
|
407
|
+
| offset | integer | No | Result-group offset |
|
|
408
|
+
|
|
409
|
+
### timeline_by_facet
|
|
410
|
+
|
|
411
|
+
One entity's ordered interval sequence — every escalation the entity facet
|
|
412
|
+
(e.g. a machine id) appeared in, as `[startedAt, endedAt)` spans with durations,
|
|
413
|
+
in `created_at` order. Open intervals report `endedAt: null`. Gaps between
|
|
414
|
+
consecutive intervals are untracked time and are preserved, not filled. The
|
|
415
|
+
entity facet value must be stored as a JSON string (GIN containment match).
|
|
416
|
+
Mirrors `POST /api/escalations/timeline-by-facet`.
|
|
417
|
+
|
|
418
|
+
| | |
|
|
419
|
+
|---|---|
|
|
420
|
+
| Read-safe | Yes |
|
|
421
|
+
|
|
422
|
+
**Parameters:**
|
|
423
|
+
|
|
424
|
+
| Field | Type | Required | Description |
|
|
425
|
+
|-------|------|----------|-------------|
|
|
426
|
+
| facet | object | Yes | `{ key, value }` — the entity facet to trace |
|
|
427
|
+
| query | object | No | Optional extra filter / role scope (or `entity` — the derived system) |
|
|
428
|
+
| window | object | No | `{ from, to }` — only intervals overlapping this window (overlap-filtered, not clipped) |
|
|
429
|
+
| select | object | No | `columns` / `facets` to surface per interval (default: all three columns) |
|
|
430
|
+
| liveStatuses | string[] | No | Statuses considered live (default `["pending"]`) |
|
|
431
|
+
| limit | integer | No | Max intervals |
|
|
432
|
+
|
|
380
433
|
## Workflow Configuration
|
|
381
434
|
|
|
382
435
|
### list_workflow_configs
|
|
@@ -915,6 +915,64 @@ const result = await lt.escalations.claimByFacets({
|
|
|
915
915
|
|
|
916
916
|
**Returns:** `LTApiResult<{ claimed }>`.
|
|
917
917
|
|
|
918
|
+
## aggregateByFacets
|
|
919
|
+
|
|
920
|
+
Grouped analytics over the escalation intervals — every escalation is one open interval `[created_at, ended_at)`, `ended_at` `NULL` while live. Two measures: **membership** (rows or, with `distinctBy`, distinct entities open at an instant; a past `asOf` reconstructs the live set then) and **dwell** (open-seconds per group within a half-open `[from, to)` window, clipped to it). `query.entity` names an entity facet key and scopes to every role declaring it as its `entity_facet` — the entity's system (mutually exclusive with `role`/`roles`); `groupBy.state` groups by the derived state label, each role contributing per its `entity_state_source`.
|
|
921
|
+
|
|
922
|
+
```typescript
|
|
923
|
+
// The printer fleet: how the entities spent the window, one row per state
|
|
924
|
+
// (idle, printing, printer-harvest, printer-service)
|
|
925
|
+
const result = await lt.escalations.aggregateByFacets({
|
|
926
|
+
query: { entity: 'serialNumber' },
|
|
927
|
+
groupBy: { state: true },
|
|
928
|
+
measure: { kind: 'dwell', window: { from: '2026-08-01T00:00:00Z', to: '2026-08-02T00:00:00Z' } },
|
|
929
|
+
});
|
|
930
|
+
|
|
931
|
+
// The slice: the same bands per model value (p1s vs h2s)
|
|
932
|
+
await lt.escalations.aggregateByFacets({
|
|
933
|
+
query: { entity: 'serialNumber' },
|
|
934
|
+
groupBy: { state: true, facets: ['model'] },
|
|
935
|
+
measure: { kind: 'dwell', window: { from: '2026-08-01T00:00:00Z', to: '2026-08-02T00:00:00Z' } },
|
|
936
|
+
});
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
| Field | Type | Required | Description |
|
|
940
|
+
|-------|------|----------|-------------|
|
|
941
|
+
| `query` | `AnalyticsQuery` | Yes | The filter — `role`/`roles` or `entity`, plus `facets`, `anyOf` (rows carrying ANY of the given facet sets, max 200), `block`, `range`, `exists`, and `prefix` (case-insensitive prefix match on facet values — the locate affordance). `status`/`available`/`jeopardy` and query-level paging are rejected with 400: liveness derives from the interval, `liveStatuses`, and the measure anchor |
|
|
942
|
+
| `groupBy` | `FacetGroupBy` | Yes | `columns` (`role`, `subtype`, `status`), `facets` (metadata keys), `state` (derived state label; mutually exclusive with `states[]`). Empty object → one total row |
|
|
943
|
+
| `measure` | `AggregateMeasure` | Yes | `{ kind: 'membership', asOf? }` or `{ kind: 'dwell', window }` |
|
|
944
|
+
| `distinctBy` | `string` | No | Membership only — count DISTINCT of this facet (entities, not rows) |
|
|
945
|
+
| `states` | `Array<{ name, match }>` | No | Pure labeling: tag each group with the first matching state name |
|
|
946
|
+
| `liveStatuses` | `string[]` | No | Statuses considered live (default `['pending']`) |
|
|
947
|
+
| `orderBy` / `limit` / `offset` | — | No | Order and page the RESULT groups (capped at `LT_ANALYTICS_MAX_GROUPS`) |
|
|
948
|
+
|
|
949
|
+
**Returns:** `LTApiResult<{ groups: AggregateRow[]; overflow: boolean }>` — each group carries the grouped keys, `count` or `dwellSeconds`, and `sampleCount`. Gate: `read_all` on every role in scope (entity-derived roles included); a roleless filter requires a global principal; counts-only groupings (no `groupBy.facets`) are open to any login while `features.publicPaceBoard` stands. Mirrors `POST /api/escalations/aggregate-by-facets`.
|
|
950
|
+
|
|
951
|
+
## timelineByFacet
|
|
952
|
+
|
|
953
|
+
One entity's ordered interval sequence — every escalation the facet appeared in, as `[startedAt, endedAt)` spans with durations, in `created_at` order. Open intervals report `endedAt: null`; gaps are untracked time, preserved. GIN-served containment match — the stored facet value must be a JSON string. Always takes the full read gate (an entity's movement history is item-level disclosure).
|
|
954
|
+
|
|
955
|
+
```typescript
|
|
956
|
+
// One printer's movement across the fleet's queues
|
|
957
|
+
const result = await lt.escalations.timelineByFacet({
|
|
958
|
+
facet: { key: 'serialNumber', value: 'PRN-001' },
|
|
959
|
+
query: { entity: 'serialNumber' },
|
|
960
|
+
});
|
|
961
|
+
```
|
|
962
|
+
|
|
963
|
+
| Field | Type | Required | Description |
|
|
964
|
+
|-------|------|----------|-------------|
|
|
965
|
+
| `facet` | `{ key, value }` | Yes | The entity facet to trace |
|
|
966
|
+
| `query` | `AnalyticsQuery` | No | Optional extra filter / role scope (or `entity` — the derived system) |
|
|
967
|
+
| `window` | `{ from, to }` | No | Only intervals overlapping the window (overlap-filtered, not clipped) |
|
|
968
|
+
| `select` | `{ columns?, facets? }` | No | Columns/facets to surface per interval (default: all three columns) |
|
|
969
|
+
| `liveStatuses` | `string[]` | No | Statuses considered live (default `['pending']`) |
|
|
970
|
+
| `order` | `'asc' \| 'desc'` | No | Interval ordering by start instant (default `asc`); `desc` + `before` pages a long history recent-first |
|
|
971
|
+
| `before` | `Date \| string` | No | Strict upper bound on `startedAt` — the "load earlier" cursor |
|
|
972
|
+
| `limit` | `integer` | No | Max intervals |
|
|
973
|
+
|
|
974
|
+
**Returns:** `LTApiResult<{ intervals: TimelineInterval[]; overflow: boolean }>`. Mirrors `POST /api/escalations/timeline-by-facet`.
|
|
975
|
+
|
|
918
976
|
---
|
|
919
977
|
|
|
920
978
|
## getStationMetrics
|
package/docs/cli.md
CHANGED
|
@@ -78,8 +78,19 @@ ltc esc claim-by-meta orderId order-123 # Claim by metadata key
|
|
|
78
78
|
ltc esc claim-by-meta orderId order-123 --assignee ext-user-42 # On behalf of user
|
|
79
79
|
ltc esc resolve-by-meta orderId order-123 --data '{"approved": true}'
|
|
80
80
|
ltc esc resolve-by-meta orderId order-123 --data '{"approved": true}' --assignee ext-user-42
|
|
81
|
+
|
|
82
|
+
# Analytics: grouped membership/dwell aggregates and per-entity timelines
|
|
83
|
+
# The fleet: how the entities spent the window, one row per state
|
|
84
|
+
ltc esc aggregate-facets --entity serialNumber --group-state \
|
|
85
|
+
--window '{"from":"2026-08-01T00:00:00Z","to":"2026-08-02T00:00:00Z"}'
|
|
86
|
+
# The individual: one printer's ordered interval timeline
|
|
87
|
+
ltc esc timeline serialNumber PRN-001 --entity serialNumber
|
|
81
88
|
```
|
|
82
89
|
|
|
90
|
+
`aggregate-facets` runs grouped analytics over the escalation intervals — membership at an instant (`--as-of`, default now), or dwell over a window (`--window` switches the measure). Scope with `--role`, `--roles`, or `--entity <key>` (the entity facet key — scopes to every role declaring it, the entity's system; no role at all spans every pond, global principals only). Group with `--group-columns role,subtype,status`, `--group-facets <list>`, or `--group-state` (the derived state label — each role's subtypes or itself). Refine with `--facets`/`--exists`, `--distinct-by <key>` (count entities, not rows; membership only), `--live-statuses`, and page the result groups with `--order-by`/`--limit`/`--offset`.
|
|
91
|
+
|
|
92
|
+
`timeline <key> <value>` prints one entity's ordered escalation-interval timeline (gaps = untracked time). `--entity` scopes to the entity's system (every role declaring that entity facet); `--roles` restricts to specific roles; a call with neither spans every pond and requires a global principal. `--from`/`--to` keep only overlapping intervals; `--select-facets` surfaces metadata keys per interval.
|
|
93
|
+
|
|
83
94
|
### Workflows
|
|
84
95
|
|
|
85
96
|
```bash
|
package/docs/dashboard.md
CHANGED
|
@@ -56,7 +56,7 @@ The LLM authoring add-on. Appears when an Anthropic key is configured.
|
|
|
56
56
|
| Page | Route | Purpose |
|
|
57
57
|
|------|-------|---------|
|
|
58
58
|
| **Accounts** | `/admin/users` | User accounts and service accounts (bots). Create, edit, assign roles, manage API keys. |
|
|
59
|
-
| **Roles** | `/admin/roles` | Define roles — the queues, forms, and membership that connect workflows to people.
|
|
59
|
+
| **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
60
|
| **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. |
|
|
61
61
|
|
|
62
62
|
### Infrastructure
|
|
@@ -223,41 +223,43 @@ Because roles carry the queue, the form schema, the membership, and the capacity
|
|
|
223
223
|
|
|
224
224
|
**Master list** (`/admin/roles`) — one row per role:
|
|
225
225
|
|
|
226
|
-
- **
|
|
227
|
-
- **
|
|
226
|
+
- **Role** — an ops status dot (green marks roles that appear as stations on the [Pace Board](#pace-board)) beside the display name: the user-set title, else Title Case derived from the key.
|
|
227
|
+
- **Key** — the exact technical role id.
|
|
228
|
+
- **Description** — the role's human-facing summary.
|
|
228
229
|
- **Preceded By** — the role's prior step (`parent_role`), linked. Prior steps compose the Pace Board's sequences.
|
|
229
|
-
- **
|
|
230
|
-
- **Member Count** — how many accounts hold the role.
|
|
230
|
+
- **Members** — how many accounts hold the role.
|
|
231
231
|
- **Capacity** — SLA (minutes), Target (per hour), and Staff side by side. These drive the Pace Board's computed metrics.
|
|
232
232
|
|
|
233
233
|
Search filters by key, label, or description. **+ Add Role** creates a role here; roles referenced in workflow configs are also auto-created at startup. Click any row to open [Role Detail](#role-detail).
|
|
234
234
|
|
|
235
|
-
**API:** `GET /api/roles` lists roles. `POST /api/roles` creates. `GET /api/roles/details` returns full `RoleDetail` shapes.
|
|
235
|
+
**API:** `GET /api/roles` lists roles. `POST /api/roles` creates. `GET /api/roles/details` returns full `RoleDetail` shapes.
|
|
236
236
|
|
|
237
237
|
### Role Detail
|
|
238
238
|
|
|
239
|
-
Accessible at `/admin/roles/:role`. One page per role — a header
|
|
239
|
+
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
240
|
|
|
241
|
-
**
|
|
241
|
+
**Identity** — display name and description, shown on role rows and station labels. The danger zone lives here too: a role nothing references can be deleted.
|
|
242
242
|
|
|
243
|
-
**
|
|
243
|
+
**Pace Board** — everything the board consumes about this role, in one column:
|
|
244
244
|
|
|
245
|
-
|
|
245
|
+
- **Station** — the toggle that shows the role as a station on the [Pace Board](#pace-board).
|
|
246
|
+
- **Capacity** — `sla_minutes`, `target_per_hour`, and `worker_count`; enter any two and the section hints the derived third (`throughput = workers / (sla / 60)`).
|
|
247
|
+
- **Priority** — `priority_threshold_minutes` and the age facet (`priority_facet`) driving the Pace Board priority count and the jeopardy filter (blank = `sla_minutes` and `created_at`). The dials stay editable while the station is hidden — they also drive jeopardy in the queues.
|
|
248
|
+
- **Entity** — the metadata key naming what moves through this station (`entity_facet`, e.g. `serialNumber`, `orderId`). Roles sharing a key form that entity's system: the [entity lens](operations.md#entity-lens), per-entity dwell, and timelines all derive from it. Once a key is set, the **States from** picker (`entity_state_source`) chooses how the station names the entity's state — **Station** (being here is one state, e.g. a servicing queue) or **Subtypes** (this one role holds several states named by each escalation's subtype, e.g. a fleet role parking `idle` / `printing`).
|
|
249
|
+
- **Sequence** — board geometry, shown while the station is visible: **Prior Step** (places the role in one Pace Board sequence; a role with no prior step starts its own), **Upstream Inputs** (the roles this station also draws from in other sequences — mixin-like, many allowed, saved live, rendered as a merge glyph on the station, never a bend in the line), and **Home Segment** (lead the home Pace Board with this role's sequence; one role holds this).
|
|
246
250
|
|
|
247
|
-
|
|
248
|
-
- **Upstream Inputs** — the roles this station also draws from in other sequences. Mixin-like, many allowed, saved live. Rendered on the Pace Board as a merge glyph on the station, never a bend in the line: an upstream is an input, not a descendant.
|
|
251
|
+
**Schemas** — the role's contracts:
|
|
249
252
|
|
|
250
|
-
|
|
253
|
+
- **Enforcement** — the `enforce_schema` toggle: validate every resolve payload server-side against the role's form schema.
|
|
254
|
+
- **Escalation Schema** — shows the schema version currently in use and links to the [Escalation Schema](#escalation-schema) editor page.
|
|
255
|
+
- **Escalations List Schema** — shows the list-schema version in use and links to the [Escalations List Schema](#escalations-list-schema) editor page.
|
|
256
|
+
- **Metadata Schema** — JSON Schema that validates each escalation's `metadata` at creation time. Its keys appear in faceted search autocomplete.
|
|
251
257
|
|
|
252
258
|
**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.
|
|
253
259
|
|
|
254
|
-
**
|
|
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. Live-save.
|
|
255
261
|
|
|
256
|
-
**
|
|
257
|
-
|
|
258
|
-
**Properties** — a free JSON bag for custom per-role values.
|
|
259
|
-
|
|
260
|
-
**API:** `PATCH /api/roles/:role` updates identity, capacity, placement, and schemas. `POST /api/roles/escalation-chains` adds a chain target; `DELETE /api/roles/escalation-chains` removes one.
|
|
262
|
+
**API:** `PATCH /api/roles/:role` updates identity, Pace Board dials, placement, schemas, and pins.
|
|
261
263
|
|
|
262
264
|
### Escalation Schema
|
|
263
265
|
|
|
@@ -334,10 +336,10 @@ The central queue for all escalation activity across every workflow.
|
|
|
334
336
|
- **Title = queue selector** — the page title reads as the selected role's friendly title, or "All Escalations" when unfiltered; clicking it switches queues over the same `?role=` param the filter bar mirrors. The **My Escalations** personal inbox (`/escalations/queue`) carries the identical control, titling itself "My Escalations" or the filtered role's title.
|
|
335
337
|
- **Filter bar** — filter by status (pending/claimed/resolved), role, workflow type, priority, and time window.
|
|
336
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.
|
|
337
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.
|
|
338
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.
|
|
339
342
|
- **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.
|
|
340
|
-
- **Escalate** — forward a claimed escalation to a higher-tier role via the escalation chain.
|
|
341
343
|
- **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.
|
|
342
344
|
|
|
343
345
|
**API:** `GET /api/escalations` lists with filters. `POST /api/escalations/:id/claim` claims. `POST /api/escalations/:id/resolve` resolves.
|
package/docs/data.md
CHANGED
|
@@ -106,9 +106,9 @@ Updated when claimed or resolved.
|
|
|
106
106
|
|
|
107
107
|
The storage is the **shared HotMesh table `public.hmsh_escalations`**
|
|
108
108
|
(written by both long-tail and the HotMesh SDK). `lt_escalations` remains as a
|
|
109
|
-
backward-compatible **view** over it — `SELECT *` plus
|
|
110
|
-
|
|
111
|
-
managed by the SDK on `hmsh_escalations` (see below).
|
|
109
|
+
backward-compatible **view** over it — `SELECT *` plus computed `available` and
|
|
110
|
+
`ended_at` columns — so existing read queries and the public API are unchanged.
|
|
111
|
+
Indexes are managed by the SDK on `hmsh_escalations` (see below).
|
|
112
112
|
|
|
113
113
|
Role read/write scope does **not** add columns here. An escalation carries a `role`
|
|
114
114
|
and an optional `assigned_to`; work-surface scope lives on the membership table
|
|
@@ -159,11 +159,12 @@ contract is stable.
|
|
|
159
159
|
| `created_at` | `TIMESTAMPTZ NOT NULL` | `NOW()` | Row creation time |
|
|
160
160
|
| `updated_at` | `TIMESTAMPTZ NOT NULL` | `NOW()` | Last modification |
|
|
161
161
|
|
|
162
|
-
The `lt_escalations` view adds
|
|
162
|
+
The `lt_escalations` view adds two computed columns on top of the above:
|
|
163
163
|
|
|
164
164
|
| Column | Type | Description |
|
|
165
165
|
|--------|------|-------------|
|
|
166
166
|
| `available` | `BOOLEAN` | `true` when claimable — `assigned_to IS NULL OR assigned_until IS NULL OR assigned_until <= NOW()` |
|
|
167
|
+
| `ended_at` | `TIMESTAMPTZ` | The instant the row left the live set — `NULL` while `pending`, `COALESCE(resolved_at, updated_at)` once terminal. Resolve stamps `resolved_at` in its guarded transition; cancel and expire stamp only `updated_at` in theirs, and no long-tail write path touches a terminal row afterward, so the coalesce is the transition instant for all three terminal statuses. Every escalation is thereby one open interval `[created_at, ended_at)` — the time-series the analytics surfaces read |
|
|
167
168
|
|
|
168
169
|
**Claiming is implicit.** There is no separate status for "claimed". An escalation is considered claimed when `assigned_to IS NOT NULL` and `assigned_until > NOW()`. When the claim expires, the escalation is available again without any status change. The `/available` endpoint uses this logic:
|
|
169
170
|
|
|
@@ -187,6 +188,13 @@ WHERE status = 'pending'
|
|
|
187
188
|
| `idx_hmsh_esc_task` | `(task_id) WHERE task_id IS NOT NULL` | Join escalations to their parent task |
|
|
188
189
|
| `idx_hmsh_esc_workflow` | `(workflow_id) WHERE workflow_id IS NOT NULL` | Look up escalation by workflow ID |
|
|
189
190
|
|
|
191
|
+
**Indexes** (app-managed, built at startup with `CREATE INDEX CONCURRENTLY`):
|
|
192
|
+
|
|
193
|
+
| Index | Columns | Purpose |
|
|
194
|
+
|-------|---------|---------|
|
|
195
|
+
| `idx_hmsh_esc_resolved_cover` | `(role, resolved_at DESC, claimed_at, created_at) WHERE status = 'resolved'` | Station-metrics percentile pass — bounds each station's scan to its resolved window and feeds the wait/work percentiles from the index |
|
|
196
|
+
| `idx_hmsh_esc_ended_at` | `((COALESCE(resolved_at, updated_at))) WHERE status <> 'pending'` | The analytics interval scans — dwell and past-membership need terminal rows whose end instant falls after the window/`asOf`. The predicate excludes `pending` inserts, so each row costs exactly one index write, at its terminal transition |
|
|
197
|
+
|
|
190
198
|
### lt_users
|
|
191
199
|
|
|
192
200
|
User and bot identity records. Users are created via the API and assigned roles that determine which escalations they can claim. Bot accounts (`account_type = 'bot'`) are service identities that authenticate with API keys.
|
|
@@ -24,9 +24,11 @@ swatches, and overlay scrims.
|
|
|
24
24
|
|
|
25
25
|
Chroma is spent, not spread.
|
|
26
26
|
|
|
27
|
-
- **Surfaces are near-neutral.** Hover fills
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
- **Surfaces are near-neutral; lines carry the hue.** Hover fills and section
|
|
28
|
+
bands stay a whisper off-neutral, but the thin structure — borders,
|
|
29
|
+
dividers, outlines, field rules — carries a clear, light tint of the accent
|
|
30
|
+
so the frame reads as thematic. Chroma never lands on a large rectangle — a
|
|
31
|
+
colored slab makes every real accent invisible — but a hairline may glow.
|
|
30
32
|
- **One elevation logic, both polarities.** The page carries the base wash
|
|
31
33
|
(a light theme tint by day, the deep shade at night); sections and sheets
|
|
32
34
|
are distinct surfaces on it (white paper by day, the deeper well at
|
|
@@ -64,10 +66,10 @@ Chroma is spent, not spread.
|
|
|
64
66
|
|
|
65
67
|
## 4. Fields and controls
|
|
66
68
|
|
|
67
|
-
- **One recipe** (`.field/.input/.select/.textarea`): white body, light
|
|
68
|
-
|
|
69
|
-
and fill takes the theme tint on focus. Every field-like control uses
|
|
70
|
-
recipe or the tokens — no bespoke field styling.
|
|
69
|
+
- **One recipe** (`.field/.input/.select/.textarea`): white body, a light
|
|
70
|
+
accent-tinted border, soft radius (`--lt-radius-field`), border deepens to
|
|
71
|
+
accent and fill takes the theme tint on focus. Every field-like control uses
|
|
72
|
+
the recipe or the tokens — no bespoke field styling.
|
|
71
73
|
- **Width follows content, capped by the measure:**
|
|
72
74
|
- Generated forms hold a readable measure (`max-w-form`, 56rem). A wide
|
|
73
75
|
monitor gets margin, never a 2000px input.
|