@hotmeshio/long-tail 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. package/build/api/escalations/create.js +30 -9
  2. package/build/api/roles.d.ts +32 -0
  3. package/build/api/roles.js +73 -0
  4. package/build/bin/ltc.js +23 -0
  5. package/build/lib/cli/commands/roles.d.ts +17 -0
  6. package/build/lib/cli/commands/roles.js +102 -0
  7. package/build/lib/db/schemas/017_role_schema_versions.sql +40 -0
  8. package/build/lib/db/schemas/018_role_upstreams.sql +21 -0
  9. package/build/routes/roles.js +20 -0
  10. package/build/scripts/ortho-populate.d.ts +80 -8
  11. package/build/scripts/ortho-populate.js +204 -52
  12. package/build/sdk/index.d.ts +2 -0
  13. package/build/sdk/index.js +2 -0
  14. package/build/services/interceptor/completion.js +17 -12
  15. package/build/services/orchestrator/condition.d.ts +25 -1
  16. package/build/services/orchestrator/condition.js +29 -1
  17. package/build/services/role/index.d.ts +15 -1
  18. package/build/services/role/index.js +45 -1
  19. package/build/services/role/sql.d.ts +23 -2
  20. package/build/services/role/sql.js +93 -16
  21. package/build/services/role/types.d.ts +41 -0
  22. package/build/system/mcp-servers/admin/index.js +3 -3
  23. package/build/system/mcp-servers/admin/schemas.d.ts +25 -2
  24. package/build/system/mcp-servers/admin/schemas.js +11 -2
  25. package/build/system/mcp-servers/admin/users.js +42 -0
  26. package/build/system/mcp-servers/human-queue-schemas.d.ts +6 -0
  27. package/build/system/mcp-servers/human-queue-schemas.js +4 -0
  28. package/build/system/mcp-servers/human-queue.js +10 -2
  29. package/build/system/seed/tool-manifests-admin.d.ts +371 -0
  30. package/build/system/seed/tool-manifests-admin.js +3 -1
  31. package/build/tsconfig.tsbuildinfo +1 -1
  32. package/build/types/escalation.d.ts +16 -0
  33. package/build/types/escalation.js +17 -0
  34. package/dashboard/dist/assets/{AdminDashboard-Bg1HgukI.js → AdminDashboard-rZDVS2G_.js} +2 -2
  35. package/dashboard/dist/assets/{AdminDashboard-Bg1HgukI.js.map → AdminDashboard-rZDVS2G_.js.map} +1 -1
  36. package/dashboard/dist/assets/{AgentConfigPage-HBe_YDU_.js → AgentConfigPage-CbyyCjCT.js} +2 -2
  37. package/dashboard/dist/assets/{AgentConfigPage-HBe_YDU_.js.map → AgentConfigPage-CbyyCjCT.js.map} +1 -1
  38. package/dashboard/dist/assets/{AgentDetailPage-BsaiYbwL.js → AgentDetailPage-DMeHvidT.js} +2 -2
  39. package/dashboard/dist/assets/{AgentDetailPage-BsaiYbwL.js.map → AgentDetailPage-DMeHvidT.js.map} +1 -1
  40. package/dashboard/dist/assets/{AgentsPage-DhN2lIBh.js → AgentsPage-C8fKLxh-.js} +2 -2
  41. package/dashboard/dist/assets/{AgentsPage-DhN2lIBh.js.map → AgentsPage-C8fKLxh-.js.map} +1 -1
  42. package/dashboard/dist/assets/{AvailableEscalationsPage-_xcWvlIz.js → AvailableEscalationsPage-CUHcybKU.js} +2 -2
  43. package/dashboard/dist/assets/{AvailableEscalationsPage-_xcWvlIz.js.map → AvailableEscalationsPage-CUHcybKU.js.map} +1 -1
  44. package/dashboard/dist/assets/{BotPicker-DY6iUVC9.js → BotPicker-DGhniNcy.js} +2 -2
  45. package/dashboard/dist/assets/{BotPicker-DY6iUVC9.js.map → BotPicker-DGhniNcy.js.map} +1 -1
  46. package/dashboard/dist/assets/{CapabilitiesPage-w1owqsmN.js → CapabilitiesPage-ClfuW4ya.js} +2 -2
  47. package/dashboard/dist/assets/{CapabilitiesPage-w1owqsmN.js.map → CapabilitiesPage-ClfuW4ya.js.map} +1 -1
  48. package/dashboard/dist/assets/{CollapsibleSection-DIMGjlJ-.js → CollapsibleSection-DnECdKEY.js} +2 -2
  49. package/dashboard/dist/assets/{CollapsibleSection-DIMGjlJ-.js.map → CollapsibleSection-DnECdKEY.js.map} +1 -1
  50. package/dashboard/dist/assets/{CredentialsPage-D1OmMHux.js → CredentialsPage-DdZ-jyyc.js} +2 -2
  51. package/dashboard/dist/assets/{CredentialsPage-D1OmMHux.js.map → CredentialsPage-DdZ-jyyc.js.map} +1 -1
  52. package/dashboard/dist/assets/{CronLabel-CQy3DrUQ.js → CronLabel-CymxLvY0.js} +2 -2
  53. package/dashboard/dist/assets/{CronLabel-CQy3DrUQ.js.map → CronLabel-CymxLvY0.js.map} +1 -1
  54. package/dashboard/dist/assets/{CustomDurationPicker-BoBkl9tw.js → CustomDurationPicker-8JHvVWD2.js} +2 -2
  55. package/dashboard/dist/assets/{CustomDurationPicker-BoBkl9tw.js.map → CustomDurationPicker-8JHvVWD2.js.map} +1 -1
  56. package/dashboard/dist/assets/{DropZone-QXbiuU0b.js → DropZone-DEX0BBqt.js} +2 -2
  57. package/dashboard/dist/assets/{DropZone-QXbiuU0b.js.map → DropZone-DEX0BBqt.js.map} +1 -1
  58. package/dashboard/dist/assets/{ElapsedCell-Donjm9gT.js → ElapsedCell-BRZfw_Af.js} +2 -2
  59. package/dashboard/dist/assets/{ElapsedCell-Donjm9gT.js.map → ElapsedCell-BRZfw_Af.js.map} +1 -1
  60. package/dashboard/dist/assets/EscalationSchemaPage-Do2iPKUl.js +9 -0
  61. package/dashboard/dist/assets/EscalationSchemaPage-Do2iPKUl.js.map +1 -0
  62. package/dashboard/dist/assets/{EscalationsOverview-CvHIJ_GG.js → EscalationsOverview-CiQO3ntf.js} +2 -2
  63. package/dashboard/dist/assets/{EscalationsOverview-CvHIJ_GG.js.map → EscalationsOverview-CiQO3ntf.js.map} +1 -1
  64. package/dashboard/dist/assets/{EventTable-DpAXMmT3.js → EventTable-CLuAbba_.js} +2 -2
  65. package/dashboard/dist/assets/{EventTable-DpAXMmT3.js.map → EventTable-CLuAbba_.js.map} +1 -1
  66. package/dashboard/dist/assets/{GraphInvokePage-De1XVx5d.js → GraphInvokePage-BFRi6Nny.js} +2 -2
  67. package/dashboard/dist/assets/{GraphInvokePage-De1XVx5d.js.map → GraphInvokePage-BFRi6Nny.js.map} +1 -1
  68. package/dashboard/dist/assets/{HomePage-BYR5VXXw.js → HomePage-m1pnxilb.js} +2 -2
  69. package/dashboard/dist/assets/{HomePage-BYR5VXXw.js.map → HomePage-m1pnxilb.js.map} +1 -1
  70. package/dashboard/dist/assets/{ListToolbar-Bx7jGzPA.js → ListToolbar-D4Y-M7wN.js} +2 -2
  71. package/dashboard/dist/assets/{ListToolbar-Bx7jGzPA.js.map → ListToolbar-D4Y-M7wN.js.map} +1 -1
  72. package/dashboard/dist/assets/{McpOverview-DXRKcJN0.js → McpOverview-mfJpETsW.js} +2 -2
  73. package/dashboard/dist/assets/{McpOverview-DXRKcJN0.js.map → McpOverview-mfJpETsW.js.map} +1 -1
  74. package/dashboard/dist/assets/{McpQueryDetailPage-CmW15w5e.js → McpQueryDetailPage-Cb99vI7v.js} +2 -2
  75. package/dashboard/dist/assets/{McpQueryDetailPage-CmW15w5e.js.map → McpQueryDetailPage-Cb99vI7v.js.map} +1 -1
  76. package/dashboard/dist/assets/{McpQueryPage-BE6I011z.js → McpQueryPage-CKKOxks0.js} +2 -2
  77. package/dashboard/dist/assets/{McpQueryPage-BE6I011z.js.map → McpQueryPage-CKKOxks0.js.map} +1 -1
  78. package/dashboard/dist/assets/{McpRunDetailPage-Dsa3G3XQ.js → McpRunDetailPage-CyTbHs0J.js} +2 -2
  79. package/dashboard/dist/assets/{McpRunDetailPage-Dsa3G3XQ.js.map → McpRunDetailPage-CyTbHs0J.js.map} +1 -1
  80. package/dashboard/dist/assets/{McpRunsPage-uTbym66Y.js → McpRunsPage-MIwmK-Ky.js} +2 -2
  81. package/dashboard/dist/assets/{McpRunsPage-uTbym66Y.js.map → McpRunsPage-MIwmK-Ky.js.map} +1 -1
  82. package/dashboard/dist/assets/{NamespacePill-BGf0jHwE.js → NamespacePill-CwKdoyRM.js} +2 -2
  83. package/dashboard/dist/assets/{NamespacePill-BGf0jHwE.js.map → NamespacePill-CwKdoyRM.js.map} +1 -1
  84. package/dashboard/dist/assets/OperationsPage-DPesph0U.js +2 -0
  85. package/dashboard/dist/assets/OperationsPage-DPesph0U.js.map +1 -0
  86. package/dashboard/dist/assets/{OperatorDashboard-i8cxaDlk.js → OperatorDashboard-K1gzULr7.js} +2 -2
  87. package/dashboard/dist/assets/{OperatorDashboard-i8cxaDlk.js.map → OperatorDashboard-K1gzULr7.js.map} +1 -1
  88. package/dashboard/dist/assets/{PageHeader-BZJsEVOC.js → PageHeader-BhaWTWcM.js} +2 -2
  89. package/dashboard/dist/assets/{PageHeader-BZJsEVOC.js.map → PageHeader-BhaWTWcM.js.map} +1 -1
  90. package/dashboard/dist/assets/{PageHeaderWithStats-BWuD7s9c.js → PageHeaderWithStats-yex5WmHB.js} +2 -2
  91. package/dashboard/dist/assets/{PageHeaderWithStats-BWuD7s9c.js.map → PageHeaderWithStats-yex5WmHB.js.map} +1 -1
  92. package/dashboard/dist/assets/{ProcessDetailPage-CP7GZhgs.js → ProcessDetailPage-DBIrY6Vd.js} +2 -2
  93. package/dashboard/dist/assets/{ProcessDetailPage-CP7GZhgs.js.map → ProcessDetailPage-DBIrY6Vd.js.map} +1 -1
  94. package/dashboard/dist/assets/{ProcessesListPage-btsU6hBR.js → ProcessesListPage-CrLGDCAD.js} +2 -2
  95. package/dashboard/dist/assets/{ProcessesListPage-btsU6hBR.js.map → ProcessesListPage-CrLGDCAD.js.map} +1 -1
  96. package/dashboard/dist/assets/RoleDetailPage-7j6Mk780.js +11 -0
  97. package/dashboard/dist/assets/RoleDetailPage-7j6Mk780.js.map +1 -0
  98. package/dashboard/dist/assets/{RolePill-Pb9TKSQN.js → RolePill-Cl71eDUA.js} +2 -2
  99. package/dashboard/dist/assets/{RolePill-Pb9TKSQN.js.map → RolePill-Cl71eDUA.js.map} +1 -1
  100. package/dashboard/dist/assets/RolesPage-B0ZgjB2V.js +2 -0
  101. package/dashboard/dist/assets/{RolesPage-1DBoEMsb.js.map → RolesPage-B0ZgjB2V.js.map} +1 -1
  102. package/dashboard/dist/assets/{RunAsSelector-Cp6j3FEY.js → RunAsSelector-DA0h41ml.js} +2 -2
  103. package/dashboard/dist/assets/{RunAsSelector-Cp6j3FEY.js.map → RunAsSelector-DA0h41ml.js.map} +1 -1
  104. package/dashboard/dist/assets/{StickyPagination-Pr8kawv8.js → StickyPagination-t5B71hwd.js} +2 -2
  105. package/dashboard/dist/assets/{StickyPagination-Pr8kawv8.js.map → StickyPagination-t5B71hwd.js.map} +1 -1
  106. package/dashboard/dist/assets/{StreamMessageDetail-BqmQ-N3y.js → StreamMessageDetail-CYFnZsYA.js} +2 -2
  107. package/dashboard/dist/assets/{StreamMessageDetail-BqmQ-N3y.js.map → StreamMessageDetail-CYFnZsYA.js.map} +1 -1
  108. package/dashboard/dist/assets/{SwimlaneTimeline-y1YmXrsj.js → SwimlaneTimeline-CY18gBek.js} +2 -2
  109. package/dashboard/dist/assets/{SwimlaneTimeline-y1YmXrsj.js.map → SwimlaneTimeline-CY18gBek.js.map} +1 -1
  110. package/dashboard/dist/assets/{TagInput-Df4myuYF.js → TagInput-B_WQiuer.js} +2 -2
  111. package/dashboard/dist/assets/{TagInput-Df4myuYF.js.map → TagInput-B_WQiuer.js.map} +1 -1
  112. package/dashboard/dist/assets/{TaskDetailPage-CT5rXHK5.js → TaskDetailPage-B9owzuhV.js} +2 -2
  113. package/dashboard/dist/assets/{TaskDetailPage-CT5rXHK5.js.map → TaskDetailPage-B9owzuhV.js.map} +1 -1
  114. package/dashboard/dist/assets/{TaskQueuePill-YalYdCNo.js → TaskQueuePill-Lgn2obNU.js} +2 -2
  115. package/dashboard/dist/assets/{TaskQueuePill-YalYdCNo.js.map → TaskQueuePill-Lgn2obNU.js.map} +1 -1
  116. package/dashboard/dist/assets/{TasksListPage-DOy6JlKS.js → TasksListPage-Dlsu7cWG.js} +2 -2
  117. package/dashboard/dist/assets/{TasksListPage-DOy6JlKS.js.map → TasksListPage-Dlsu7cWG.js.map} +1 -1
  118. package/dashboard/dist/assets/{TimeAgo-BAQwZSpq.js → TimeAgo-CSzifQXB.js} +2 -2
  119. package/dashboard/dist/assets/{TimeAgo-BAQwZSpq.js.map → TimeAgo-CSzifQXB.js.map} +1 -1
  120. package/dashboard/dist/assets/{TimestampCell-BK_jufns.js → TimestampCell-CIxgN57O.js} +2 -2
  121. package/dashboard/dist/assets/{TimestampCell-BK_jufns.js.map → TimestampCell-CIxgN57O.js.map} +1 -1
  122. package/dashboard/dist/assets/{ToolPill-VGpT6Quy.js → ToolPill-uVqcbHPE.js} +2 -2
  123. package/dashboard/dist/assets/{ToolPill-VGpT6Quy.js.map → ToolPill-uVqcbHPE.js.map} +1 -1
  124. package/dashboard/dist/assets/{ToolTestPanel-5PMQyUCe.js → ToolTestPanel-Bw844EW3.js} +2 -2
  125. package/dashboard/dist/assets/{ToolTestPanel-5PMQyUCe.js.map → ToolTestPanel-Bw844EW3.js.map} +1 -1
  126. package/dashboard/dist/assets/{TopicDetailPage-Cn1ShfR8.js → TopicDetailPage-HuDx2oAW.js} +2 -2
  127. package/dashboard/dist/assets/{TopicDetailPage-Cn1ShfR8.js.map → TopicDetailPage-HuDx2oAW.js.map} +1 -1
  128. package/dashboard/dist/assets/{TopicsPage-D67tjS8L.js → TopicsPage-CAG0RhAK.js} +2 -2
  129. package/dashboard/dist/assets/{TopicsPage-D67tjS8L.js.map → TopicsPage-CAG0RhAK.js.map} +1 -1
  130. package/dashboard/dist/assets/{UserName-Dsu1Jk4N.js → UserName-CoGYeig8.js} +2 -2
  131. package/dashboard/dist/assets/{UserName-Dsu1Jk4N.js.map → UserName-CoGYeig8.js.map} +1 -1
  132. package/dashboard/dist/assets/{WorkflowExecutionPage-BwQoOmUE.js → WorkflowExecutionPage-Dhb9EWgI.js} +2 -2
  133. package/dashboard/dist/assets/{WorkflowExecutionPage-BwQoOmUE.js.map → WorkflowExecutionPage-Dhb9EWgI.js.map} +1 -1
  134. package/dashboard/dist/assets/{WorkflowPill-pPUqmx6Q.js → WorkflowPill-IDjTRfQ8.js} +2 -2
  135. package/dashboard/dist/assets/{WorkflowPill-pPUqmx6Q.js.map → WorkflowPill-IDjTRfQ8.js.map} +1 -1
  136. package/dashboard/dist/assets/{WorkflowsDashboard-D3z7COgH.js → WorkflowsDashboard-bJ6Zebfv.js} +2 -2
  137. package/dashboard/dist/assets/{WorkflowsDashboard-D3z7COgH.js.map → WorkflowsDashboard-bJ6Zebfv.js.map} +1 -1
  138. package/dashboard/dist/assets/{WorkflowsOverview-C15JcxyY.js → WorkflowsOverview-B8262fVx.js} +2 -2
  139. package/dashboard/dist/assets/{WorkflowsOverview-C15JcxyY.js.map → WorkflowsOverview-B8262fVx.js.map} +1 -1
  140. package/dashboard/dist/assets/{YamlWorkflowDetailPage-BYq3t7E-.js → YamlWorkflowDetailPage-DhW3q2cO.js} +2 -2
  141. package/dashboard/dist/assets/{YamlWorkflowDetailPage-BYq3t7E-.js.map → YamlWorkflowDetailPage-DhW3q2cO.js.map} +1 -1
  142. package/dashboard/dist/assets/{YamlWorkflowsPage-B44WgwE_.js → YamlWorkflowsPage-C8hBjSn_.js} +2 -2
  143. package/dashboard/dist/assets/{YamlWorkflowsPage-B44WgwE_.js.map → YamlWorkflowsPage-C8hBjSn_.js.map} +1 -1
  144. package/dashboard/dist/assets/{agents-BRmmK6Y9.js → agents-j2YErVp4.js} +2 -2
  145. package/dashboard/dist/assets/{agents-BRmmK6Y9.js.map → agents-j2YErVp4.js.map} +1 -1
  146. package/dashboard/dist/assets/{bots-BD75mzGG.js → bots-CD8Bl5pL.js} +2 -2
  147. package/dashboard/dist/assets/{bots-BD75mzGG.js.map → bots-CD8Bl5pL.js.map} +1 -1
  148. package/dashboard/dist/assets/{capabilities-DDgC_Kds.js → capabilities-Dz190EAa.js} +2 -2
  149. package/dashboard/dist/assets/{capabilities-DDgC_Kds.js.map → capabilities-Dz190EAa.js.map} +1 -1
  150. package/dashboard/dist/assets/{controlplane-BJh12lMv.js → controlplane-CkUHGPA3.js} +2 -2
  151. package/dashboard/dist/assets/{controlplane-BJh12lMv.js.map → controlplane-CkUHGPA3.js.map} +1 -1
  152. package/dashboard/dist/assets/{escalation-DIJt2sgn.js → escalation-BXTwi4J1.js} +2 -2
  153. package/dashboard/dist/assets/{escalation-DIJt2sgn.js.map → escalation-BXTwi4J1.js.map} +1 -1
  154. package/dashboard/dist/assets/{escalation-columns-zl6E94Up.js → escalation-columns-GxxwSbDk.js} +2 -2
  155. package/dashboard/dist/assets/{escalation-columns-zl6E94Up.js.map → escalation-columns-GxxwSbDk.js.map} +1 -1
  156. package/dashboard/dist/assets/{index-DU7YumVJ.js → index-BK3PVYcd.js} +2 -2
  157. package/dashboard/dist/assets/{index-DU7YumVJ.js.map → index-BK3PVYcd.js.map} +1 -1
  158. package/dashboard/dist/assets/{index-CknKy4RA.js → index-BkwsJ-0v.js} +2 -2
  159. package/dashboard/dist/assets/{index-CknKy4RA.js.map → index-BkwsJ-0v.js.map} +1 -1
  160. package/dashboard/dist/assets/{index-B0kmiSmt.js → index-CaMVn0kW.js} +2 -2
  161. package/dashboard/dist/assets/{index-B0kmiSmt.js.map → index-CaMVn0kW.js.map} +1 -1
  162. package/dashboard/dist/assets/{index-BUbzfEA9.js → index-CkL8bpdH.js} +2 -2
  163. package/dashboard/dist/assets/{index-BUbzfEA9.js.map → index-CkL8bpdH.js.map} +1 -1
  164. package/dashboard/dist/assets/index-CoGkgm9-.js +6 -0
  165. package/dashboard/dist/assets/{index-DccCwyZl.js.map → index-CoGkgm9-.js.map} +1 -1
  166. package/dashboard/dist/assets/{index-CtDf8vvQ.js → index-D-7W2AHB.js} +9 -9
  167. package/dashboard/dist/assets/index-D-7W2AHB.js.map +1 -0
  168. package/dashboard/dist/assets/{index-CV4lEd4I.js → index-DGKgXObc.js} +2 -2
  169. package/dashboard/dist/assets/{index-CV4lEd4I.js.map → index-DGKgXObc.js.map} +1 -1
  170. package/dashboard/dist/assets/{index-DVVIeBBk.js → index-DTrcYOM2.js} +2 -2
  171. package/dashboard/dist/assets/{index-DVVIeBBk.js.map → index-DTrcYOM2.js.map} +1 -1
  172. package/dashboard/dist/assets/{index-BhznDv-4.js → index-NfVWOFbF.js} +2 -2
  173. package/dashboard/dist/assets/{index-BhznDv-4.js.map → index-NfVWOFbF.js.map} +1 -1
  174. package/dashboard/dist/assets/index-Wny0bRgq.css +1 -0
  175. package/dashboard/dist/assets/{index-DrUkTcn-.js → index-YliQ4kW_.js} +2 -2
  176. package/dashboard/dist/assets/{index-DrUkTcn-.js.map → index-YliQ4kW_.js.map} +1 -1
  177. package/dashboard/dist/assets/{index-CpgV4Kkp.js → index-eLRh-2NW.js} +2 -2
  178. package/dashboard/dist/assets/{index-CpgV4Kkp.js.map → index-eLRh-2NW.js.map} +1 -1
  179. package/dashboard/dist/assets/{index-CDPBXB4e.js → index-n-lZY5S6.js} +2 -2
  180. package/dashboard/dist/assets/{index-CDPBXB4e.js.map → index-n-lZY5S6.js.map} +1 -1
  181. package/dashboard/dist/assets/{index-j-C7g_Ej.js → index-qT7eB_Hk.js} +2 -2
  182. package/dashboard/dist/assets/{index-j-C7g_Ej.js.map → index-qT7eB_Hk.js.map} +1 -1
  183. package/dashboard/dist/assets/{knowledge-pNDWACXB.js → knowledge-CSAbLtk7.js} +2 -2
  184. package/dashboard/dist/assets/{knowledge-pNDWACXB.js.map → knowledge-CSAbLtk7.js.map} +1 -1
  185. package/dashboard/dist/assets/{mcp-xddLBwUW.js → mcp-D0Ha3aEo.js} +2 -2
  186. package/dashboard/dist/assets/{mcp-xddLBwUW.js.map → mcp-D0Ha3aEo.js.map} +1 -1
  187. package/dashboard/dist/assets/{mcp-query-CmNjvxTG.js → mcp-query-BySmvmuS.js} +2 -2
  188. package/dashboard/dist/assets/{mcp-query-CmNjvxTG.js.map → mcp-query-BySmvmuS.js.map} +1 -1
  189. package/dashboard/dist/assets/{pipelines-B05sd2nB.js → pipelines-DiqWgxJd.js} +2 -2
  190. package/dashboard/dist/assets/{pipelines-B05sd2nB.js.map → pipelines-DiqWgxJd.js.map} +1 -1
  191. package/dashboard/dist/assets/roles-BdtnzLsL.js +2 -0
  192. package/dashboard/dist/assets/roles-BdtnzLsL.js.map +1 -0
  193. package/dashboard/dist/assets/{tasks-Bm1OKrJA.js → tasks-DYCUi5nZ.js} +2 -2
  194. package/dashboard/dist/assets/{tasks-Bm1OKrJA.js.map → tasks-DYCUi5nZ.js.map} +1 -1
  195. package/dashboard/dist/assets/{topics-BVIqtmd4.js → topics-CA7i8ymr.js} +2 -2
  196. package/dashboard/dist/assets/{topics-BVIqtmd4.js.map → topics-CA7i8ymr.js.map} +1 -1
  197. package/dashboard/dist/assets/{useEventHooks-_qCzU74s.js → useEventHooks-SfWdyc5L.js} +2 -2
  198. package/dashboard/dist/assets/{useEventHooks-_qCzU74s.js.map → useEventHooks-SfWdyc5L.js.map} +1 -1
  199. package/dashboard/dist/assets/{useNamespace-CDnCSFpG.js → useNamespace-CZUsk5Vv.js} +2 -2
  200. package/dashboard/dist/assets/{useNamespace-CDnCSFpG.js.map → useNamespace-CZUsk5Vv.js.map} +1 -1
  201. package/dashboard/dist/assets/{useYamlActivityEvents-zrHMhaFM.js → useYamlActivityEvents-DKQ5dREx.js} +2 -2
  202. package/dashboard/dist/assets/{useYamlActivityEvents-zrHMhaFM.js.map → useYamlActivityEvents-DKQ5dREx.js.map} +1 -1
  203. package/dashboard/dist/assets/{users-BPBMZg27.js → users-BxKf5dAX.js} +2 -2
  204. package/dashboard/dist/assets/{users-BPBMZg27.js.map → users-BxKf5dAX.js.map} +1 -1
  205. package/dashboard/dist/assets/{vendor-icons-C1zVP_dv.js → vendor-icons-CEWCGPeJ.js} +76 -76
  206. package/dashboard/dist/assets/vendor-icons-CEWCGPeJ.js.map +1 -0
  207. package/dashboard/dist/assets/{workflows-CT6nTL1I.js → workflows-DNPQ-j-7.js} +2 -2
  208. package/dashboard/dist/assets/{workflows-CT6nTL1I.js.map → workflows-DNPQ-j-7.js.map} +1 -1
  209. package/dashboard/dist/assets/{yaml-workflows-CdfjkOKU.js → yaml-workflows-CA8BrFxc.js} +2 -2
  210. package/dashboard/dist/assets/{yaml-workflows-CdfjkOKU.js.map → yaml-workflows-CA8BrFxc.js.map} +1 -1
  211. package/dashboard/dist/index.html +3 -3
  212. package/docs/api/http/roles.md +62 -0
  213. package/docs/api/mcp/admin.md +32 -1
  214. package/docs/api/mcp/human-queue.md +2 -0
  215. package/docs/api/sdk/roles.md +39 -0
  216. package/docs/cli.md +13 -0
  217. package/docs/dashboard.md +13 -6
  218. package/docs/data.md +34 -0
  219. package/docs/hitl-guide.md +25 -3
  220. package/package.json +2 -2
  221. package/dashboard/dist/assets/OperationsPage-CAmiFHI6.js +0 -2
  222. package/dashboard/dist/assets/OperationsPage-CAmiFHI6.js.map +0 -1
  223. package/dashboard/dist/assets/RoleDetailPage-BB_e330f.js +0 -16
  224. package/dashboard/dist/assets/RoleDetailPage-BB_e330f.js.map +0 -1
  225. package/dashboard/dist/assets/RolesPage-1DBoEMsb.js +0 -2
  226. package/dashboard/dist/assets/index-BRtKQtQZ.css +0 -1
  227. package/dashboard/dist/assets/index-CtDf8vvQ.js.map +0 -1
  228. package/dashboard/dist/assets/index-DccCwyZl.js +0 -6
  229. package/dashboard/dist/assets/roles-bRgaMcdy.js +0 -2
  230. package/dashboard/dist/assets/roles-bRgaMcdy.js.map +0 -1
  231. package/dashboard/dist/assets/vendor-icons-C1zVP_dv.js.map +0 -1
@@ -724,7 +724,7 @@ Create a new role. Lowercase alphanumeric with hyphens/underscores.
724
724
 
725
725
  ### update_role
726
726
 
727
- Update a role's metadata: display name, description, form schema, metadata schema, free properties bag, operations visibility, process parent, and the typed operational targets (SLA minutes, throughput goal, worker count). Only provided fields are changed.
727
+ Update a role's metadata: display name, description, form schema, metadata schema, free properties bag, operations visibility, process parent, and the typed operational targets (SLA minutes, throughput goal, worker count). Only provided fields are changed. A change to `form_schema` or `metadata_schema` snapshots the new pair into the role's schema version history and advances its current version.
728
728
 
729
729
  | | |
730
730
  |---|---|
@@ -745,6 +745,37 @@ Update a role's metadata: display name, description, form schema, metadata schem
745
745
  | `sla_minutes` | `number \| null` | No | Target resolution time in minutes |
746
746
  | `target_per_hour` | `number \| null` | No | Throughput goal (items resolved per hour) |
747
747
  | `worker_count` | `number \| null` | No | Station capacity (staff or machines) |
748
+ | `upstream_roles` | `string[] \| null` | No | Replace the set of roles this station draws input from across other Operations sequences (omitted = preserve; `null` or `[]` = clear) |
749
+ | `change_summary` | `string` | No | Label recorded on the schema version snapshot when this update changes a schema field |
750
+
751
+ ### get_role_schema
752
+
753
+ Fetch a role's `form_schema` + `metadata_schema` pair. With `version`, reads that immutable snapshot from the version history (the one an escalation pinned via `metadata.schema_version`); without it, reads the live (latest) schema and its current version number.
754
+
755
+ | | |
756
+ |---|---|
757
+ | Read-safe | Yes |
758
+
759
+ **Parameters:**
760
+
761
+ | Field | Type | Required | Description |
762
+ |-------|------|----------|-------------|
763
+ | role | string | Yes | Role whose schema to fetch |
764
+ | version | number | No | Version pin (positive integer) |
765
+
766
+ ### list_role_schema_versions
767
+
768
+ List a role's schema version history, newest first. Each entry carries the version, presence flags for the two schemas, the change summary, and whether it is the current version.
769
+
770
+ | | |
771
+ |---|---|
772
+ | Read-safe | Yes |
773
+
774
+ **Parameters:**
775
+
776
+ | Field | Type | Required | Description |
777
+ |-------|------|----------|-------------|
778
+ | role | string | Yes | Role whose history to list |
748
779
 
749
780
  ### add_escalation_chain
750
781
 
@@ -33,6 +33,7 @@ Create a new escalation for human review. Returns the escalation ID.
33
33
  | type | string | No | Escalation type classification (default: "mcp") |
34
34
  | subtype | string | No | Escalation subtype (default: "tool_call") |
35
35
  | priority | number | No | Priority: 1 (highest) to 4 (lowest) (default: 2) |
36
+ | schema_version | number | No | Pin the role schema version the resolver form renders (defaults to latest) |
36
37
 
37
38
  ### check_resolution
38
39
 
@@ -116,6 +117,7 @@ Create an escalation and pause the workflow until a human responds. Returns a si
116
117
  | type | string | No | Escalation type classification (default: "mcp") |
117
118
  | subtype | string | No | Escalation subtype (default: "wait_for_human") |
118
119
  | priority | number | No | Priority: 1 (highest) to 4 (lowest) (default: 1) |
120
+ | schema_version | number | No | Pin the role schema version the resolver form renders (defaults to latest; an embedded `form_schema` takes precedence) |
119
121
 
120
122
  ## Scope and One-Time Users
121
123
 
@@ -269,7 +269,46 @@ const result = await lt.roles.update({
269
269
  | `sla_minutes` | `number \| null` | No | SLA target in minutes |
270
270
  | `target_per_hour` | `number \| null` | No | Throughput target (items per hour) |
271
271
  | `worker_count` | `number \| null` | No | Station capacity |
272
+ | `upstream_roles` | `string[] \| null` | No | Replace the set of roles this station draws input from across other Operations sequences (omitted = preserve; `null` or `[]` = clear). Distinct from `parent_role`, which places the role in its own sequence |
273
+ | `change_summary` | `string` | No | Label recorded on the schema version snapshot when this update changes a schema field |
274
+
275
+ When the update changes `form_schema` or `metadata_schema`, the new pair is snapshotted into the role's version history and `current_schema_version` advances.
272
276
 
273
277
  **Returns:** `LTApiResult<RoleDetail>` — the updated role.
274
278
 
275
279
  **Auth:** Role manager (admin type, superadmin, or engineer)
280
+
281
+ ## getSchema
282
+
283
+ Fetch a role's `form_schema` + `metadata_schema` pair. With `version`, reads that immutable snapshot from the version history (the one an escalation pins via `conditionLT`'s `schemaVersion`); without it, reads the live (latest) schema and its current version number. A missing version is a 404 — it never falls back to a different version.
284
+
285
+ ```typescript
286
+ const latest = await lt.roles.getSchema({ role: 'reviewer' });
287
+ const pinned = await lt.roles.getSchema({ role: 'reviewer', version: 3 });
288
+ ```
289
+
290
+ **Parameters:**
291
+
292
+ | Field | Type | Required | Description |
293
+ |-------|------|----------|-------------|
294
+ | `role` | `string` | Yes | Role whose schema to fetch |
295
+ | `version` | `number` | No | Version pin (positive integer) |
296
+
297
+ **Returns:** `LTApiResult<RoleSchemaVersion>` — `{ role, version, form_schema, metadata_schema, change_summary, created_at, latest_version }`.
298
+
299
+ ## listSchemaVersions
300
+
301
+ List a role's schema version history, newest first. Each entry carries the version number, presence flags for the two schemas, the change summary, and whether it is the role's current version.
302
+
303
+ ```typescript
304
+ const result = await lt.roles.listSchemaVersions({ role: 'reviewer' });
305
+ // result.data.versions → [{ version, has_form_schema, has_metadata_schema, change_summary, created_at, is_current }, …]
306
+ ```
307
+
308
+ **Parameters:**
309
+
310
+ | Field | Type | Required | Description |
311
+ |-------|------|----------|-------------|
312
+ | `role` | `string` | Yes | Role whose history to list |
313
+
314
+ **Returns:** `LTApiResult<{ versions: RoleSchemaVersionSummary[] }>`
package/docs/cli.md CHANGED
@@ -127,6 +127,19 @@ ltc users list
127
127
  ltc users get USER_ID
128
128
  ```
129
129
 
130
+ ### Roles
131
+
132
+ Roles are the queue-backed work surfaces where paused workflows hand off to people. Role schemas are versioned — every schema save adds an immutable snapshot, and escalations can pin one via `schemaVersion`.
133
+
134
+ ```bash
135
+ ltc roles list # Roles with schema version, member and workflow counts
136
+ ltc roles schema reviewer # Latest form/metadata schema (with current version)
137
+ ltc roles schema reviewer --version 3 # Immutable v3 snapshot
138
+ ltc roles schema-versions reviewer # Version history, newest first
139
+ ltc roles save-schema reviewer --file schema.json --summary "Added lotNumber"
140
+ # Save the escalation form schema (a change creates the next version)
141
+ ```
142
+
130
143
  ### Compiler
131
144
 
132
145
  ```bash
package/docs/dashboard.md CHANGED
@@ -196,15 +196,18 @@ User Accounts and Service Accounts live on the same page, separated by a tab tog
196
196
 
197
197
  ### Roles
198
198
 
199
- Define roles and configure escalation chains that control how work flows between teams.
199
+ A role is where a running workflow hands work to a person. Work waits in the role's queue, the role's schema shapes the form the person completes, membership decides who can see and resolve which items, and submitting the form resumes the workflow exactly where it paused. Roles are also where escalation chains are configured the paths work takes between teams when it needs another set of hands.
200
200
 
201
201
  - **Role list** — all roles in the system. Each row shows the role key, title, description, user count, chain count, workflow count, and an OPS badge if `ops_visible` is set.
202
- - **Role detail** — click a role to open its detail panel: identity fields (title, description), escalation chains, and the capacity settings (`sla_minutes`, `target_per_hour`, `worker_count`). Edit these inline and save via `PATCH /api/roles/:role`.
202
+ - **Role detail** — click a role to open its detail panel: identity fields (title, description), escalation chains, members, schemas with version history, and the capacity settings (`sla_minutes`, `target_per_hour`, `worker_count`). Edit these inline and save via `PATCH /api/roles/:role`.
203
+ - **Members** — who holds the role: admins manage it; members work its queue according to their read/write scope (read = which items appear; write = which they can claim and resolve).
204
+ - **Prior Step and Upstream Inputs** — Prior Step (`parent_role`) places the role in one Operations sequence; a role with no prior step starts its own. Upstream Inputs declare the roles it also draws from in other sequences — mixin-like, many allowed — rendered on the Operations chart as a merge glyph on the station rather than a bend in the line.
205
+ - **Escalation Schema editor** (`/admin/roles/:role/schema`) — the versioned escalation form has its own page. Enter form fields as JSON Schema in a full-width editor with an optional change summary; every save that changes the schema appends an immutable snapshot and advances the current version. The version rail shows the history — expand any version to view its snapshot or load it into the editor as the base for the next save. Workflows pin a version via `schemaVersion` in the `conditionLT` config so their resolver form keeps that exact shape; escalations without a pin render the latest. The role detail page shows which version is in use and links here.
203
206
  - **Create Role** — add a new role. Roles referenced in workflow configs are auto-created, but you can also create them here for organizational clarity.
204
207
  - **Scope picker** — when granting a role at `member` type, a Scope picker offers the five named work-surface 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`). `admin` and `superadmin` grants show no Scope picker — they always work the whole queue. The picker enforces **write ⊆ read**, so a write breadth wider than the read breadth cannot be selected.
205
208
  - **Escalation chains** — define source → target role mappings. When a reviewer escalates, the chain determines which roles receive the escalation next. Chains are directional (reviewer → engineer → admin) and support multiple targets per source.
206
209
 
207
- **API:** `GET /api/roles` lists roles. `POST /api/roles` creates. `PATCH /api/roles/:role` updates metadata and capacity fields. `GET /api/roles/details` returns full `RoleDetail` shapes. `GET /api/roles/escalation-chains` lists chains. `POST /api/roles/escalation-chains` adds a chain.
210
+ **API:** `GET /api/roles` lists roles. `POST /api/roles` creates. `PATCH /api/roles/:role` updates metadata and capacity fields. `GET /api/roles/details` returns full `RoleDetail` shapes. `GET /api/roles/:role/schema` fetches the latest or a pinned schema version; `GET /api/roles/:role/schema/versions` lists the history. `GET /api/roles/escalation-chains` lists chains. `POST /api/roles/escalation-chains` adds a chain.
208
211
 
209
212
  ### DB Maintenance
210
213
 
@@ -264,11 +267,15 @@ Accessible at `/escalations`. A statistics dashboard for escalation health acros
264
267
 
265
268
  ### Operations
266
269
 
267
- Accessible at `/operations`. The COO shop-floor view — shows actual-vs-target flow across every ops-visible role as a pace chart, with a station table and detail panel below.
270
+ Accessible at `/operations` (sidebar: Operations; page header: **Pace Board**). The COO shop-floor view — shows actual-vs-target flow across ops-visible roles as a pace chart, with a station table and detail panel below.
268
271
 
272
+ Execution is a graph; this page tells its story as **sequences**. Each ops-visible role with no prior step (or whose prior step is outside the ops set) starts a sequence, followed by its `parent_role` descendants in dependency order. The table is always the ground truth of the queues; the SVG is the narrative line drawn through them.
273
+
274
+ - **Sequence picker** — when more than one sequence exists, tabs appear above the chart, one per sequence, named by its origin role (station count alongside). The active sequence is deep-linked (`?fragment=<origin role>`) and each switch is a browser-history entry, so a shared URL opens the same sequence and back/forward walks between them.
269
275
  - **Period selector** — `15m`, `1h`, `24h`, `7d`, `30d`. Controls the lookback window for resolved counts, percentile metrics, and the throughput metrics.
270
- - **Pace chart** — connects all ops-visible 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. Station circles are colored by pace ratio (green ≥ 100%, amber ≥ 60%, red below), with a three-number strip per station: pending, active, resolved.
271
- - **Station table** — one row per ops-visible role with PENDING, ACTIVE, RESOLVED, P99 WAIT, P99 WORK, and a PRESSURE mini-bar. Stations with in-arrears items show a sub-row with a link to oldest-first queue view.
276
+ - **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).
277
+ - **Merge affordance** — a station that declares upstream inputs (roles feeding it from other sequences) 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.
278
+ - **Station table** — one row per station with PENDING, ACTIVE, RESOLVED (column bands in the chart's hues), P99 WAIT, P99 WORK, and a TREND mini-bar. A merge icon next to the role name marks cross-sequence inputs. Stations with in-arrears items show a sub-row with a link to the oldest-first queue view.
272
279
  - **Station detail panel** — opens on row or circle click. Shows the role's identity, an independent period toggle, and full metric breakdown (wait/work percentiles, SLA target, worker count, links to queue).
273
280
 
274
281
  Roles appear on this view when `ops_visible = true` is set. The capacity settings (`sla_minutes`, `target_per_hour`, `worker_count`) drive the computed metrics. Set these via `PATCH /api/roles/:role` or the Roles admin page. See [Operations](operations.md) for the full concept doc.
package/docs/data.md CHANGED
@@ -11,10 +11,44 @@ Canonical role registry. Roles referenced by other tables are seeded here.
11
11
  | Column | Type | Default | Description |
12
12
  |--------|------|---------|-------------|
13
13
  | `role` | `TEXT` | — | Primary key |
14
+ | `title` | `TEXT` | — | Display name |
15
+ | `description` | `TEXT` | — | Short human-readable purpose |
16
+ | `form_schema` | `JSONB` | — | Live (latest) JSON Schema for the escalation resolve form |
17
+ | `metadata_schema` | `JSONB` | — | Live (latest) JSON Schema validating `lt_escalations.metadata` at creation |
18
+ | `current_schema_version` | `INTEGER` | — | Version of the live schema pair; advances on every schema change (null until the role first carries a schema) |
19
+ | `properties` | `JSONB` | `'{}'` | Free user-owned bag (icons, colors, tags) |
20
+ | `ops_visible` | `BOOLEAN` | `false` | Show as a station on the Operations view |
21
+ | `parent_role` | `TEXT` | — | FK to `lt_roles(role)` — the single prior step placing this role in one Operations sequence (cross-sequence inputs live in `lt_role_upstreams`) |
22
+ | `sla_minutes` | `NUMERIC` | — | Target resolution time |
23
+ | `target_per_hour` | `NUMERIC` | — | Throughput target |
24
+ | `worker_count` | `NUMERIC` | — | Station capacity |
14
25
  | `created_at` | `TIMESTAMPTZ NOT NULL` | `NOW()` | Row creation time |
15
26
 
16
27
  **Seeds:** `reviewer`, `engineer`, `admin`, `superadmin`.
17
28
 
29
+ ### lt_role_upstreams
30
+
31
+ The graph edges that don't fit the line. `lt_roles.parent_role` places a role in one Operations sequence (its single "prior step"); this table carries the remaining edges — the roles a station draws input from that live in other sequences (mixin-like, many allowed). The Operations chart renders them as a merge affordance on the station rather than bending the sequence. Replaced as a set through the same atomic `PATCH /api/roles/:role` statement (`upstream_roles`).
32
+
33
+ | Column | Type | Default | Description |
34
+ |--------|------|---------|-------------|
35
+ | `role` | `TEXT NOT NULL` | — | FK to `lt_roles(role)` `ON DELETE CASCADE`; part of the primary key |
36
+ | `upstream_role` | `TEXT NOT NULL` | — | FK to `lt_roles(role)` `ON DELETE CASCADE`; part of the primary key. `CHECK (role <> upstream_role)` |
37
+ | `created_at` | `TIMESTAMPTZ NOT NULL` | `NOW()` | When the edge was declared |
38
+
39
+ ### lt_role_schemas
40
+
41
+ Immutable version history of each role's schema pair. Every change to a role's `form_schema` or `metadata_schema` (via `PATCH /api/roles/:role`, the SDK, or the `update_role` MCP tool) appends the next `(role, version)` snapshot in the same atomic statement that updates `lt_roles`. Escalations pin a version via `metadata.schema_version` (`conditionLT`'s `schemaVersion` field) so the resolver form they render stays exactly what their author specified; unpinned escalations use the live columns on `lt_roles`.
42
+
43
+ | Column | Type | Default | Description |
44
+ |--------|------|---------|-------------|
45
+ | `role` | `TEXT NOT NULL` | — | FK to `lt_roles(role)` `ON DELETE CASCADE`; part of the primary key |
46
+ | `version` | `INTEGER NOT NULL` | — | Monotonic per role; part of the primary key |
47
+ | `form_schema` | `JSONB` | — | Snapshot of the form schema at this version |
48
+ | `metadata_schema` | `JSONB` | — | Snapshot of the metadata schema at this version |
49
+ | `change_summary` | `TEXT` | — | Optional label supplied with the change |
50
+ | `created_at` | `TIMESTAMPTZ NOT NULL` | `NOW()` | When this version was created |
51
+
18
52
  ### lt_tasks
19
53
 
20
54
  Tracks every workflow execution. Created by the LT interceptor when a workflow starts; updated when it completes or fails.
@@ -160,10 +160,32 @@ export async function reviewWorkflow(envelope: LTEnvelope) {
160
160
  }
161
161
  ```
162
162
 
163
- ### Per-Escalation vs Workflow-Level Schema
163
+ ### Which Schema Renders the Form
164
164
 
165
- - **Workflow config `resolver_schema`**: Default form for all escalations of this workflow type. Set in the workflow registry.
166
- - **`metadata.form_schema`**: Per-escalation override. Takes precedence over workflow config. Use when different escalation points in the same workflow need different forms.
165
+ The resolver form resolves in order, most specific first:
166
+
167
+ 1. **`metadata.form_schema`** — a full JSON Schema embedded on the escalation row. Use when different escalation points in the same workflow need different forms.
168
+ 2. **`metadata.schema_version`** — a pin to a specific version of the role's schema (set via `schemaVersion` in the `conditionLT` config). The form renders exactly that snapshot, even after the role's schema changes.
169
+ 3. **Workflow config `resolver_schema`** — default form for all escalations of this workflow type. Set in the workflow registry.
170
+ 4. **The role's latest `form_schema`** — the fallback every role provides.
171
+
172
+ ### Versioned Role Schemas
173
+
174
+ Every save that changes a role's `form_schema` or `metadata_schema` appends an immutable snapshot to the version history (`lt_role_schemas`) and advances the role's current version. Escalations that need a guaranteed shape pin one:
175
+
176
+ ```typescript
177
+ const decision = await conditionLT<{ approved: boolean; lotNumber: string }>(signalId, {
178
+ role: 'reviewer',
179
+ description: instructions,
180
+ schemaVersion: 3, // this row renders role schema v3, always
181
+ });
182
+ ```
183
+
184
+ The pin travels as `metadata.schema_version` on the row (GIN-indexed, queryable like any facet). A pin that names a missing version fails at creation with a 400 — it never falls through to a different version. Without a pin, the role's latest schema always applies: workflow authors who don't care get the current form automatically; authors who depend on a specific field (say, a form that gained `lotNumber` in v3 and the workflow reads it back) pin the version and the round trip stays aligned.
185
+
186
+ `metadata.schema_version` also selects which `metadata_schema` validates the creation-time metadata bag on `POST /api/escalations`.
187
+
188
+ Inspect versions via `GET /api/roles/:role/schema?version=N`, `GET /api/roles/:role/schema/versions`, `lt.roles.getSchema` / `lt.roles.listSchemaVersions`, `ltc roles schema <role> --version N`, or the `get_role_schema` / `list_role_schema_versions` admin MCP tools. Save a new version by writing only the schema — `PATCH /api/roles/:role` with `form_schema` (+ optional `change_summary`), `lt.roles.update`, `ltc roles save-schema <role> --file schema.json`, or the `update_role` MCP tool. The dashboard edits it on the role's Escalation Schema page (`/admin/roles/:role/schema`), with the version history and snapshot viewer alongside.
167
189
 
168
190
  ---
169
191
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/long-tail",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "description": "Long Tail Workflows — Durable AI workflows with human-in-the-loop escalation. Powered by PostgreSQL.",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",
@@ -71,7 +71,7 @@
71
71
  "@anthropic-ai/sdk": "^0.92.0",
72
72
  "@aws-sdk/client-s3": "^3.1017.0",
73
73
  "@aws-sdk/s3-request-presigner": "^3.1045.0",
74
- "@hotmeshio/hotmesh": "^0.25.1",
74
+ "@hotmeshio/hotmesh": "^0.25.2",
75
75
  "@modelcontextprotocol/sdk": "^1.27.1",
76
76
  "@opentelemetry/exporter-trace-otlp-proto": "^0.215.0",
77
77
  "@opentelemetry/resources": "^2.5.1",
@@ -1,2 +0,0 @@
1
- import{a as R,j as e}from"./vendor-query-B2UbickB.js";import{b as le}from"./roles-bRgaMcdy.js";import{K as ee}from"./index-CtDf8vvQ.js";import{i as oe}from"./useEventHooks-_qCzU74s.js";import{P as ie}from"./PageHeader-BZJsEVOC.js";import{X as ce,ae as D,au as xe,av as de}from"./vendor-icons-C1zVP_dv.js";import{L}from"./vendor-react-CXumBFUA.js";const te=800,se=270,A=40,pe=76,B=24,ue=72,z=te-A-pe,ne=se-B-ue,w=B+ne,X=A+z,N="0.5s cubic-bezier(0.4, 0, 0.2, 1)";function he(t){return t.length===0?"":t.map((n,l)=>`${l===0?"M":"L"} ${n.x.toFixed(1)} ${n.y.toFixed(1)}`).join(" ")}function Q(t){if(t.length===0)return"";if(t.length===1)return`M ${t[0].x} ${t[0].y}`;const n=[];for(let l=0;l<t.length-1;l++){const i=t[Math.max(0,l-1)],o=t[l],x=t[l+1],a=t[Math.min(t.length-1,l+2)],c=(o.x+(x.x-i.x)/6).toFixed(1),u=(o.y+(x.y-i.y)/6).toFixed(1),r=(x.x-(a.x-o.x)/6).toFixed(1),d=(x.y-(a.y-o.y)/6).toFixed(1);l===0&&n.push(`M ${o.x.toFixed(1)} ${o.y.toFixed(1)}`),n.push(`C ${c} ${u}, ${r} ${d}, ${x.x.toFixed(1)} ${x.y.toFixed(1)}`)}return n.join(" ")}function me(t){return t>=1e4?`${Math.round(t/1e3)}k`:t>=1e3?`${(t/1e3).toFixed(1)}k`:String(t)}function V(t){return t.length>0?t[t.length-1]:void 0}function fe(t){return t==null?{stroke:"#cbd5e1",fill:"#f8fafc"}:t>=1?{stroke:"#10b981",fill:"#ecfdf5"}:t>=.6?{stroke:"#f59e0b",fill:"#fffbeb"}:{stroke:"#ef4444",fill:"#fef2f2"}}const I="#6366f1",ye=13;function je(t){const n=[...t].sort((i,o)=>i.y-o.y);let l=-1/0;return n.map(i=>{const o=Math.max(i.y+3,l+ye);return l=o,{...i,labelY:o}})}function ge({stations:t,selectedRole:n,onSelect:l,periodHours:i}){const[o,x]=R.useState(null),a=t.length;if(a===0)return null;const c=s=>a===1?A+z/2:A+s/(a-1)*z,u=new Map(t.map((s,h)=>[s.role,c(h)])),r=t.map((s,h)=>{var W,P,v,F;const b=s.target_per_hour??null,q=b!=null?b*i:null;return{idx:h,x:c(h),expected:q,actual:((W=s.metric)==null?void 0:W.resolved)??0,active:((P=s.metric)==null?void 0:P.claimed)??0,pending:((v=s.metric)==null?void 0:v.pending)??0,inArrears:((F=s.metric)==null?void 0:F.in_arrears)??0}}),d=r.some(s=>s.expected!=null),y=Math.max(1,...r.map(s=>s.expected??0),...r.map(s=>s.actual),...r.map(s=>s.expected!=null?s.active:0))*1.15,m=s=>B+ne*(1-Math.max(0,Math.min(s,y))/y),f=r.filter(s=>s.expected!=null),S=f.map(s=>({...s,y:m(s.expected)})),j=f.map(s=>({...s,y:m(s.actual)})),k=f.map(s=>({...s,y:m(s.active)})),C=he(S),_=Q(j),E=Q(k),M=V(j),p=V(S),$=V(k),U=j.length>=2&&M?`${_} L ${M.x.toFixed(1)} ${w} L ${j[0].x.toFixed(1)} ${w} Z`:"",re=je([p?{key:"target",text:"target",color:"#ef4444",y:p.y}:null,M?{key:"actual",text:"actual",color:"#475569",y:M.y}:null,$?{key:"active",text:"active",color:I,y:$.y}:null].filter(Boolean));return e.jsxs("svg",{viewBox:`0 0 ${te} ${se}`,className:"w-full h-full",preserveAspectRatio:"xMidYMid meet",style:{display:"block"},children:[t.map(s=>{if(!s.parent_role)return null;const h=u.get(s.parent_role);if(h==null)return null;const b=c(t.findIndex(q=>q.role===s.role));return e.jsx("line",{x1:h,y1:w,x2:b,y2:w,stroke:"#e2e8f0",strokeWidth:.75},`dep-${s.role}`)}),U&&e.jsx("path",{d:U,fill:"rgb(71 85 105 / 0.05)",style:{transition:`d ${N}`}}),C&&e.jsx("path",{d:C,fill:"none",stroke:"#ef4444",strokeWidth:.5,strokeLinejoin:"round",opacity:.9,style:{transition:`d ${N}`}}),S.map(s=>e.jsx("text",{x:s.x,y:s.y-3.5,textAnchor:"middle",fontSize:6.5,fill:"#ef4444",fontFamily:"ui-monospace, monospace",fontWeight:"500",opacity:.7,style:{transition:`y ${N}`},children:Math.round(s.expected)},`tval-${s.idx}`)),E&&e.jsx("path",{d:E,fill:"none",stroke:I,strokeWidth:.75,strokeDasharray:"2 3",strokeLinecap:"round",opacity:.5,style:{transition:`d ${N}`}}),_&&e.jsx("path",{d:_,fill:"none",stroke:"#475569",strokeWidth:1,strokeLinejoin:"round",strokeLinecap:"round",opacity:.7,style:{transition:`d ${N}`}}),k.map(s=>{if(s.pending<=0&&s.active<=0)return null;const h=Math.max(2.5,Math.min(5,2.5+s.active/10));return e.jsxs("g",{transform:`translate(${s.x} ${s.y})`,style:{transition:`transform ${N}`},children:[e.jsx("circle",{r:h,fill:I,opacity:.85}),s.pending>0&&e.jsx("text",{y:-h-4,textAnchor:"middle",fontSize:7.5,fill:"#64748b",fontFamily:"ui-monospace, monospace",fontWeight:"600",children:s.pending}),s.active>0&&e.jsx("text",{y:h+9,textAnchor:"middle",fontSize:7.5,fill:I,fontFamily:"ui-monospace, monospace",fontWeight:"600",children:s.active})]},`active-${s.idx}`)}),r.map(s=>{const h=t[s.idx],b=s.x,q=s.expected!=null?m(s.actual):w,W=s.expected&&s.expected>0?s.actual/s.expected:null,{stroke:P}=fe(s.expected!=null?W:null),v=Math.max(3,Math.min(5.5,3+s.actual/120)),F=h.role===n,G=o===s.idx,Y=s.expected==null?"idle · set a target rate to plot pace":`${s.actual} done · target ${Math.round(s.expected)}`+(s.pending>0?` · ${s.pending} in queue`:"")+(s.active>0?` · ${s.active} active`:""),O=Y.length*5.5+20,J=Math.max(A+O/2+4,Math.min(X-O/2-4,b)),K=q-v-10;return e.jsxs("g",{onClick:()=>l(h.role),onMouseEnter:()=>x(s.idx),onMouseLeave:()=>x(null),style:{cursor:"pointer"},children:[e.jsxs("g",{transform:`translate(${b} ${q})`,style:{transition:`transform ${N}`},children:[e.jsx("circle",{r:22,fill:"transparent"}),s.inArrears>0&&e.jsx("circle",{r:v+4,fill:"none",stroke:"#ef4444",strokeWidth:1,strokeDasharray:"3 2",opacity:.6}),e.jsx("circle",{r:v,fill:P,opacity:.9,style:{transition:`r ${N}`}}),s.expected!=null&&e.jsx("text",{y:-v-5,textAnchor:"middle",fontSize:9.5,fill:"#334155",fontFamily:"ui-monospace, monospace",fontWeight:"500",children:me(s.actual)}),G&&!F&&e.jsx("circle",{r:v+3,fill:"none",stroke:P,strokeWidth:1,opacity:.4}),F&&e.jsx("circle",{r:v+4,fill:"none",stroke:"#6366f1",strokeWidth:2})]}),e.jsx("text",{x:b,y:w+14,textAnchor:"middle",fontSize:9.5,fill:"#475569",fontFamily:"ui-sans-serif, sans-serif",fontWeight:"500",children:h.title??h.role}),e.jsx("text",{x:b,y:w+26,textAnchor:"middle",fontSize:8,fill:"#94a3b8",fontFamily:"ui-monospace, monospace",children:h.role}),G&&e.jsxs("g",{children:[e.jsx("rect",{x:J-O/2,y:K-16,width:O,height:19,rx:3,fill:"#0f172a",opacity:.88}),e.jsx("text",{x:J,y:K-3,textAnchor:"middle",fontSize:8.5,fill:"white",fontFamily:"ui-monospace, monospace",children:Y})]})]},h.role)}),!d&&e.jsx("text",{x:A+z/2,y:w+48,textAnchor:"middle",fontSize:8.5,fill:"#94a3b8",fontFamily:"ui-sans-serif, sans-serif",children:"Set target_per_hour on each role in Admin → Roles to enable the pace chart"}),re.map(s=>e.jsx("text",{x:X+6,y:s.labelY,fontSize:8,fill:s.color,fontFamily:"ui-sans-serif, sans-serif",fontWeight:s.key==="actual"?700:500,opacity:s.key==="active"?.85:1,style:{transition:`y ${N}`},children:s.text},s.key))]})}const ae=["15m","1h","24h","7d","30d"],be=t=>ae.includes(t);function T(t){if(t==null)return"—";if(t<1){const n=Math.round(t*60);return n<=0?"< 1s":`${n}s`}return t<60?`${t.toFixed(0)}m`:`${(t/60).toFixed(1)}h`}function H({label:t,value:n}){return e.jsxs("div",{className:"flex items-center justify-between py-1",children:[e.jsx("span",{className:"text-[10px] text-text-tertiary uppercase tracking-wider",children:t}),e.jsx("span",{className:"text-xs font-mono font-medium text-text-primary tabular-nums",children:n})]})}function ve({period:t,onChange:n}){return e.jsx("div",{className:"flex items-center gap-1 mb-4",children:ae.map(l=>e.jsx("button",{onClick:()=>n(l),className:`px-2 py-0.5 text-[10px] font-mono rounded transition-colors ${t===l?"bg-accent/10 text-accent font-semibold":"text-text-quaternary hover:text-text-secondary"}`,children:l},l))})}function Ne({role:t,globalPeriod:n,onClose:l}){const[i,o]=R.useState(be(n)?n:"24h"),{data:x}=ee(i),a=x==null?void 0:x.stations.find(d=>d.role===t.role),c=t.sla_minutes??void 0,u=t.target_per_hour??void 0,r=t.worker_count??void 0;return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex items-start justify-between mb-1",children:[e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsxs("div",{className:"flex items-baseline gap-1.5 flex-wrap",children:[e.jsx("span",{className:"text-sm font-mono font-bold text-text-primary",children:t.role}),t.title&&e.jsx("span",{className:"text-[10px] text-text-secondary",children:t.title})]}),t.description&&e.jsx("p",{className:"text-[10px] text-text-tertiary mt-0.5 line-clamp-2 leading-relaxed",children:t.description})]}),e.jsx("button",{onClick:l,className:"text-text-quaternary hover:text-text-secondary transition-colors mt-0.5 shrink-0 ml-2",children:e.jsx(ce,{className:"w-4 h-4"})})]}),e.jsxs(L,{to:`/admin/roles/${encodeURIComponent(t.role)}`,className:"flex items-center gap-1 text-[10px] text-accent hover:underline mb-4",children:[e.jsx(D,{className:"w-2.5 h-2.5"}),"Edit in Roles"]}),e.jsx(ve,{period:i,onChange:o}),(u||c||r)&&e.jsxs("div",{className:"flex items-center gap-4 mb-4 text-[10px]",children:[u&&e.jsxs("div",{children:[e.jsxs("div",{className:"font-mono font-semibold text-text-primary",children:[u,"/h"]}),e.jsx("div",{className:"text-text-quaternary uppercase tracking-wider text-[8.5px]",children:"target"})]}),c&&e.jsxs("div",{children:[e.jsxs("div",{className:"font-mono font-semibold text-text-primary",children:[c,"m"]}),e.jsx("div",{className:"text-text-quaternary uppercase tracking-wider text-[8.5px]",children:"SLA"})]}),r&&e.jsxs("div",{children:[e.jsx("div",{className:"font-mono font-semibold text-text-primary",children:r}),e.jsx("div",{className:"text-text-quaternary uppercase tracking-wider text-[8.5px]",children:"workers"})]})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-x-3",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-[9px] text-text-quaternary uppercase tracking-wider mb-1.5",children:"Wait (queue)"}),e.jsx(H,{label:"P99",value:T((a==null?void 0:a.wait.p99)??null)}),e.jsx(H,{label:"avg",value:T((a==null?void 0:a.wait.avg)??null)})]}),e.jsxs("div",{children:[e.jsx("p",{className:"text-[9px] text-text-quaternary uppercase tracking-wider mb-1.5",children:"Work (proc.)"}),e.jsx(H,{label:"P99",value:T((a==null?void 0:a.work.p99)??null)}),e.jsx(H,{label:"avg",value:T((a==null?void 0:a.work.avg)??null)})]})]}),e.jsxs("div",{className:"border-t border-surface-border/40 pt-2 mt-3 space-y-1.5",children:[c&&e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-tertiary",children:"SLA target"}),e.jsxs("span",{className:"text-xs font-mono text-text-secondary",children:[c,"m"]})]}),a&&a.in_arrears>0&&e.jsxs(L,{to:`/escalations/available?roles=${encodeURIComponent(JSON.stringify([a.role]))}&sort_by=created_at&order=asc`,className:"flex items-center justify-between group",children:[e.jsxs("span",{className:"text-[10px] text-red-500",children:[a.in_arrears," past SLA"]}),e.jsx(D,{className:"w-3 h-3 text-red-400 group-hover:text-red-500 transition-colors"})]})]}),e.jsx("div",{className:"mt-4 border-t border-surface-border/40 pt-3",children:e.jsxs(L,{to:`/escalations/available?roles=${encodeURIComponent(JSON.stringify([t.role]))}`,className:"flex items-center gap-1.5 text-[10px] text-accent hover:underline",children:["View full queue",e.jsx(D,{className:"w-2.5 h-2.5"})]})})]})}function ke({allMetrics:t,orderedRoles:n,period:l}){const i=t.reduce((r,d)=>r+d.pending,0),o=t.reduce((r,d)=>r+d.resolved,0),x=t.reduce((r,d)=>r+d.in_arrears,0),a=new Map(t.map(r=>[r.role,r])),c=t.filter(r=>r.in_arrears>0).length,u=t.filter(r=>r.pending>0).length;return e.jsxs(e.Fragment,{children:[e.jsxs("p",{className:"text-[9px] text-text-quaternary uppercase tracking-wider mb-3",children:["Pipeline · ",l]}),e.jsx("div",{className:"mb-4",children:c>0?e.jsxs("p",{className:"text-[11px] text-red-400",children:[c," station",c>1?"s":""," past SLA"]}):u>0?e.jsxs("p",{className:"text-[11px] text-amber-400",children:[u," station",u>1?"s":""," with backlog"]}):o>0?e.jsx("p",{className:"text-[11px] text-emerald-500",children:"Flowing — queue clear"}):e.jsx("p",{className:"text-[11px] text-text-quaternary",children:"Quiet — awaiting work"})}),e.jsxs("div",{className:"space-y-1.5 mb-4",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[9px] text-text-quaternary uppercase tracking-wider",children:"Pending"}),e.jsx("span",{className:`text-xs font-mono tabular-nums ${i>0?"text-text-primary font-semibold":"text-text-quaternary"}`,children:i})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("span",{className:"text-[9px] text-text-quaternary uppercase tracking-wider",children:["Resolved · ",l]}),e.jsx("span",{className:"text-xs font-mono tabular-nums text-text-secondary",children:o})]}),x>0&&e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[9px] text-text-quaternary uppercase tracking-wider",children:"Past SLA"}),e.jsx("span",{className:"text-xs font-mono tabular-nums text-red-500 font-semibold",children:x})]})]}),e.jsx("div",{className:"border-t border-surface-border/40 pt-2 mb-1",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-[8px] text-text-quaternary uppercase tracking-wider flex-1",children:"Station"}),e.jsx("span",{className:"text-[8px] text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0",children:"pend"}),e.jsx("span",{className:"text-[8px] text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0",children:"actv"}),e.jsx("span",{className:"text-[8px] text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0",children:"res"})]})}),e.jsx("div",{className:"space-y-0.5",children:n.map(r=>{const d=a.get(r.role),g=(d==null?void 0:d.pending)??0,y=(d==null?void 0:d.claimed)??0,m=(d==null?void 0:d.resolved)??0,f=(d==null?void 0:d.in_arrears)??0,S=r.title||r.role,j=f>0,k=g>0;return e.jsxs("div",{className:"flex items-center gap-2 py-0.5",children:[e.jsxs("span",{className:`text-[10px] font-mono flex-1 truncate ${j?"text-red-400":k?"text-text-primary":"text-text-quaternary"}`,children:[S,j&&e.jsx("span",{className:"ml-1 text-[8px] text-red-400 font-semibold",children:"SLA"})]}),e.jsx("span",{className:`text-[10px] font-mono tabular-nums w-8 text-right shrink-0 ${j?"text-red-400":k?"text-text-primary font-semibold":"text-text-quaternary"}`,children:g>0?g:"—"}),e.jsx("span",{className:`text-[10px] font-mono tabular-nums w-8 text-right shrink-0 ${y>0?"text-accent":"text-text-quaternary"}`,children:y>0?y:"—"}),e.jsx("span",{className:`text-[10px] font-mono tabular-nums w-8 text-right shrink-0 ${m>0?"text-text-secondary":"text-text-quaternary"}`,children:m>0?m:"—"})]},r.role)})}),e.jsx("p",{className:"text-[9px] text-text-quaternary mt-5",children:"Select a station for queue detail."})]})}function $e({role:t,allMetrics:n,orderedRoles:l,globalPeriod:i,onClose:o}){return e.jsx("div",{className:"w-[280px] shrink-0 px-6 py-8 flex flex-col overflow-y-auto min-h-0",children:t?e.jsx(Ne,{role:t,globalPeriod:i,onClose:o}):e.jsx(ke,{allMetrics:n,orderedRoles:l,period:i})})}const we=["15m","1h","24h","7d","30d"],Se={"15m":.25,"1h":1,"24h":24,"7d":168,"30d":720};function Me(t){const n=t.filter(a=>a.ops_visible),l=new Map(n.map(a=>[a.role,a])),i=[],o=new Set,x=n.filter(a=>!a.parent_role||!l.has(a.parent_role)).map(a=>({role:a,depth:0}));for(;x.length>0;){const a=x.shift();o.has(a.role.role)||(o.add(a.role.role),i.push(a),n.filter(c=>c.parent_role===a.role.role&&!o.has(c.role)).forEach(c=>x.push({role:c,depth:a.depth+1})))}return n.filter(a=>!o.has(a.role)).forEach(a=>i.push({role:a,depth:0})),i}function Z(t){if(t==null)return"—";if(t<1){const n=Math.round(t*60);return n<=0?"< 1s":`${n}s`}return t<60?`${t.toFixed(0)}m`:`${(t/60).toFixed(1)}h`}function Re(t,n,l,i){if(!n)return{pct:null,color:"bg-surface-border",historical:!1};if(t>0){const c=t/n,u=Math.round(c*100),r=c>1?"bg-amber-400":c<.2?"bg-orange-400":"bg-emerald-400";return{pct:u,color:r,historical:!1}}if(i===0||l==null)return{pct:null,color:"bg-surface-border",historical:!0};const o=l/100,x=Math.round(l),a=o>1?"bg-amber-400":"bg-surface-border";return{pct:x,color:a,historical:!0}}function _e({role:t,metric:n,selected:l,onClick:i}){const o=(n==null?void 0:n.pending)??0,x=(n==null?void 0:n.claimed)??0,a=(n==null?void 0:n.resolved)??0,c=(n==null?void 0:n.in_arrears)??0,u=t.target_per_hour??null,{pct:r,color:d,historical:g}=Re(o,u,(n==null?void 0:n.throughput_pct)??null,a),y=r!=null?Math.min(100,r):0;return e.jsxs(e.Fragment,{children:[e.jsxs("div",{role:"button",tabIndex:0,onClick:i,onKeyDown:m=>{m.key==="Enter"&&i()},className:`grid items-center gap-4 py-3.5 cursor-pointer transition-colors ${l?"border-l-2 border-accent":"pl-0.5"}`,style:{gridTemplateColumns:"1fr 64px 56px 56px 60px 72px 72px 104px"},children:[e.jsxs("div",{className:"flex items-center gap-1.5 min-w-0",children:[e.jsx("span",{className:"font-mono font-bold text-text-primary truncate text-[11px]",children:t.role}),t.title&&e.jsx("span",{className:"text-[10px] text-text-tertiary truncate",children:t.title})]}),e.jsx("span",{className:`text-xs font-mono tabular-nums text-right ${u!=null?"text-text-secondary":"text-text-quaternary"}`,children:u??"—"}),e.jsx("span",{className:`text-xs font-mono tabular-nums text-right ${o>0?"text-text-primary font-semibold":"text-text-quaternary"}`,children:o}),e.jsx("span",{className:`text-xs font-mono tabular-nums text-right ${x>0?"text-accent font-semibold":"text-text-quaternary"}`,children:x}),e.jsx("span",{className:"text-xs font-mono tabular-nums text-right text-text-quaternary",children:a}),e.jsx("span",{className:"text-xs font-mono tabular-nums text-right text-text-secondary",children:Z((n==null?void 0:n.wait.p99)??null)}),e.jsx("span",{className:"text-xs font-mono tabular-nums text-right text-text-secondary",children:Z((n==null?void 0:n.work.p99)??null)}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("div",{className:"w-12 h-1.5 bg-surface-sunken rounded-full overflow-hidden shrink-0",children:e.jsx("div",{className:`h-full rounded-full ${d}`,style:{width:`${y}%`}})}),e.jsx("span",{className:`text-[10px] font-mono tabular-nums ${r!=null&&r>100?"text-amber-500 font-semibold":"text-text-quaternary"}`,title:g?"Throughput efficiency for the selected period":void 0,children:r!=null?`${r}%${g?"↩":""}`:"—"})]})]}),c>0&&e.jsxs("div",{className:"flex items-center gap-1.5 pb-2 pl-1",children:[e.jsx(de,{className:"w-3 h-3 text-red-400 shrink-0"}),e.jsxs(L,{to:`/escalations/available?roles=${encodeURIComponent(JSON.stringify([t.role]))}&sort_by=created_at&order=asc`,className:"text-[10px] text-red-400 hover:text-red-500 hover:underline",onClick:m=>m.stopPropagation(),children:[c," past SLA — view oldest first →"]})]})]})}function qe(){const t=["ROLE","TARGET/H","PENDING","ACTIVE","RESOLVED","P99 WAIT","P99 WORK","TREND"];return e.jsx("div",{className:"grid items-center gap-4 py-1.5 border-b border-surface-border mb-0.5",style:{gridTemplateColumns:"1fr 64px 56px 56px 60px 72px 72px 104px"},children:t.map((n,l)=>e.jsx("span",{className:`text-[9px] font-semibold uppercase tracking-wider text-text-quaternary ${l>0&&l<7?"text-right":""}`,children:n},n))})}function Oe(){var M;const[t,n]=R.useState("24h"),[l,i]=R.useState(null),[o,x]=R.useState(!1);oe();const{data:a,isLoading:c,refetch:u}=le(),{data:r,isLoading:d,refetch:g}=ee(t),y=(a==null?void 0:a.roles)??[],m=(r==null?void 0:r.stations)??[],f=R.useMemo(()=>Me(y),[y]),S=R.useMemo(()=>f.map(({role:p})=>({role:p.role,title:p.title,parent_role:p.parent_role,target_per_hour:p.target_per_hour??null,metric:m.find($=>$.role===p.role)})),[f,m]),j=((M=f.find(({role:p})=>p.role===l))==null?void 0:M.role)??null,k=c||d,C=async()=>{x(!0),await Promise.all([u(),g()]),x(!1)},_=p=>i($=>$===p?null:p),E=e.jsx("div",{className:"flex items-center gap-0.5",children:we.map(p=>e.jsx("button",{onClick:()=>n(p),className:`px-2.5 py-1 text-[10px] font-mono rounded transition-colors ${t===p?"bg-accent/10 text-accent font-semibold":"text-text-quaternary hover:text-text-secondary"}`,children:p},p))});return e.jsxs("div",{className:"flex flex-col flex-1 min-h-0",children:[e.jsx(ie,{title:"Operations",docsHash:"#docs:dashboard.md:operations",center:E,actions:e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("button",{onClick:C,disabled:o,className:"text-text-quaternary hover:text-text-secondary transition-colors disabled:opacity-40",children:e.jsx(xe,{className:`w-3.5 h-3.5 ${o?"animate-spin":""}`})}),e.jsx(L,{to:"/admin/roles",className:"btn-secondary text-xs",children:"Configure"})]})}),k?e.jsxs("div",{className:"animate-pulse space-y-6 mt-4",children:[e.jsx("div",{className:"h-8 bg-surface-sunken rounded w-64"}),e.jsx("div",{className:"h-64 bg-surface-sunken rounded w-full"}),e.jsx("div",{className:"space-y-3",children:[1,2,3].map(p=>e.jsx("div",{className:"h-8 bg-surface-sunken rounded w-full"},p))})]}):f.length===0?e.jsxs("div",{className:"mt-8",children:[e.jsx("p",{className:"text-sm text-text-secondary mb-2",children:"Stations appear here once roles are marked visible in Operations."}),e.jsxs("p",{className:"text-xs text-text-tertiary",children:["Go to"," ",e.jsx(L,{to:"/admin/roles",className:"text-accent hover:underline",children:"Roles"})," ","and enable ",e.jsx("strong",{children:"Visible in Operations"})," on roles that represent pipeline stations."]})]}):e.jsxs("div",{className:"flex flex-col flex-1 min-h-0",children:[e.jsxs("div",{className:"flex-1 min-h-0 flex items-stretch overflow-hidden",children:[e.jsx("div",{className:"flex-1 min-w-0 min-h-0 flex flex-col justify-center overflow-hidden p-4",children:e.jsx(ge,{stations:S,selectedRole:l,onSelect:_,periodHours:Se[t]})}),e.jsx("div",{className:"w-px bg-surface-border shrink-0 self-stretch"}),e.jsx($e,{role:j,allMetrics:m,orderedRoles:f.map(p=>p.role),globalPeriod:t,onClose:()=>i(null)})]}),e.jsxs("div",{className:"h-[30vh] flex-none flex flex-col border-t border-surface-border overflow-hidden",children:[e.jsx(qe,{}),e.jsx("div",{className:"flex-1 min-h-0 overflow-y-auto divide-y divide-surface-border/30",children:f.map(({role:p})=>e.jsx(_e,{role:p,metric:m.find($=>$.role===p.role),selected:l===p.role,onClick:()=>_(p.role)},p.role))})]})]})]})}export{Oe as OperationsPage};
2
- //# sourceMappingURL=OperationsPage-CAmiFHI6.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"OperationsPage-CAmiFHI6.js","sources":["../../src/pages/operations/PaceChart.tsx","../../src/pages/operations/StationDetailPanel.tsx","../../src/pages/operations/OperationsPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport type { StationMetric } from '../../api/escalations';\n\nexport interface ChartStation {\n role: string;\n title: string | null;\n target_per_hour: number | null;\n parent_role: string | null;\n metric: StationMetric | undefined;\n}\n\ninterface PaceChartProps {\n stations: ChartStation[];\n selectedRole: string | null;\n onSelect: (role: string) => void;\n /** Selected window length in hours — target count = target_per_hour × this. */\n periodHours: number;\n}\n\n// ── SVG layout ────────────────────────────────────────────────────────────────\n\nconst W = 800;\nconst H = 270;\nconst ML = 40;\nconst MR = 76;\nconst MT = 24;\nconst MB = 72;\n\nconst chartW = W - ML - MR;\nconst chartH = H - MT - MB;\nconst bottom = MT + chartH;\nconst right = ML + chartW;\n\n// Smooth transition applied when the window changes and values re-scale.\nconst EASE = '0.5s cubic-bezier(0.4, 0, 0.2, 1)';\n\n// ── Path builders ─────────────────────────────────────────────────────────────\n\n/** Straight polyline — the target line is a discrete count per station. */\nfunction linePath(pts: { x: number; y: number }[]): string {\n if (pts.length === 0) return '';\n return pts.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x.toFixed(1)} ${p.y.toFixed(1)}`).join(' ');\n}\n\n/** Catmull-Rom → cubic bezier — smooth curves for the actual and active lines. */\nfunction catmullPath(pts: { x: number; y: number }[]): string {\n if (pts.length === 0) return '';\n if (pts.length === 1) return `M ${pts[0].x} ${pts[0].y}`;\n const segs: string[] = [];\n for (let i = 0; i < pts.length - 1; i++) {\n const p0 = pts[Math.max(0, i - 1)];\n const p1 = pts[i];\n const p2 = pts[i + 1];\n const p3 = pts[Math.min(pts.length - 1, i + 2)];\n const cp1x = (p1.x + (p2.x - p0.x) / 6).toFixed(1);\n const cp1y = (p1.y + (p2.y - p0.y) / 6).toFixed(1);\n const cp2x = (p2.x - (p3.x - p1.x) / 6).toFixed(1);\n const cp2y = (p2.y - (p3.y - p1.y) / 6).toFixed(1);\n if (i === 0) segs.push(`M ${p1.x.toFixed(1)} ${p1.y.toFixed(1)}`);\n segs.push(`C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${p2.x.toFixed(1)} ${p2.y.toFixed(1)}`);\n }\n return segs.join(' ');\n}\n\nfunction compact(n: number): string {\n return n >= 10000 ? `${Math.round(n / 1000)}k` : n >= 1000 ? `${(n / 1000).toFixed(1)}k` : String(n);\n}\n\nfunction last<T>(a: T[]): T | undefined {\n return a.length > 0 ? a[a.length - 1] : undefined;\n}\n\n// ── Colors ────────────────────────────────────────────────────────────────────\n\n/** Circle color from actual vs the window target. Meeting or beating the target\n * is good (green); short is the concern (amber → red). */\nfunction paceColor(ratio: number | null): { stroke: string; fill: string } {\n if (ratio == null) return { stroke: '#cbd5e1', fill: '#f8fafc' };\n if (ratio >= 1.0) return { stroke: '#10b981', fill: '#ecfdf5' }; // met/beat target\n if (ratio >= 0.6) return { stroke: '#f59e0b', fill: '#fffbeb' }; // behind\n return { stroke: '#ef4444', fill: '#fef2f2' }; // well behind\n}\n\nconst ACTIVE_COLOR = '#6366f1'; // active/in-queue — indigo, secondary\n\n// ── End-label stacking — spread close labels so text doesn't collide ────────────\n\nconst MIN_GAP = 13;\nfunction spreadLabels<T extends { y: number }>(labels: T[]): (T & { labelY: number })[] {\n const sorted = [...labels].sort((a, b) => a.y - b.y);\n let prev = -Infinity;\n return sorted.map((l) => {\n const labelY = Math.max(l.y + 3, prev + MIN_GAP);\n prev = labelY;\n return { ...l, labelY };\n });\n}\n\n// ── Chart ─────────────────────────────────────────────────────────────────────\n\nexport function PaceChart({ stations, selectedRole, onSelect, periodHours }: PaceChartProps) {\n const [hoveredIdx, setHoveredIdx] = useState<number | null>(null);\n\n const n = stations.length;\n if (n === 0) return null;\n\n const xOf = (i: number) => (n === 1 ? ML + chartW / 2 : ML + (i / (n - 1)) * chartW);\n const xMap = new Map(stations.map((s, i) => [s.role, xOf(i)]));\n\n // Per-station counts for the selected window:\n // - target : expected units = target_per_hour × periodHours (e.g. 22/h · 15m ≈ 5).\n // - actual : units resolved in the window (\"the number we see\").\n // - active : units in the queue being worked right now (claimed).\n const rows = stations.map((s, i) => {\n const target = s.target_per_hour ?? null;\n const expected = target != null ? target * periodHours : null;\n return {\n idx: i,\n x: xOf(i),\n expected,\n actual: s.metric?.resolved ?? 0,\n active: s.metric?.claimed ?? 0,\n pending: s.metric?.pending ?? 0,\n inArrears: s.metric?.in_arrears ?? 0,\n };\n });\n\n const hasTargets = rows.some((r) => r.expected != null);\n\n const dataMax = Math.max(\n 1,\n ...rows.map((r) => r.expected ?? 0),\n ...rows.map((r) => r.actual),\n ...rows.map((r) => (r.expected != null ? r.active : 0)),\n );\n const maxVal = dataMax * 1.15;\n const yScale = (v: number) => MT + chartH * (1 - Math.max(0, Math.min(v, maxVal)) / maxVal);\n\n const withTarget = rows.filter((r) => r.expected != null);\n const targetPts = withTarget.map((r) => ({ ...r, y: yScale(r.expected as number) }));\n const actualPts = withTarget.map((r) => ({ ...r, y: yScale(r.actual) }));\n const activePts = withTarget.map((r) => ({ ...r, y: yScale(r.active) }));\n\n const targetLinePath = linePath(targetPts);\n const actualSplinePath = catmullPath(actualPts);\n const activeSplinePath = catmullPath(activePts);\n\n const lastActual = last(actualPts);\n const lastTarget = last(targetPts);\n const lastActive = last(activePts);\n\n const areaPath =\n actualPts.length >= 2 && lastActual\n ? `${actualSplinePath} L ${lastActual.x.toFixed(1)} ${bottom} L ${actualPts[0].x.toFixed(1)} ${bottom} Z`\n : '';\n\n const endLabels = spreadLabels(\n [\n lastTarget ? { key: 'target', text: 'target', color: '#ef4444', y: lastTarget.y } : null,\n lastActual ? { key: 'actual', text: 'actual', color: '#475569', y: lastActual.y } : null,\n lastActive ? { key: 'active', text: 'active', color: ACTIVE_COLOR, y: lastActive.y } : null,\n ].filter(Boolean) as { key: string; text: string; color: string; y: number }[],\n );\n\n return (\n <svg viewBox={`0 0 ${W} ${H}`} className=\"w-full h-full\" preserveAspectRatio=\"xMidYMid meet\" style={{ display: 'block' }}>\n {/* Dependency connector lines (parent → child), drawn at chart floor */}\n {stations.map((s) => {\n if (!s.parent_role) return null;\n const px = xMap.get(s.parent_role);\n if (px == null) return null;\n const cx = xOf(stations.findIndex((r) => r.role === s.role));\n return <line key={`dep-${s.role}`} x1={px} y1={bottom} x2={cx} y2={bottom} stroke=\"#e2e8f0\" strokeWidth={0.75} />;\n })}\n\n {/* Area under the actual curve */}\n {areaPath && <path d={areaPath} fill=\"rgb(71 85 105 / 0.05)\" style={{ transition: `d ${EASE}` }} />}\n\n {/* Target — thin red solid reference at the window's expected count */}\n {targetLinePath && (\n <path d={targetLinePath} fill=\"none\" stroke=\"#ef4444\" strokeWidth={0.5} strokeLinejoin=\"round\" opacity={0.9} style={{ transition: `d ${EASE}` }} />\n )}\n\n {/* Per-role target for the window (target_per_hour × duration), just above the red line */}\n {targetPts.map((tp) => (\n <text\n key={`tval-${tp.idx}`}\n x={tp.x}\n y={tp.y - 3.5}\n textAnchor=\"middle\"\n fontSize={6.5}\n fill=\"#ef4444\"\n fontFamily=\"ui-monospace, monospace\"\n fontWeight=\"500\"\n opacity={0.7}\n style={{ transition: `y ${EASE}` }}\n >\n {Math.round(tp.expected as number)}\n </text>\n ))}\n\n {/* Active — thin dotted, how many are in the queue right now */}\n {activeSplinePath && (\n <path d={activeSplinePath} fill=\"none\" stroke={ACTIVE_COLOR} strokeWidth={0.75} strokeDasharray=\"2 3\" strokeLinecap=\"round\" opacity={0.5} style={{ transition: `d ${EASE}` }} />\n )}\n\n {/* Actual — thin solid, primary line */}\n {actualSplinePath && (\n <path d={actualSplinePath} fill=\"none\" stroke=\"#475569\" strokeWidth={1} strokeLinejoin=\"round\" strokeLinecap=\"round\" opacity={0.7} style={{ transition: `d ${EASE}` }} />\n )}\n\n {/* Active markers — small dots on the active line.\n Above the dot: pending (the total in the queue, available + claimed).\n Below the dot: active (claimed, being worked right now). */}\n {activePts.map((ap) => {\n if (ap.pending <= 0 && ap.active <= 0) return null;\n const ar = Math.max(2.5, Math.min(5, 2.5 + ap.active / 10));\n return (\n <g key={`active-${ap.idx}`} transform={`translate(${ap.x} ${ap.y})`} style={{ transition: `transform ${EASE}` }}>\n <circle r={ar} fill={ACTIVE_COLOR} opacity={0.85} />\n {ap.pending > 0 && (\n <text y={-ar - 4} textAnchor=\"middle\" fontSize={7.5} fill=\"#64748b\" fontFamily=\"ui-monospace, monospace\" fontWeight=\"600\">\n {ap.pending}\n </text>\n )}\n {ap.active > 0 && (\n <text y={ar + 9} textAnchor=\"middle\" fontSize={7.5} fill={ACTIVE_COLOR} fontFamily=\"ui-monospace, monospace\" fontWeight=\"600\">\n {ap.active}\n </text>\n )}\n </g>\n );\n })}\n\n {/* Station nodes — actual (resolved) as a dot on the actual line, styled like\n the active dot but a bit larger (actual is the most important number).\n The count sits below the dot so it stays legible as numbers grow. */}\n {rows.map((row) => {\n const s = stations[row.idx];\n const x = row.x;\n const cy = row.expected != null ? yScale(row.actual) : bottom;\n const ratio = row.expected && row.expected > 0 ? row.actual / row.expected : null;\n const { stroke } = paceColor(row.expected != null ? ratio : null);\n // Same small scale as the active dot — a hair larger.\n const r = Math.max(3, Math.min(5.5, 3 + row.actual / 120));\n const isSelected = s.role === selectedRole;\n const isHovered = hoveredIdx === row.idx;\n\n const tooltip =\n row.expected == null\n ? 'idle · set a target rate to plot pace'\n : `${row.actual} done · target ${Math.round(row.expected)}`\n + (row.pending > 0 ? ` · ${row.pending} in queue` : '')\n + (row.active > 0 ? ` · ${row.active} active` : '');\n const tipW = tooltip.length * 5.5 + 20;\n const tipX = Math.max(ML + tipW / 2 + 4, Math.min(right - tipW / 2 - 4, x));\n const tipY = cy - r - 10;\n\n return (\n <g\n key={s.role}\n onClick={() => onSelect(s.role)}\n onMouseEnter={() => setHoveredIdx(row.idx)}\n onMouseLeave={() => setHoveredIdx(null)}\n style={{ cursor: 'pointer' }}\n >\n {/* Animated marker group — slides vertically as the window rescales */}\n <g transform={`translate(${x} ${cy})`} style={{ transition: `transform ${EASE}` }}>\n <circle r={22} fill=\"transparent\" />\n {row.inArrears > 0 && (\n <circle r={r + 4} fill=\"none\" stroke=\"#ef4444\" strokeWidth={1} strokeDasharray=\"3 2\" opacity={0.6} />\n )}\n <circle r={r} fill={stroke} opacity={0.9} style={{ transition: `r ${EASE}` }} />\n {row.expected != null && (\n <text y={-r - 5} textAnchor=\"middle\" fontSize={9.5} fill=\"#334155\" fontFamily=\"ui-monospace, monospace\" fontWeight=\"500\">\n {compact(row.actual)}\n </text>\n )}\n {isHovered && !isSelected && <circle r={r + 3} fill=\"none\" stroke={stroke} strokeWidth={1} opacity={0.4} />}\n {isSelected && <circle r={r + 4} fill=\"none\" stroke=\"#6366f1\" strokeWidth={2} />}\n </g>\n\n {/* Station labels (fixed at the floor) */}\n <text x={x} y={bottom + 14} textAnchor=\"middle\" fontSize={9.5} fill=\"#475569\" fontFamily=\"ui-sans-serif, sans-serif\" fontWeight=\"500\">\n {s.title ?? s.role}\n </text>\n <text x={x} y={bottom + 26} textAnchor=\"middle\" fontSize={8} fill=\"#94a3b8\" fontFamily=\"ui-monospace, monospace\">\n {s.role}\n </text>\n\n {/* Tooltip */}\n {isHovered && (\n <g>\n <rect x={tipX - tipW / 2} y={tipY - 16} width={tipW} height={19} rx={3} fill=\"#0f172a\" opacity={0.88} />\n <text x={tipX} y={tipY - 3} textAnchor=\"middle\" fontSize={8.5} fill=\"white\" fontFamily=\"ui-monospace, monospace\">\n {tooltip}\n </text>\n </g>\n )}\n </g>\n );\n })}\n\n {/* No-target hint */}\n {!hasTargets && (\n <text x={ML + chartW / 2} y={bottom + 48} textAnchor=\"middle\" fontSize={8.5} fill=\"#94a3b8\" fontFamily=\"ui-sans-serif, sans-serif\">\n Set target_per_hour on each role in Admin → Roles to enable the pace chart\n </text>\n )}\n\n {/* End labels — text + count, rendered last so they sit above everything */}\n {endLabels.map((l) => (\n <text\n key={l.key}\n x={right + 6}\n y={l.labelY}\n fontSize={8}\n fill={l.color}\n fontFamily=\"ui-sans-serif, sans-serif\"\n fontWeight={l.key === 'actual' ? 700 : 500}\n opacity={l.key === 'active' ? 0.85 : 1}\n style={{ transition: `y ${EASE}` }}\n >\n {l.text}\n </text>\n ))}\n </svg>\n );\n}\n","import { useState } from 'react';\nimport { Link } from 'react-router-dom';\nimport { X, ExternalLink } from 'lucide-react';\nimport { useStationMetrics } from '../../api/escalations';\nimport type { StationMetric } from '../../api/escalations';\nimport type { RoleDetail } from '../../api/roles';\n\ninterface StationDetailPanelProps {\n role: RoleDetail | null;\n allMetrics: StationMetric[];\n orderedRoles: RoleDetail[];\n globalPeriod: string;\n onClose: () => void;\n}\n\n// Mirrors OperationsPage's selector — the panel reports the same windows the\n// chart can show, and opens on whichever one the chart has selected.\nconst PERIODS = ['15m', '1h', '24h', '7d', '30d'] as const;\ntype Period = (typeof PERIODS)[number];\n\nconst isPeriod = (p: string): p is Period => (PERIODS as readonly string[]).includes(p);\n\nfunction fmt(min: number | null): string {\n if (min == null) return '—';\n if (min < 1) {\n const s = Math.round(min * 60);\n return s <= 0 ? '< 1s' : `${s}s`;\n }\n if (min < 60) return `${min.toFixed(0)}m`;\n return `${(min / 60).toFixed(1)}h`;\n}\n\nfunction MetricRow({ label, value }: { label: string; value: string }) {\n return (\n <div className=\"flex items-center justify-between py-1\">\n <span className=\"text-[10px] text-text-tertiary uppercase tracking-wider\">{label}</span>\n <span className=\"text-xs font-mono font-medium text-text-primary tabular-nums\">{value}</span>\n </div>\n );\n}\n\nfunction PeriodSelector({ period, onChange }: { period: Period; onChange: (p: Period) => void }) {\n return (\n <div className=\"flex items-center gap-1 mb-4\">\n {PERIODS.map((p) => (\n <button\n key={p}\n onClick={() => onChange(p)}\n className={`px-2 py-0.5 text-[10px] font-mono rounded transition-colors ${\n period === p\n ? 'bg-accent/10 text-accent font-semibold'\n : 'text-text-quaternary hover:text-text-secondary'\n }`}\n >\n {p}\n </button>\n ))}\n </div>\n );\n}\n\nfunction RoleView({ role, globalPeriod, onClose }: { role: RoleDetail; globalPeriod: string; onClose: () => void }) {\n // Open on the chart's selected window so the panel's numbers agree with the\n // chart beside it; the local selector still lets the viewer drill around.\n const [period, setPeriod] = useState<Period>(isPeriod(globalPeriod) ? globalPeriod : '24h');\n const { data } = useStationMetrics(period);\n const metric = data?.stations.find((s) => s.role === role.role);\n const slaMinutes = role.sla_minutes ?? undefined;\n const targetPerHour = role.target_per_hour ?? undefined;\n const workerCount = role.worker_count ?? undefined;\n\n return (\n <>\n {/* Header */}\n <div className=\"flex items-start justify-between mb-1\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-baseline gap-1.5 flex-wrap\">\n <span className=\"text-sm font-mono font-bold text-text-primary\">{role.role}</span>\n {role.title && (\n <span className=\"text-[10px] text-text-secondary\">{role.title}</span>\n )}\n </div>\n {role.description && (\n <p className=\"text-[10px] text-text-tertiary mt-0.5 line-clamp-2 leading-relaxed\">\n {role.description}\n </p>\n )}\n </div>\n <button\n onClick={onClose}\n className=\"text-text-quaternary hover:text-text-secondary transition-colors mt-0.5 shrink-0 ml-2\"\n >\n <X className=\"w-4 h-4\" />\n </button>\n </div>\n\n <Link\n to={`/admin/roles/${encodeURIComponent(role.role)}`}\n className=\"flex items-center gap-1 text-[10px] text-accent hover:underline mb-4\"\n >\n <ExternalLink className=\"w-2.5 h-2.5\" />\n Edit in Roles\n </Link>\n\n <PeriodSelector period={period} onChange={setPeriod} />\n\n {/* Capacity settings */}\n {(targetPerHour || slaMinutes || workerCount) && (\n <div className=\"flex items-center gap-4 mb-4 text-[10px]\">\n {targetPerHour && (\n <div>\n <div className=\"font-mono font-semibold text-text-primary\">{targetPerHour}/h</div>\n <div className=\"text-text-quaternary uppercase tracking-wider text-[8.5px]\">target</div>\n </div>\n )}\n {slaMinutes && (\n <div>\n <div className=\"font-mono font-semibold text-text-primary\">{slaMinutes}m</div>\n <div className=\"text-text-quaternary uppercase tracking-wider text-[8.5px]\">SLA</div>\n </div>\n )}\n {workerCount && (\n <div>\n <div className=\"font-mono font-semibold text-text-primary\">{workerCount}</div>\n <div className=\"text-text-quaternary uppercase tracking-wider text-[8.5px]\">workers</div>\n </div>\n )}\n </div>\n )}\n\n {/* Two-column wait / work grid */}\n <div className=\"grid grid-cols-2 gap-x-3\">\n <div>\n <p className=\"text-[9px] text-text-quaternary uppercase tracking-wider mb-1.5\">Wait (queue)</p>\n <MetricRow label=\"P99\" value={fmt(metric?.wait.p99 ?? null)} />\n <MetricRow label=\"avg\" value={fmt(metric?.wait.avg ?? null)} />\n </div>\n <div>\n <p className=\"text-[9px] text-text-quaternary uppercase tracking-wider mb-1.5\">Work (proc.)</p>\n <MetricRow label=\"P99\" value={fmt(metric?.work.p99 ?? null)} />\n <MetricRow label=\"avg\" value={fmt(metric?.work.avg ?? null)} />\n </div>\n </div>\n\n {/* SLA + arrears */}\n <div className=\"border-t border-surface-border/40 pt-2 mt-3 space-y-1.5\">\n {slaMinutes && (\n <div className=\"flex items-center justify-between\">\n <span className=\"text-[10px] text-text-tertiary\">SLA target</span>\n <span className=\"text-xs font-mono text-text-secondary\">{slaMinutes}m</span>\n </div>\n )}\n {metric && metric.in_arrears > 0 && (\n <Link\n to={`/escalations/available?roles=${encodeURIComponent(JSON.stringify([metric.role]))}&sort_by=created_at&order=asc`}\n className=\"flex items-center justify-between group\"\n >\n <span className=\"text-[10px] text-red-500\">{metric.in_arrears} past SLA</span>\n <ExternalLink className=\"w-3 h-3 text-red-400 group-hover:text-red-500 transition-colors\" />\n </Link>\n )}\n </div>\n\n {/* View queue — the facet URL parser reads `roles` as a JSON array; a\n bare `role=` param is ignored and would show every role's queue. */}\n <div className=\"mt-4 border-t border-surface-border/40 pt-3\">\n <Link\n to={`/escalations/available?roles=${encodeURIComponent(JSON.stringify([role.role]))}`}\n className=\"flex items-center gap-1.5 text-[10px] text-accent hover:underline\"\n >\n View full queue\n <ExternalLink className=\"w-2.5 h-2.5\" />\n </Link>\n </div>\n </>\n );\n}\n\nfunction OverviewPanel({\n allMetrics,\n orderedRoles,\n period,\n}: {\n allMetrics: StationMetric[];\n orderedRoles: RoleDetail[];\n period: string;\n}) {\n const totalPending = allMetrics.reduce((s, m) => s + m.pending, 0);\n const totalResolved = allMetrics.reduce((s, m) => s + m.resolved, 0);\n const totalArrears = allMetrics.reduce((s, m) => s + m.in_arrears, 0);\n\n const metricByRole = new Map(allMetrics.map((m) => [m.role, m]));\n const stationsAtRisk = allMetrics.filter((m) => m.in_arrears > 0).length;\n const stationsWithLoad = allMetrics.filter((m) => m.pending > 0).length;\n\n return (\n <>\n <p className=\"text-[9px] text-text-quaternary uppercase tracking-wider mb-3\">\n Pipeline · {period}\n </p>\n\n {/* Health headline */}\n <div className=\"mb-4\">\n {stationsAtRisk > 0 ? (\n <p className=\"text-[11px] text-red-400\">\n {stationsAtRisk} station{stationsAtRisk > 1 ? 's' : ''} past SLA\n </p>\n ) : stationsWithLoad > 0 ? (\n <p className=\"text-[11px] text-amber-400\">\n {stationsWithLoad} station{stationsWithLoad > 1 ? 's' : ''} with backlog\n </p>\n ) : totalResolved > 0 ? (\n <p className=\"text-[11px] text-emerald-500\">Flowing — queue clear</p>\n ) : (\n <p className=\"text-[11px] text-text-quaternary\">Quiet — awaiting work</p>\n )}\n </div>\n\n {/* Key counters */}\n <div className=\"space-y-1.5 mb-4\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-[9px] text-text-quaternary uppercase tracking-wider\">Pending</span>\n <span className={`text-xs font-mono tabular-nums ${totalPending > 0 ? 'text-text-primary font-semibold' : 'text-text-quaternary'}`}>\n {totalPending}\n </span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-[9px] text-text-quaternary uppercase tracking-wider\">Resolved · {period}</span>\n <span className=\"text-xs font-mono tabular-nums text-text-secondary\">{totalResolved}</span>\n </div>\n {totalArrears > 0 && (\n <div className=\"flex items-center justify-between\">\n <span className=\"text-[9px] text-text-quaternary uppercase tracking-wider\">Past SLA</span>\n <span className=\"text-xs font-mono tabular-nums text-red-500 font-semibold\">{totalArrears}</span>\n </div>\n )}\n </div>\n\n {/* Divider + column headers */}\n <div className=\"border-t border-surface-border/40 pt-2 mb-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-[8px] text-text-quaternary uppercase tracking-wider flex-1\">Station</span>\n <span className=\"text-[8px] text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0\">pend</span>\n <span className=\"text-[8px] text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0\">actv</span>\n <span className=\"text-[8px] text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0\">res</span>\n </div>\n </div>\n\n {/* Station list in pipeline order */}\n <div className=\"space-y-0.5\">\n {orderedRoles.map((r) => {\n const m = metricByRole.get(r.role);\n const pending = m?.pending ?? 0;\n const claimed = m?.claimed ?? 0;\n const resolved = m?.resolved ?? 0;\n const inArrears = m?.in_arrears ?? 0;\n const label = r.title || r.role;\n const hasAlert = inArrears > 0;\n const hasLoad = pending > 0;\n\n return (\n <div key={r.role} className=\"flex items-center gap-2 py-0.5\">\n <span\n className={`text-[10px] font-mono flex-1 truncate ${\n hasAlert ? 'text-red-400' : hasLoad ? 'text-text-primary' : 'text-text-quaternary'\n }`}\n >\n {label}\n {hasAlert && (\n <span className=\"ml-1 text-[8px] text-red-400 font-semibold\">SLA</span>\n )}\n </span>\n <span\n className={`text-[10px] font-mono tabular-nums w-8 text-right shrink-0 ${\n hasAlert ? 'text-red-400' : hasLoad ? 'text-text-primary font-semibold' : 'text-text-quaternary'\n }`}\n >\n {pending > 0 ? pending : '—'}\n </span>\n <span className={`text-[10px] font-mono tabular-nums w-8 text-right shrink-0 ${claimed > 0 ? 'text-accent' : 'text-text-quaternary'}`}>\n {claimed > 0 ? claimed : '—'}\n </span>\n <span className={`text-[10px] font-mono tabular-nums w-8 text-right shrink-0 ${resolved > 0 ? 'text-text-secondary' : 'text-text-quaternary'}`}>\n {resolved > 0 ? resolved : '—'}\n </span>\n </div>\n );\n })}\n </div>\n\n <p className=\"text-[9px] text-text-quaternary mt-5\">\n Select a station for queue detail.\n </p>\n </>\n );\n}\n\nexport function StationDetailPanel({\n role,\n allMetrics,\n orderedRoles,\n globalPeriod,\n onClose,\n}: StationDetailPanelProps) {\n return (\n <div className=\"w-[280px] shrink-0 px-6 py-8 flex flex-col overflow-y-auto min-h-0\">\n {role ? (\n <RoleView role={role} globalPeriod={globalPeriod} onClose={onClose} />\n ) : (\n <OverviewPanel allMetrics={allMetrics} orderedRoles={orderedRoles} period={globalPeriod} />\n )}\n </div>\n );\n}\n","import { useState, useMemo } from 'react';\nimport { Link } from 'react-router-dom';\nimport { AlertTriangle, RefreshCw } from 'lucide-react';\nimport { useRoleDetails, type RoleDetail } from '../../api/roles';\nimport { useStationMetrics } from '../../api/escalations';\nimport type { StationMetric } from '../../api/escalations';\nimport { useStationMetricsEvents } from '../../hooks/useEventHooks';\nimport { PageHeader } from '../../components/common/layout/PageHeader';\nimport { PaceChart, type ChartStation } from './PaceChart';\nimport { StationDetailPanel } from './StationDetailPanel';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nconst PERIODS = ['15m', '1h', '24h', '7d', '30d'] as const;\ntype Period = (typeof PERIODS)[number];\n\n// Window length in hours — used to express the target as a count for the\n// selected window (target_per_hour × hours), e.g. 22/h over 15m ≈ 5 expected.\nconst PERIOD_HOURS: Record<Period, number> = {\n '15m': 0.25,\n '1h': 1,\n '24h': 24,\n '7d': 168,\n '30d': 720,\n};\n\ninterface OrderedStation {\n role: RoleDetail;\n depth: number;\n}\n\n// ── Topological sort — BFS, tracking actual depth ─────────────────────────────\n\nfunction topoSort(roles: RoleDetail[]): OrderedStation[] {\n const opsRoles = roles.filter((r) => r.ops_visible);\n const roleMap = new Map(opsRoles.map((r) => [r.role, r]));\n const result: OrderedStation[] = [];\n const visited = new Set<string>();\n\n // Roots: no parent, or parent not in ops set\n const queue: OrderedStation[] = opsRoles\n .filter((r) => !r.parent_role || !roleMap.has(r.parent_role))\n .map((r) => ({ role: r, depth: 0 }));\n\n while (queue.length > 0) {\n const item = queue.shift()!;\n if (visited.has(item.role.role)) continue;\n visited.add(item.role.role);\n result.push(item);\n // Children get depth + 1\n opsRoles\n .filter((c) => c.parent_role === item.role.role && !visited.has(c.role))\n .forEach((c) => queue.push({ role: c, depth: item.depth + 1 }));\n }\n\n // Any unreachable (cycles / dangling)\n opsRoles\n .filter((r) => !visited.has(r.role))\n .forEach((r) => result.push({ role: r, depth: 0 }));\n\n return result;\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction fmtMin(v: number | null): string {\n if (v == null) return '—';\n if (v < 1) {\n const s = Math.round(v * 60);\n return s <= 0 ? '< 1s' : `${s}s`;\n }\n if (v < 60) return `${v.toFixed(0)}m`;\n return `${(v / 60).toFixed(1)}h`;\n}\n\nfunction loadBar(\n pending: number,\n target: number | null,\n throughputPct: number | null,\n resolved: number,\n) {\n if (!target) return { pct: null, color: 'bg-surface-border', historical: false };\n\n if (pending > 0) {\n const ratio = pending / target;\n const pct = Math.round(ratio * 100);\n // Amber: backlog. Orange: some work but well under pace. Green: healthy.\n const color = ratio > 1.0 ? 'bg-amber-400' : ratio < 0.2 ? 'bg-orange-400' : 'bg-emerald-400';\n return { pct, color, historical: false };\n }\n\n // Idle: use period throughput efficiency rather than the live backlog ratio.\n // resolved=0 means nothing happened in this window → no data, not failing.\n if (resolved === 0 || throughputPct == null) {\n return { pct: null, color: 'bg-surface-border', historical: true };\n }\n const ratio = throughputPct / 100;\n const pct = Math.round(throughputPct);\n // Amber: above baseline (produced more than target). Neutral otherwise —\n // below-target throughput isn't a positive green signal, just normal data.\n const color = ratio > 1.0 ? 'bg-amber-400' : 'bg-surface-border';\n return { pct, color, historical: true };\n}\n\n// ── Station table row ─────────────────────────────────────────────────────────\n\nfunction StationRow({\n role,\n metric,\n selected,\n onClick,\n}: {\n role: RoleDetail;\n metric: StationMetric | undefined;\n selected: boolean;\n onClick: () => void;\n}) {\n const pending = metric?.pending ?? 0;\n const claimed = metric?.claimed ?? 0;\n const resolved = metric?.resolved ?? 0;\n const inArrears = metric?.in_arrears ?? 0;\n const target = role.target_per_hour ?? null;\n const { pct, color, historical } = loadBar(pending, target, metric?.throughput_pct ?? null, resolved);\n const barWidth = pct != null ? Math.min(100, pct) : 0;\n\n return (\n <>\n <div\n role=\"button\"\n tabIndex={0}\n onClick={onClick}\n onKeyDown={(e) => { if (e.key === 'Enter') onClick(); }}\n className={`grid items-center gap-4 py-3.5 cursor-pointer transition-colors ${\n selected ? 'border-l-2 border-accent' : 'pl-0.5'\n }`}\n style={{ gridTemplateColumns: '1fr 64px 56px 56px 60px 72px 72px 104px' }}\n >\n {/* Role name */}\n <div className=\"flex items-center gap-1.5 min-w-0\">\n <span\n className=\"font-mono font-bold text-text-primary truncate text-[11px]\"\n >\n {role.role}\n </span>\n {role.title && (\n <span className=\"text-[10px] text-text-tertiary truncate\">{role.title}</span>\n )}\n </div>\n\n {/* Target / hour */}\n <span\n className={`text-xs font-mono tabular-nums text-right ${\n target != null ? 'text-text-secondary' : 'text-text-quaternary'\n }`}\n >\n {target != null ? target : '—'}\n </span>\n\n {/* Pending */}\n <span\n className={`text-xs font-mono tabular-nums text-right ${\n pending > 0 ? 'text-text-primary font-semibold' : 'text-text-quaternary'\n }`}\n >\n {pending}\n </span>\n\n {/* Active */}\n <span\n className={`text-xs font-mono tabular-nums text-right ${\n claimed > 0 ? 'text-accent font-semibold' : 'text-text-quaternary'\n }`}\n >\n {claimed}\n </span>\n\n {/* Resolved */}\n <span className=\"text-xs font-mono tabular-nums text-right text-text-quaternary\">\n {resolved}\n </span>\n\n {/* P99 wait */}\n <span className=\"text-xs font-mono tabular-nums text-right text-text-secondary\">\n {fmtMin(metric?.wait.p99 ?? null)}\n </span>\n\n {/* P99 work */}\n <span className=\"text-xs font-mono tabular-nums text-right text-text-secondary\">\n {fmtMin(metric?.work.p99 ?? null)}\n </span>\n\n {/* Load mini-bar */}\n <div className=\"flex items-center gap-2\">\n <div className=\"w-12 h-1.5 bg-surface-sunken rounded-full overflow-hidden shrink-0\">\n <div className={`h-full rounded-full ${color}`} style={{ width: `${barWidth}%` }} />\n </div>\n <span\n className={`text-[10px] font-mono tabular-nums ${\n pct != null && pct > 100\n ? 'text-amber-500 font-semibold'\n : 'text-text-quaternary'\n }`}\n title={historical ? 'Throughput efficiency for the selected period' : undefined}\n >\n {pct != null ? `${pct}%${historical ? '↩' : ''}` : '—'}\n </span>\n </div>\n </div>\n\n {/* In-arrears sub-row */}\n {inArrears > 0 && (\n <div className=\"flex items-center gap-1.5 pb-2 pl-1\">\n <AlertTriangle className=\"w-3 h-3 text-red-400 shrink-0\" />\n <Link\n to={`/escalations/available?roles=${encodeURIComponent(JSON.stringify([role.role]))}&sort_by=created_at&order=asc`}\n className=\"text-[10px] text-red-400 hover:text-red-500 hover:underline\"\n onClick={(e) => e.stopPropagation()}\n >\n {inArrears} past SLA — view oldest first →\n </Link>\n </div>\n )}\n </>\n );\n}\n\n// ── Table header ──────────────────────────────────────────────────────────────\n\nfunction TableHead() {\n const cols = ['ROLE', 'TARGET/H', 'PENDING', 'ACTIVE', 'RESOLVED', 'P99 WAIT', 'P99 WORK', 'TREND'];\n return (\n <div\n className=\"grid items-center gap-4 py-1.5 border-b border-surface-border mb-0.5\"\n style={{ gridTemplateColumns: '1fr 64px 56px 56px 60px 72px 72px 104px' }}\n >\n {cols.map((col, i) => (\n <span\n key={col}\n className={`text-[9px] font-semibold uppercase tracking-wider text-text-quaternary ${\n i > 0 && i < 7 ? 'text-right' : ''\n }`}\n >\n {col}\n </span>\n ))}\n </div>\n );\n}\n\n\n// ── Page ──────────────────────────────────────────────────────────────────────\n\nexport function OperationsPage() {\n const [period, setPeriod] = useState<Period>('24h');\n const [selectedRole, setSelectedRole] = useState<string | null>(null);\n const [refreshing, setRefreshing] = useState(false);\n\n // Push-driven refresh: every escalation event invalidates ['stationMetrics']\n // through the central event system (debounced, transport-agnostic). The\n // header's refresh button covers user-initiated reloads.\n useStationMetricsEvents();\n\n const { data: roleData, isLoading: rolesLoading, refetch: refetchRoles } = useRoleDetails();\n const {\n data: metricsData,\n isLoading: metricsLoading,\n refetch: refetchMetrics,\n } = useStationMetrics(period);\n\n const roles = roleData?.roles ?? [];\n const metrics = metricsData?.stations ?? [];\n\n // BFS-ordered stations with exact depth tracked\n const ordered = useMemo((): OrderedStation[] => topoSort(roles), [roles]);\n\n const chartStations = useMemo(\n (): ChartStation[] =>\n ordered.map(({ role: r }) => ({\n role: r.role,\n title: r.title,\n parent_role: r.parent_role,\n target_per_hour: r.target_per_hour ?? null,\n metric: metrics.find((m) => m.role === r.role),\n })),\n [ordered, metrics],\n );\n\n const selectedRoleDetail =\n ordered.find(({ role }) => role.role === selectedRole)?.role ?? null;\n\n const isLoading = rolesLoading || metricsLoading;\n\n const handleRefresh = async () => {\n setRefreshing(true);\n await Promise.all([refetchRoles(), refetchMetrics()]);\n setRefreshing(false);\n };\n\n const handleSelect = (role: string) =>\n setSelectedRole((prev) => (prev === role ? null : role));\n\n const periodSelector = (\n <div className=\"flex items-center gap-0.5\">\n {PERIODS.map((p) => (\n <button\n key={p}\n onClick={() => setPeriod(p)}\n className={`px-2.5 py-1 text-[10px] font-mono rounded transition-colors ${\n period === p\n ? 'bg-accent/10 text-accent font-semibold'\n : 'text-text-quaternary hover:text-text-secondary'\n }`}\n >\n {p}\n </button>\n ))}\n </div>\n );\n\n return (\n <div className=\"flex flex-col flex-1 min-h-0\">\n <PageHeader\n title=\"Operations\"\n docsHash=\"#docs:dashboard.md:operations\"\n center={periodSelector}\n actions={\n <div className=\"flex items-center gap-3\">\n <button\n onClick={handleRefresh}\n disabled={refreshing}\n className=\"text-text-quaternary hover:text-text-secondary transition-colors disabled:opacity-40\"\n >\n <RefreshCw className={`w-3.5 h-3.5 ${refreshing ? 'animate-spin' : ''}`} />\n </button>\n <Link to=\"/admin/roles\" className=\"btn-secondary text-xs\">\n Configure\n </Link>\n </div>\n }\n />\n\n {isLoading ? (\n <div className=\"animate-pulse space-y-6 mt-4\">\n <div className=\"h-8 bg-surface-sunken rounded w-64\" />\n <div className=\"h-64 bg-surface-sunken rounded w-full\" />\n <div className=\"space-y-3\">\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"h-8 bg-surface-sunken rounded w-full\" />\n ))}\n </div>\n </div>\n ) : ordered.length === 0 ? (\n <div className=\"mt-8\">\n <p className=\"text-sm text-text-secondary mb-2\">Stations appear here once roles are marked visible in Operations.</p>\n <p className=\"text-xs text-text-tertiary\">\n Go to{' '}\n <Link to=\"/admin/roles\" className=\"text-accent hover:underline\">\n Roles\n </Link>{' '}\n and enable <strong>Visible in Operations</strong> on roles that represent pipeline\n stations.\n </p>\n </div>\n ) : (\n /* Console layout: fixed header (above) → flexible middle → fixed table (30vh) */\n <div className=\"flex flex-col flex-1 min-h-0\">\n\n {/* Middle row: flexible height — SVG fills left, sidebar fixed-width right */}\n <div className=\"flex-1 min-h-0 flex items-stretch overflow-hidden\">\n {/* SVG chart — scales to fill available space */}\n <div className=\"flex-1 min-w-0 min-h-0 flex flex-col justify-center overflow-hidden p-4\">\n <PaceChart\n stations={chartStations}\n selectedRole={selectedRole}\n onSelect={handleSelect}\n periodHours={PERIOD_HOURS[period]}\n />\n </div>\n {/* Vertical divider */}\n <div className=\"w-px bg-surface-border shrink-0 self-stretch\" />\n {/* Right sidebar — fixed width, scrolls its own content */}\n <StationDetailPanel\n role={selectedRoleDetail}\n allMetrics={metrics}\n orderedRoles={ordered.map((o) => o.role)}\n globalPeriod={period}\n onClose={() => setSelectedRole(null)}\n />\n </div>\n\n {/* Bottom row: fixed 30vh — table header sticky, rows scroll */}\n <div className=\"h-[30vh] flex-none flex flex-col border-t border-surface-border overflow-hidden\">\n <TableHead />\n <div className=\"flex-1 min-h-0 overflow-y-auto divide-y divide-surface-border/30\">\n {ordered.map(({ role }) => (\n <StationRow\n key={role.role}\n role={role}\n metric={metrics.find((m) => m.role === role.role)}\n selected={selectedRole === role.role}\n onClick={() => handleSelect(role.role)}\n />\n ))}\n </div>\n </div>\n\n </div>\n )}\n </div>\n );\n}\n"],"names":["W","H","ML","MR","MT","MB","chartW","chartH","bottom","right","EASE","linePath","pts","p","i","catmullPath","segs","p0","p1","p2","p3","cp1x","cp1y","cp2x","cp2y","compact","n","last","a","paceColor","ratio","ACTIVE_COLOR","MIN_GAP","spreadLabels","labels","sorted","b","prev","l","labelY","PaceChart","stations","selectedRole","onSelect","periodHours","hoveredIdx","setHoveredIdx","useState","xOf","xMap","rows","target","expected","_a","_b","_c","_d","hasTargets","r","maxVal","yScale","v","withTarget","targetPts","actualPts","activePts","targetLinePath","actualSplinePath","activeSplinePath","lastActual","lastTarget","lastActive","areaPath","endLabels","px","cx","jsx","tp","ap","ar","row","s","x","cy","stroke","isSelected","isHovered","tooltip","tipW","tipX","tipY","jsxs","PERIODS","isPeriod","fmt","min","MetricRow","label","value","PeriodSelector","period","onChange","RoleView","role","globalPeriod","onClose","setPeriod","data","useStationMetrics","metric","slaMinutes","targetPerHour","workerCount","Fragment","X","Link","ExternalLink","OverviewPanel","allMetrics","orderedRoles","totalPending","m","totalResolved","totalArrears","metricByRole","stationsAtRisk","stationsWithLoad","pending","claimed","resolved","inArrears","hasAlert","hasLoad","StationDetailPanel","PERIOD_HOURS","topoSort","roles","opsRoles","roleMap","result","visited","queue","item","fmtMin","loadBar","throughputPct","pct","color","StationRow","selected","onClick","historical","barWidth","e","AlertTriangle","TableHead","cols","col","OperationsPage","setSelectedRole","refreshing","setRefreshing","useStationMetricsEvents","roleData","rolesLoading","refetchRoles","useRoleDetails","metricsData","metricsLoading","refetchMetrics","metrics","ordered","useMemo","chartStations","selectedRoleDetail","isLoading","handleRefresh","handleSelect","periodSelector","PageHeader","RefreshCw","o"],"mappings":"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"}
@@ -1,16 +0,0 @@
1
- import{a as c,j as e}from"./vendor-query-B2UbickB.js";import{b as X,e as Y,f as Z,d as ee,g as te,h as se}from"./roles-bRgaMcdy.js";import{J as T}from"./index-CtDf8vvQ.js";import{C as ae}from"./ConfirmDeleteModal-D9_1b4MW.js";import{T as q,ah as re,v as L,V as ne,p as le,k as oe,g as $,as as ie,N as ce}from"./vendor-icons-C1zVP_dv.js";import{e as de,c as me}from"./vendor-react-CXumBFUA.js";import"./Modal-CSrxpXeM.js";function I(a){return a==null?"":JSON.stringify(a,null,2)}function _(a){const d=a.trim();if(!d)return{ok:!0,value:void 0};try{const o=JSON.parse(d);return typeof o!="object"||Array.isArray(o)||o===null?{ok:!1}:{ok:!0,value:o}}catch{return{ok:!1}}}function h({icon:a,color:d,label:o,aside:f}){return e.jsxs("div",{className:"flex items-center justify-between mb-3 pb-2 border-b border-surface-border",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(a,{className:`w-4 h-4 ${d}`,strokeWidth:1.5}),e.jsx("h2",{className:"section-h2",children:o})]}),f]})}function xe({role:a,allRoles:d}){const{data:o}=ee(),f=te(),j=se(),[x,p]=c.useState(""),t=(o==null?void 0:o.chains)??[],r=c.useMemo(()=>t.filter(l=>l.source_role===a.role).map(l=>l.target_role),[t,a.role]),N=c.useMemo(()=>d.map(l=>l.role).filter(l=>l!==a.role&&l!=="superadmin"&&!r.includes(l)),[d,a.role,r]);return a.role==="superadmin"?e.jsx("p",{className:"text-[11px] text-text-tertiary",children:"Superadmins can escalate to any role implicitly."}):e.jsxs("div",{className:"space-y-3",children:[r.length===0?e.jsx("p",{className:"text-[11px] text-text-tertiary",children:"Add a target role to route hand-offs."}):e.jsx("div",{className:"flex flex-wrap gap-1.5",children:r.map(l=>e.jsxs("span",{className:"inline-flex items-center gap-1 px-2 py-0.5 text-[10px] bg-surface-sunken rounded font-mono text-text-secondary",children:[l,e.jsx("button",{onClick:()=>j.mutate({source_role:a.role,target_role:l}),className:"text-text-quaternary hover:text-red-400 transition-colors leading-none ml-0.5",children:"×"})]},l))}),N.length>0&&e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("select",{value:x,onChange:l=>p(l.target.value),className:"select text-xs font-mono flex-1",children:[e.jsx("option",{value:"",children:"Add target…"}),N.map(l=>e.jsx("option",{value:l,children:l},l))]}),e.jsx("button",{onClick:()=>{x&&f.mutate({source_role:a.role,target_role:x},{onSuccess:()=>p("")})},disabled:!x||f.isPending,className:"px-2.5 py-1 text-xs rounded bg-accent text-text-inverse hover:bg-accent-hover transition-colors disabled:opacity-40 disabled:cursor-not-allowed shrink-0",children:f.isPending?"…":"Add"})]})]})}function pe(a){return{title:a.title??"",description:a.description??"",ops_visible:a.ops_visible,parent_role:a.parent_role??"",form_schema:I(a.form_schema),metadata_schema:I(a.metadata_schema),properties:I(a.properties)||"{}",sla_minutes:a.sla_minutes!=null?String(a.sla_minutes):"",target_per_hour:a.target_per_hour!=null?String(a.target_per_hour):"",worker_count:a.worker_count!=null?String(a.worker_count):""}}function Ne(){const{role:a}=de(),d=me(),{data:o,isLoading:f}=X(),j=Y(),x=Z(),p=(o==null?void 0:o.roles)??[],t=p.find(s=>s.role===a),[r,N]=c.useState({title:"",description:"",ops_visible:!1,parent_role:"",form_schema:"",metadata_schema:"",properties:"{}",sla_minutes:"",target_per_hour:"",worker_count:""}),[l,E]=c.useState(!1),[V,k]=c.useState(!1),[v,b]=c.useState({}),[W,M]=c.useState(!1),[g,J]=c.useState(new Set),R=s=>J(n=>new Set([...n,s]));c.useEffect(()=>{t&&(N(pe(t)),E(!1),k(!1),b({}),J(new Set))},[a,t==null?void 0:t.role]);const u=s=>{N(n=>({...n,...s})),E(!0),k(!1)},B=c.useMemo(()=>p.filter(s=>{if(!t||s.role===t.role)return!1;let n=s;const i=new Set;for(;n!=null&&n.parent_role;){if(n.parent_role===t.role)return!1;if(i.has(n.parent_role))break;i.add(n.parent_role),n=p.find(m=>m.role===n.parent_role)}return!0}),[p,t]),U=()=>{const s=_(r.form_schema),n=_(r.metadata_schema),i=_(r.properties),m={};if(s.ok||(m.form_schema="Invalid JSON"),n.ok||(m.metadata_schema="Invalid JSON"),i.ok||(m.properties="Invalid JSON"),b(m),Object.keys(m).length||!t)return;const w=A=>{const y=parseFloat(A);return isNaN(y)?null:y},Q=A=>{const y=w(A);return y==null?null:Math.round(y)};j.mutate({role:t.role,title:r.title.trim()||null,description:r.description.trim()||null,ops_visible:r.ops_visible,parent_role:r.parent_role||null,form_schema:s.value??null,metadata_schema:n.value??null,properties:i.value??{},sla_minutes:w(r.sla_minutes),target_per_hour:w(r.target_per_hour),worker_count:Q(r.worker_count)},{onSuccess:()=>{E(!1),k(!0),J(new Set),window.setTimeout(()=>k(!1),2500)}})},H=()=>{t&&x.mutate(t.role,{onSuccess:()=>d("/admin/roles")})};if(f)return e.jsxs("div",{className:"animate-pulse space-y-4",children:[e.jsx("div",{className:"h-8 bg-surface-sunken rounded w-48"}),e.jsx("div",{className:"h-4 bg-surface-sunken rounded w-64"}),e.jsx("div",{className:"h-64 bg-surface-sunken rounded"})]});if(!t)return e.jsxs("div",{className:"flex flex-col items-center justify-center py-20 text-center",children:[e.jsx(q,{className:"w-12 h-12 text-text-quaternary mb-4",strokeWidth:1}),e.jsx("h2",{className:"text-lg font-medium text-text-primary mb-2",children:"Role not found"})]});const K=t.user_count>0||t.chain_count>0||t.workflow_count>0,G=l&&!Object.values(v).some(Boolean),S=parseFloat(r.sla_minutes),C=parseFloat(r.target_per_hour),O=parseFloat(r.worker_count),P=!isNaN(S)&&S>0,D=!isNaN(C)&&C>0,F=!isNaN(O)&&O>0,z=P&&F&&!D?`→ target ≈ ${(O/(S/60)).toFixed(1)}/h`:P&&D&&!F?`→ workers ≈ ${(C*(S/60)).toFixed(1)}`:D&&F&&!P?`→ SLA ≈ ${(O/C*60).toFixed(0)}m`:null;return e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-start justify-between mb-10",children:[e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-3 mb-1",children:[e.jsx(q,{className:"w-5 h-5 text-accent",strokeWidth:1.5}),e.jsx("h1",{className:"text-lg font-mono font-medium text-text-primary",children:t.role}),t.ops_visible&&e.jsxs("span",{className:"inline-flex items-center gap-0.5 text-[9px] font-bold uppercase tracking-wider text-accent",children:[e.jsx(re,{className:"w-2.5 h-2.5"})," ops"]}),t.parent_role&&e.jsxs("span",{className:"flex items-center gap-1 text-[10px] text-text-quaternary font-mono",children:[e.jsx(L,{className:"w-3 h-3"})," ",t.parent_role]})]}),t.title&&e.jsx("p",{className:"text-sm text-text-secondary pl-8",children:t.title})]}),e.jsxs("div",{className:"flex items-center gap-2 shrink-0",children:[V&&e.jsxs("span",{className:"flex items-center gap-1 text-xs text-status-success animate-page-enter",children:[e.jsx(ne,{className:"w-3 h-3"})," Saved"]}),j.error&&e.jsx("span",{className:"text-xs text-status-error max-w-[180px] truncate",children:j.error.message}),e.jsx("button",{onClick:U,disabled:!G||j.isPending,className:"px-3 py-1.5 text-xs rounded-md bg-accent text-text-inverse hover:bg-accent-hover transition-colors disabled:opacity-40 disabled:cursor-not-allowed",children:j.isPending?"Saving…":"Save Role"}),!K&&e.jsxs("button",{onClick:()=>M(!0),className:"px-3 py-1.5 text-xs rounded-md text-red-400/60 hover:text-red-400 hover:bg-red-600/10 transition-colors flex items-center gap-1",children:[e.jsx(le,{className:"w-3 h-3"})," Delete"]})]})]}),e.jsxs("div",{className:"grid grid-cols-3 gap-16 items-start",children:[e.jsxs("div",{className:"space-y-14",children:[e.jsxs("div",{className:"space-y-8",children:[e.jsx(h,{icon:q,color:"text-accent",label:"Identity"}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-1.5",children:"Display Name"}),e.jsx("input",{type:"text",value:r.title,onChange:s=>u({title:s.target.value}),placeholder:`e.g., ${t.role.charAt(0).toUpperCase()+t.role.slice(1)}`,className:"input text-sm w-full"})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-1.5",children:"Description"}),e.jsx("textarea",{value:r.description,onChange:s=>u({description:s.target.value}),placeholder:"A short description shown on role cards and in the operations view.",rows:4,className:"input text-sm w-full resize-none"})]})]}),e.jsxs("div",{className:"space-y-5",children:[e.jsx(h,{icon:L,color:"text-text-tertiary",label:"Prior Step"}),e.jsxs("select",{value:r.parent_role,onChange:s=>u({parent_role:s.target.value}),className:"select text-sm w-full font-mono",children:[e.jsx("option",{value:"",children:"None — root process"}),B.map(s=>e.jsx("option",{value:s.role,children:s.title?`${s.role} — ${s.title}`:s.role},s.role))]})]}),e.jsxs("div",{children:[e.jsx(h,{icon:oe,color:"text-text-tertiary",label:"Ops View"}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("p",{className:"text-[10px] text-text-tertiary",children:"Show as station on /operations"}),e.jsx("button",{onClick:()=>u({ops_visible:!r.ops_visible}),className:`w-9 h-5 rounded-full transition-colors relative shrink-0 ${r.ops_visible?"bg-accent":"bg-surface-border"}`,children:e.jsx("span",{className:`absolute top-[3px] left-0 w-3.5 h-3.5 rounded-full bg-white transition-transform shadow ${r.ops_visible?"translate-x-[18px]":"translate-x-[3px]"}`})})]})]}),e.jsxs("div",{children:[e.jsx(h,{icon:$,color:"text-text-quaternary",label:"Properties",aside:!g.has("properties")&&t.properties&&Object.keys(t.properties).length>0?e.jsx("button",{onClick:()=>R("properties"),className:"text-[9px] text-accent hover:underline",children:"Edit"}):e.jsx("span",{className:"text-[9px] font-normal normal-case text-text-quaternary",children:"custom JSON"})}),!g.has("properties")&&t.properties&&Object.keys(t.properties).length>0?e.jsx(T,{data:t.properties,defaultCollapsed:!0}):e.jsxs(e.Fragment,{children:[e.jsx("textarea",{value:r.properties,onChange:s=>{const n=s.target.value;u({properties:n}),b(i=>({...i,properties:_(n).ok?void 0:"Invalid JSON"}))},rows:5,spellCheck:!1,className:"input text-xs font-mono w-full resize-none",placeholder:`{
2
- "icon": "wrench",
3
- "color": "#6366f1"
4
- }`}),v.properties&&e.jsx("p",{className:"text-[10px] text-status-error mt-1",children:v.properties})]})]})]}),e.jsxs("div",{className:"space-y-14",children:[e.jsx(h,{icon:ie,color:"text-amber-400",label:"Capacity",aside:e.jsx("span",{className:"text-[9px] font-semibold uppercase tracking-widest text-text-quaternary",children:"throughput = workers / (sla / 60)"})}),e.jsx("div",{className:"grid grid-cols-3 gap-3",children:[{key:"sla_minutes",label:"SLA",unit:"min",placeholder:"30"},{key:"target_per_hour",label:"Target",unit:"/h",placeholder:"20"},{key:"worker_count",label:"Workers",unit:"",placeholder:"4"}].map(({key:s,label:n,unit:i,placeholder:m})=>e.jsxs("div",{children:[e.jsxs("label",{className:"block text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-1.5",children:[n,i&&e.jsx("span",{className:"normal-case font-normal ml-1 text-text-quaternary",children:i})]}),e.jsx("input",{type:"number",min:"0",step:"any",value:r[s],onChange:w=>u({[s]:w.target.value}),placeholder:m,className:"input text-sm w-full font-mono"})]},s))}),z&&e.jsx("p",{className:"text-[10px] text-accent font-mono",children:z}),e.jsxs("div",{className:"pt-3 pb-4 border-t border-surface-border/40",children:[e.jsx(h,{icon:ce,color:"text-text-quaternary",label:"Escalation Targets",aside:e.jsxs("div",{className:"flex items-center gap-1",children:[e.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-emerald-500"}),e.jsx("span",{className:"text-[9px] font-semibold uppercase tracking-widest text-emerald-600",children:"Live"})]})}),e.jsx(xe,{role:t,allRoles:p})]})]}),e.jsxs("div",{className:"space-y-14",children:[e.jsxs("div",{children:[e.jsx(h,{icon:$,color:"text-cyan-400",label:"Form Schema",aside:!g.has("form_schema")&&t.form_schema?e.jsx("button",{onClick:()=>R("form_schema"),className:"text-[9px] text-accent hover:underline",children:"Edit"}):void 0}),e.jsxs("p",{className:"text-[10px] text-text-tertiary mb-3 leading-relaxed",children:["Resolver form JSON Schema. Overrideable per workflow via ",e.jsx("code",{className:"font-mono",children:"resolver_schema"}),"."]}),!g.has("form_schema")&&t.form_schema?e.jsx(T,{data:t.form_schema}):e.jsxs(e.Fragment,{children:[e.jsx("textarea",{value:r.form_schema,onChange:s=>{const n=s.target.value;u({form_schema:n}),b(i=>({...i,form_schema:_(n).ok?void 0:"Invalid JSON"}))},rows:10,spellCheck:!1,className:"input text-xs font-mono w-full resize-y",placeholder:`{
5
- "type": "object",
6
- "properties": {
7
- "notes": { "type": "string" }
8
- }
9
- }`}),v.form_schema&&e.jsx("p",{className:"text-[10px] text-status-error mt-1",children:v.form_schema})]})]}),e.jsxs("div",{children:[e.jsx(h,{icon:$,color:"text-violet-400",label:"Metadata Schema",aside:!g.has("metadata_schema")&&t.metadata_schema?e.jsx("button",{onClick:()=>R("metadata_schema"),className:"text-[9px] text-accent hover:underline",children:"Edit"}):void 0}),e.jsxs("p",{className:"text-[10px] text-text-tertiary mb-3 leading-relaxed",children:["Validates ",e.jsx("code",{className:"font-mono",children:"metadata"})," at creation time. Keys appear in faceted search autocomplete."]}),!g.has("metadata_schema")&&t.metadata_schema?e.jsx(T,{data:t.metadata_schema}):e.jsxs(e.Fragment,{children:[e.jsx("textarea",{value:r.metadata_schema,onChange:s=>{const n=s.target.value;u({metadata_schema:n}),b(i=>({...i,metadata_schema:_(n).ok?void 0:"Invalid JSON"}))},rows:10,spellCheck:!1,className:"input text-xs font-mono w-full resize-y",placeholder:`{
10
- "type": "object",
11
- "properties": {
12
- "order_id": { "type": "string" }
13
- },
14
- "required": ["order_id"]
15
- }`}),v.metadata_schema&&e.jsx("p",{className:"text-[10px] text-status-error mt-1",children:v.metadata_schema})]})]})]})]}),e.jsx(ae,{open:W,onClose:()=>M(!1),onConfirm:H,title:"Delete Role",description:e.jsxs(e.Fragment,{children:["Delete role ",e.jsx("span",{className:"font-medium font-mono text-text-primary",children:t.role}),"? This cannot be undone."]}),isPending:x.isPending,error:x.error})]})}export{Ne as RoleDetailPage};
16
- //# sourceMappingURL=RoleDetailPage-BB_e330f.js.map