@zaip-ai/cli 1.0.7 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (444) hide show
  1. package/README.md +6 -4
  2. package/dist/services/adapters.d.ts +1 -1
  3. package/dist/services/adapters.d.ts.map +1 -1
  4. package/dist/services/adapters.js +15 -5
  5. package/dist/services/adapters.js.map +1 -1
  6. package/dist/services/adapters.test.js +1 -1
  7. package/dist/services/agent-adapters.d.ts +9 -0
  8. package/dist/services/agent-adapters.d.ts.map +1 -1
  9. package/dist/services/agent-adapters.js +39 -4
  10. package/dist/services/agent-adapters.js.map +1 -1
  11. package/dist/services/agent-adapters.test.js +13 -1
  12. package/dist/services/agent-adapters.test.js.map +1 -1
  13. package/dist/services/codex-config-template.d.ts +2 -1
  14. package/dist/services/codex-config-template.d.ts.map +1 -1
  15. package/dist/services/codex-config-template.js +13 -2
  16. package/dist/services/codex-config-template.js.map +1 -1
  17. package/dist/services/codex-rules.d.ts +1 -1
  18. package/dist/services/codex-rules.d.ts.map +1 -1
  19. package/dist/services/codex-rules.js +31 -7
  20. package/dist/services/codex-rules.js.map +1 -1
  21. package/dist/services/codex-subagent-hook-template.d.ts +1 -1
  22. package/dist/services/codex-subagent-hook-template.d.ts.map +1 -1
  23. package/dist/services/codex-subagent-hook-template.js +4 -1
  24. package/dist/services/codex-subagent-hook-template.js.map +1 -1
  25. package/dist/services/doctor.d.ts.map +1 -1
  26. package/dist/services/doctor.js +3 -1
  27. package/dist/services/doctor.js.map +1 -1
  28. package/dist/services/global-codex-config.d.ts +6 -3
  29. package/dist/services/global-codex-config.d.ts.map +1 -1
  30. package/dist/services/global-codex-config.js +82 -13
  31. package/dist/services/global-codex-config.js.map +1 -1
  32. package/dist/services/global-codex-config.test.js +57 -10
  33. package/dist/services/global-codex-config.test.js.map +1 -1
  34. package/dist/services/install-agents-template.d.ts.map +1 -1
  35. package/dist/services/install-agents-template.js +15 -2
  36. package/dist/services/install-agents-template.js.map +1 -1
  37. package/dist/services/logger.js +1 -1
  38. package/dist/services/logger.js.map +1 -1
  39. package/dist/services/logger.test.js +3 -3
  40. package/dist/services/logger.test.js.map +1 -1
  41. package/dist/services/runtime-template.test.js +21 -5
  42. package/dist/services/runtime-template.test.js.map +1 -1
  43. package/dist/services/template-manifest.d.ts.map +1 -1
  44. package/dist/services/template-manifest.js +7 -0
  45. package/dist/services/template-manifest.js.map +1 -1
  46. package/package.json +1 -1
  47. package/templates/.agents/architecture/software-architecture.md +131 -0
  48. package/templates/.agents/branding/brandbook.docx +0 -0
  49. package/templates/.agents/branding/brandbook.md +60 -0
  50. package/templates/.agents/config/agents/catalog.yaml +310 -10
  51. package/templates/.agents/config/clients/codex/agents/zaip-agent-brand-strategist.toml +30 -0
  52. package/templates/.agents/config/clients/codex/agents/zaip-agent-designer.toml +5 -5
  53. package/templates/.agents/config/clients/codex/agents/zaip-agent-finalizer.toml +2 -2
  54. package/templates/.agents/config/clients/codex/agents/zaip-agent-general-operations.toml +30 -0
  55. package/templates/.agents/config/clients/codex/agents/zaip-agent-governance-compliance.toml +30 -0
  56. package/templates/.agents/config/clients/codex/agents/zaip-agent-growth-strategist.toml +30 -0
  57. package/templates/.agents/config/clients/codex/agents/zaip-agent-marketing-analyst.toml +8 -8
  58. package/templates/.agents/config/clients/codex/agents/zaip-agent-orchestrator.toml +5 -3
  59. package/templates/.agents/config/clients/codex/agents/zaip-agent-sales-specialist.toml +30 -0
  60. package/templates/.agents/config/project.yaml +1 -1
  61. package/templates/.agents/dashboard/.tmp-run-list.json +254 -0
  62. package/templates/.agents/dashboard/.zaip-managed.json +775 -163
  63. package/templates/.agents/dashboard/DATA-DICTIONARY.md +2227 -0
  64. package/templates/.agents/dashboard/README.md +48 -97
  65. package/templates/.agents/dashboard/apps/frontend/dist/assets/CostCalculationGuidePage-DxR_pgSV.js +1 -0
  66. package/templates/.agents/dashboard/apps/frontend/dist/assets/DashboardHelpDialog-4jZLTWE_.js +2156 -0
  67. package/templates/.agents/dashboard/apps/frontend/dist/assets/PricingSettingsDialog-CKcsqb-r.js +1 -0
  68. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-BomxqCBU.css +1 -0
  69. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-DmmJD2Th.js +3 -0
  70. package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-BwtitWa_.js +1 -0
  71. package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-CaKwQP43.css +1 -0
  72. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-BZ5WdaMN.css +1 -0
  73. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-N5Csr0RP.js +68 -0
  74. package/templates/.agents/dashboard/apps/frontend/dist/assets/{dashboard-localization-C98LGC-Q.js → dashboard-localization-CLJ0lT9v.js} +1 -1
  75. package/templates/.agents/dashboard/apps/frontend/dist/assets/icons-vendor-C0fj2uU9.js +1 -0
  76. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-C4aIB1PV.js +2 -0
  77. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-DK_enB5Z.css +1 -0
  78. package/templates/.agents/dashboard/apps/frontend/dist/assets/react-vendor-BEsJ-FYH.js +9 -0
  79. package/templates/.agents/dashboard/apps/frontend/dist/assets/rolldown-runtime-D9-fqq9M.js +1 -0
  80. package/templates/.agents/dashboard/apps/frontend/dist/assets/traceability-B6y6Gw12.js +1 -0
  81. package/templates/.agents/dashboard/apps/frontend/dist/assets/traceability-DFY7ZDlu.css +1 -0
  82. package/templates/.agents/dashboard/apps/frontend/dist/index.html +9 -2
  83. package/templates/.agents/dashboard/apps/frontend/package.json +1 -1
  84. package/templates/.agents/dashboard/apps/frontend/src/App.tsx +1129 -600
  85. package/templates/.agents/dashboard/apps/frontend/src/AppLoadingBoundary.tsx +48 -0
  86. package/templates/.agents/dashboard/apps/frontend/src/AppLoadingPanels.tsx +2 -0
  87. package/templates/.agents/dashboard/apps/frontend/src/AppOverlays.tsx +247 -0
  88. package/templates/.agents/dashboard/apps/frontend/src/AppRequestGuards.tsx +95 -0
  89. package/templates/.agents/dashboard/apps/frontend/src/AppSetupRoutes.tsx +87 -0
  90. package/templates/.agents/dashboard/apps/frontend/src/CostCalculationGuidePage.tsx +641 -0
  91. package/templates/.agents/dashboard/apps/frontend/src/DashboardAlternateContent.tsx +159 -0
  92. package/templates/.agents/dashboard/apps/frontend/src/DashboardCapabilityNav.tsx +13 -13
  93. package/templates/.agents/dashboard/apps/frontend/src/DashboardGlobalSettings.tsx +56 -0
  94. package/templates/.agents/dashboard/apps/frontend/src/ProjectSettingsPage.tsx +266 -315
  95. package/templates/.agents/dashboard/apps/frontend/src/ProjectStackEditor.tsx +151 -0
  96. package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.test.ts +37 -0
  97. package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.tsx +114 -12
  98. package/templates/.agents/dashboard/apps/frontend/src/app-preferences.ts +15 -0
  99. package/templates/.agents/dashboard/apps/frontend/src/app-session-token.ts +20 -0
  100. package/templates/.agents/dashboard/apps/frontend/src/cost-calculation-guide.css +267 -0
  101. package/templates/.agents/dashboard/apps/frontend/src/cost-calculation-guide.test.ts +69 -0
  102. package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +85 -0
  103. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +133 -0
  104. package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +453 -13
  105. package/templates/.agents/dashboard/apps/frontend/src/dashboard-localization.ts +1 -1
  106. package/templates/.agents/dashboard/apps/frontend/src/main.tsx +1 -0
  107. package/templates/.agents/dashboard/apps/frontend/src/operations-detail-hydration.test.ts +55 -0
  108. package/templates/.agents/dashboard/apps/frontend/src/operations-detail-hydration.ts +36 -0
  109. package/templates/.agents/dashboard/apps/frontend/src/operations-entity-page-projection.test.ts +126 -0
  110. package/templates/.agents/dashboard/apps/frontend/src/operations-entity-page-projection.ts +181 -0
  111. package/templates/.agents/dashboard/apps/frontend/src/operations-period-contract.test.ts +138 -0
  112. package/templates/.agents/dashboard/apps/frontend/src/operations-period-render.test.ts +105 -0
  113. package/templates/.agents/dashboard/apps/frontend/src/operations-period.test.ts +6 -3
  114. package/templates/.agents/dashboard/apps/frontend/src/operations-period.ts +9 -5
  115. package/templates/.agents/dashboard/apps/frontend/src/overview-status-page.test.ts +26 -0
  116. package/templates/.agents/dashboard/apps/frontend/src/period-transition-overlay.css +44 -0
  117. package/templates/.agents/dashboard/apps/frontend/src/project-settings-model.ts +121 -62
  118. package/templates/.agents/dashboard/apps/frontend/src/project-settings-page.css +482 -3
  119. package/templates/.agents/dashboard/apps/frontend/src/project-settings.test.ts +84 -17
  120. package/templates/.agents/dashboard/apps/frontend/src/snapshot-banner-semantics.test.ts +78 -0
  121. package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +234 -39
  122. package/templates/.agents/dashboard/apps/frontend/src/snapshot-notice.ts +15 -0
  123. package/templates/.agents/dashboard/apps/frontend/src/sqlite-cost-adapters.ts +26 -0
  124. package/templates/.agents/dashboard/apps/frontend/src/styles.css +14 -2
  125. package/templates/.agents/dashboard/apps/frontend/src/traceability-workspace.css +38 -2
  126. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.test.ts +253 -0
  127. package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.ts +380 -0
  128. package/templates/.agents/dashboard/apps/frontend/src/use-app-effects.ts +175 -0
  129. package/templates/.agents/dashboard/apps/frontend/src/use-app-operations-pages.ts +395 -0
  130. package/templates/.agents/dashboard/apps/frontend/src/use-app-trace-documents.ts +141 -0
  131. package/templates/.agents/dashboard/apps/frontend/src/use-idle-cost-prefetch.ts +45 -0
  132. package/templates/.agents/dashboard/apps/frontend/src/work-list-pagination.test.ts +46 -0
  133. package/templates/.agents/dashboard/apps/frontend/src/work-list-pagination.ts +36 -0
  134. package/templates/.agents/dashboard/apps/frontend/vite.config.ts +58 -5
  135. package/templates/.agents/dashboard/modules/cost-monitor/package.json +1 -1
  136. package/templates/.agents/dashboard/modules/cost-monitor/src/contracts.ts +47 -6
  137. package/templates/.agents/dashboard/modules/cost-monitor/src/duration-estimate.ts +13 -0
  138. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CompactLifecycleCostTable.test.ts +134 -0
  139. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CompactLifecycleCostTable.tsx +290 -0
  140. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostCharts.test.ts +133 -19
  141. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostCharts.tsx +134 -43
  142. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.tsx +137 -19
  143. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostModelsAuditTable.test.ts +2 -0
  144. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostModelsAuditTable.tsx +13 -10
  145. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorContent.tsx +328 -0
  146. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.test.ts +302 -6
  147. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +627 -400
  148. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostSummary.tsx +5 -13
  149. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +370 -72
  150. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.tsx +467 -326
  151. package/templates/.agents/dashboard/modules/cost-monitor/src/react/MacroCostTable.test.ts +17 -0
  152. package/templates/.agents/dashboard/modules/cost-monitor/src/react/MacroCostTable.tsx +25 -6
  153. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-copy.ts +0 -2
  154. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-page-helpers.ts +17 -0
  155. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-responsive.css +6 -3
  156. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-row-filters.ts +123 -0
  157. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-summary.ts +8 -3
  158. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-views.ts +32 -0
  159. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor.css +55 -14
  160. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-table-presentation.ts +121 -0
  161. package/templates/.agents/dashboard/modules/cost-monitor/src/react/index.ts +3 -0
  162. package/templates/.agents/dashboard/modules/cost-monitor/src/react/period-cost-groups.test.ts +68 -0
  163. package/templates/.agents/dashboard/modules/cost-monitor/src/react/period-cost-groups.ts +33 -5
  164. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-compact-cost-page.ts +119 -0
  165. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.test.ts +12 -0
  166. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.ts +146 -0
  167. package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-run-page.ts +118 -0
  168. package/templates/.agents/dashboard/modules/cost-monitor/src/server/aggregate.test.ts +2 -2
  169. package/templates/.agents/dashboard/modules/cost-monitor/src/server/aggregate.ts +5 -3
  170. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-pricing-fallback.test.ts +53 -0
  171. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-projection.basics.test.ts +34 -0
  172. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-projection.test.ts +168 -161
  173. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-projection.ts +188 -29
  174. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.test.ts +757 -0
  175. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.ts +784 -0
  176. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-terminal-day.test.ts +100 -0
  177. package/templates/.agents/dashboard/modules/cost-monitor/src/server/index.ts +1 -0
  178. package/templates/.agents/dashboard/modules/cost-monitor/src/server/refactor-map-xr9l.json +61 -0
  179. package/templates/.agents/dashboard/modules/cost-monitor/src/server/source-size-cost-server/build-cost-monitor-projection-agents.test.ts +31 -0
  180. package/templates/.agents/dashboard/modules/cost-monitor/src/server/source-size-cost-server/build-cost-summary-projection-duration.test.ts +414 -0
  181. package/templates/.agents/dashboard/modules/cost-monitor/src/task-cost-groups.test.ts +41 -0
  182. package/templates/.agents/dashboard/modules/cost-monitor/src/task-cost-groups.ts +55 -11
  183. package/templates/.agents/dashboard/modules/operations/package.json +2 -1
  184. package/templates/.agents/dashboard/modules/operations/src/contracts.ts +185 -0
  185. package/templates/.agents/dashboard/modules/operations/src/duration-population.test.ts +63 -1
  186. package/templates/.agents/dashboard/modules/operations/src/duration-population.ts +60 -5
  187. package/templates/.agents/dashboard/modules/operations/src/react/EntityDetailsDialog.tsx +14 -3
  188. package/templates/.agents/dashboard/modules/operations/src/react/EntityTables.test.ts +44 -1
  189. package/templates/.agents/dashboard/modules/operations/src/react/EntityTables.tsx +345 -18
  190. package/templates/.agents/dashboard/modules/operations/src/react/MacroPlanningDialog.tsx +1 -0
  191. package/templates/.agents/dashboard/modules/operations/src/react/OperationDateFilters.test.ts +9 -5
  192. package/templates/.agents/dashboard/modules/operations/src/react/OperationDateFilters.tsx +2 -2
  193. package/templates/.agents/dashboard/modules/operations/src/react/OperationsMainContent.tsx +168 -0
  194. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.helpers.tsx +95 -6
  195. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.test.ts +367 -14
  196. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.tsx +562 -131
  197. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +353 -355
  198. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageNavigation.tsx +65 -0
  199. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageOverview.tsx +169 -0
  200. package/templates/.agents/dashboard/modules/operations/src/react/PeriodSummaryTable.tsx +73 -8
  201. package/templates/.agents/dashboard/modules/operations/src/react/PrdDocumentDialog.test.tsx +32 -0
  202. package/templates/.agents/dashboard/modules/operations/src/react/PrdDocumentDialog.tsx +62 -6
  203. package/templates/.agents/dashboard/modules/operations/src/react/TaskBoard.test.ts +55 -0
  204. package/templates/.agents/dashboard/modules/operations/src/react/TaskBoard.tsx +27 -19
  205. package/templates/.agents/dashboard/modules/operations/src/react/TaskDetailsDialog.tsx +105 -42
  206. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkBoard.priority.test.ts +19 -0
  207. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkBoard.tsx +265 -36
  208. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.test.ts +171 -8
  209. package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.tsx +98 -68
  210. package/templates/.agents/dashboard/modules/operations/src/react/conversation-rows.test.ts +72 -1
  211. package/templates/.agents/dashboard/modules/operations/src/react/conversation-rows.ts +78 -17
  212. package/templates/.agents/dashboard/modules/operations/src/react/execution-metrics.ts +6 -4
  213. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +431 -1
  214. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +574 -20
  215. package/templates/.agents/dashboard/modules/operations/src/react/export/export-layout.test.ts +42 -4
  216. package/templates/.agents/dashboard/modules/operations/src/react/export/xlsx-runtime-e2e.test.ts +248 -0
  217. package/templates/.agents/dashboard/modules/operations/src/react/macro-planning-dialog-content.css +49 -0
  218. package/templates/.agents/dashboard/modules/operations/src/react/macro-planning-dialog.css +93 -50
  219. package/templates/.agents/dashboard/modules/operations/src/react/manual-work-priority.test.ts +82 -0
  220. package/templates/.agents/dashboard/modules/operations/src/react/operation-date-range.test.ts +29 -2
  221. package/templates/.agents/dashboard/modules/operations/src/react/operation-date-range.ts +22 -1
  222. package/templates/.agents/dashboard/modules/operations/src/react/operation-duration.test.ts +77 -4
  223. package/templates/.agents/dashboard/modules/operations/src/react/operation-duration.ts +67 -5
  224. package/templates/.agents/dashboard/modules/operations/src/react/operations-coverage-toast.test.ts +54 -0
  225. package/templates/.agents/dashboard/modules/operations/src/react/operations-global-filters.test.ts +22 -0
  226. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +149 -12
  227. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview.status-cards.test.ts +27 -0
  228. package/templates/.agents/dashboard/modules/operations/src/react/operations-page-contract.ts +176 -0
  229. package/templates/.agents/dashboard/modules/operations/src/react/operations-period-scope.test.ts +160 -0
  230. package/templates/.agents/dashboard/modules/operations/src/react/operations-period-scope.ts +47 -0
  231. package/templates/.agents/dashboard/modules/operations/src/react/operations-query.ts +10 -0
  232. package/templates/.agents/dashboard/modules/operations/src/react/operations-responsive.css +3 -0
  233. package/templates/.agents/dashboard/modules/operations/src/react/operations-view-labels.ts +28 -0
  234. package/templates/.agents/dashboard/modules/operations/src/react/operations-view-order.test.ts +21 -7
  235. package/templates/.agents/dashboard/modules/operations/src/react/operations.css +42 -0
  236. package/templates/.agents/dashboard/modules/operations/src/react/period-summary-population.test.ts +54 -0
  237. package/templates/.agents/dashboard/modules/operations/src/react/period-summary-population.ts +32 -0
  238. package/templates/.agents/dashboard/modules/operations/src/react/period-summary.test.ts +241 -6
  239. package/templates/.agents/dashboard/modules/operations/src/react/period-summary.ts +148 -6
  240. package/templates/.agents/dashboard/modules/operations/src/react/popup-localization.test.ts +1 -1
  241. package/templates/.agents/dashboard/modules/operations/src/react/unified-work-board.css +132 -0
  242. package/templates/.agents/dashboard/modules/operations/src/react/unified-work-board.css.test.ts +13 -0
  243. package/templates/.agents/dashboard/modules/operations/src/react/unified-work-board.header.test.ts +16 -0
  244. package/templates/.agents/dashboard/modules/operations/src/react/use-infinite-column.ts +11 -5
  245. package/templates/.agents/dashboard/modules/operations/src/react/work-board-model.test.ts +7 -4
  246. package/templates/.agents/dashboard/modules/operations/src/react/work-board-model.ts +5 -84
  247. package/templates/.agents/dashboard/modules/operations/src/server/build-operations-projection.test.ts +291 -15
  248. package/templates/.agents/dashboard/modules/operations/src/server/build-operations-projection.ts +182 -472
  249. package/templates/.agents/dashboard/modules/operations/src/server/canonical-compat.test.ts +40 -0
  250. package/templates/.agents/dashboard/modules/operations/src/server/canonical-compat.ts +19 -6
  251. package/templates/.agents/dashboard/modules/operations/src/server/canonical-run-projection.ts +31 -6
  252. package/templates/.agents/dashboard/modules/operations/src/server/daily-duration-projection.ts +220 -0
  253. package/templates/.agents/dashboard/modules/operations/src/server/execution-duration-projection.ts +28 -0
  254. package/templates/.agents/dashboard/modules/operations/src/server/execution-relationship-counts.test.ts +39 -2
  255. package/templates/.agents/dashboard/modules/operations/src/server/execution-relationship-counts.ts +25 -13
  256. package/templates/.agents/dashboard/modules/operations/src/server/filter.test.ts +3 -3
  257. package/templates/.agents/dashboard/modules/operations/src/server/filter.ts +7 -2
  258. package/templates/.agents/dashboard/modules/operations/src/server/index.ts +3 -0
  259. package/templates/.agents/dashboard/modules/operations/src/server/macro.ts +3 -12
  260. package/templates/.agents/dashboard/modules/operations/src/server/manifest-usage.ts +65 -0
  261. package/templates/.agents/dashboard/modules/operations/src/server/operations-projection-aggregates.ts +297 -0
  262. package/templates/.agents/dashboard/modules/operations/src/server/quick-daily-operations-projection.ts +330 -0
  263. package/templates/.agents/dashboard/modules/operations/src/server/quick-operations-projection.test.ts +505 -0
  264. package/templates/.agents/dashboard/modules/operations/src/server/quick-operations-projection.ts +609 -0
  265. package/templates/.agents/dashboard/modules/operations/src/server/runs.ts +18 -19
  266. package/templates/.agents/dashboard/modules/operations/src/work-board-model.ts +76 -0
  267. package/templates/.agents/dashboard/modules/traceability/package.json +1 -1
  268. package/templates/.agents/dashboard/modules/traceability/src/server/build-task-trace.ts +3 -1
  269. package/templates/.agents/dashboard/packages/branding/package.json +1 -1
  270. package/templates/.agents/dashboard/packages/branding/src/index.ts +1 -1
  271. package/templates/.agents/dashboard/packages/contracts/package.json +1 -1
  272. package/templates/.agents/dashboard/packages/contracts/src/index.ts +2 -0
  273. package/templates/.agents/dashboard/packages/data-export/package.json +1 -1
  274. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +1759 -0
  275. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs.metafile.json +7333 -0
  276. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +1611 -0
  277. package/templates/.agents/dashboard/packages/local-runtime/package.json +8 -3
  278. package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-dashboard.mjs +20 -0
  279. package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-runtime.mjs +114 -0
  280. package/templates/.agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs +31 -0
  281. package/templates/.agents/dashboard/packages/local-runtime/scripts/source-size-runtime-scripts/sqlite-read-model-fixture.mjs +397 -0
  282. package/templates/.agents/dashboard/packages/local-runtime/scripts/test-sqlite-read-model.mjs +589 -0
  283. package/templates/.agents/dashboard/packages/local-runtime/scripts/test-unix-launchers.mjs +53 -0
  284. package/templates/.agents/dashboard/packages/local-runtime/scripts/validate-sqlite-read-model.mjs +203 -0
  285. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +1646 -107
  286. package/templates/.agents/dashboard/packages/local-runtime/src/agent-adapter-sync.ts +30 -0
  287. package/templates/.agents/dashboard/packages/local-runtime/src/agent-catalog-settings.test.ts +3 -2
  288. package/templates/.agents/dashboard/packages/local-runtime/src/agent-catalog-settings.ts +5 -0
  289. package/templates/.agents/dashboard/packages/local-runtime/src/manifest-runs.ts +72 -33
  290. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.test.ts +339 -4
  291. package/templates/.agents/dashboard/packages/local-runtime/src/project-settings.ts +392 -1
  292. package/templates/.agents/dashboard/packages/local-runtime/src/runtime.mjs +5 -5
  293. package/templates/.agents/dashboard/packages/local-runtime/src/server-board.test.ts +110 -0
  294. package/templates/.agents/dashboard/packages/local-runtime/src/server-board.ts +189 -0
  295. package/templates/.agents/dashboard/packages/local-runtime/src/server-cross-layer-parity.test.ts +245 -0
  296. package/templates/.agents/dashboard/packages/local-runtime/src/server-daily-read-models-route.ts +64 -0
  297. package/templates/.agents/dashboard/packages/local-runtime/src/server-daily-read-models.test.ts +204 -0
  298. package/templates/.agents/dashboard/packages/local-runtime/src/server-daily-read-models.ts +194 -0
  299. package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-pages.test.ts +163 -0
  300. package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-pages.ts +354 -0
  301. package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-runs-route.test.ts +74 -0
  302. package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-runs-route.ts +113 -0
  303. package/templates/.agents/dashboard/packages/local-runtime/src/server-overview.test.ts +68 -0
  304. package/templates/.agents/dashboard/packages/local-runtime/src/server-period.ts +47 -0
  305. package/templates/.agents/dashboard/packages/local-runtime/src/server-projections.ts +483 -0
  306. package/templates/.agents/dashboard/packages/local-runtime/src/server-settings.test.ts +5 -2
  307. package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot.test.ts +178 -84
  308. package/templates/.agents/dashboard/packages/local-runtime/src/server-workspace.ts +149 -0
  309. package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +675 -267
  310. package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +1212 -309
  311. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-metric-contract.test.ts +53 -0
  312. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-metric-contract.ts +722 -0
  313. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +3743 -0
  314. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +7017 -0
  315. package/templates/.agents/dashboard/packages/local-runtime/src/work-priority-contract.test.ts +33 -0
  316. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-history.mjs +41 -0
  317. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-processes.mjs +210 -0
  318. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +514 -190
  319. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +324 -24
  320. package/templates/.agents/dashboard/packages/ui/package.json +1 -1
  321. package/templates/.agents/dashboard/packages/windows-launcher/LauncherVerificationBehaviorTests.cs +206 -0
  322. package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +95 -8
  323. package/templates/.agents/dashboard/packages/windows-launcher/build-dashboard-launcher.ps1 +2 -2
  324. package/templates/.agents/dashboard/packages/windows-launcher/run-launcher-verification-tests.ps1 +22 -0
  325. package/templates/.agents/dashboard/read-model.dbml +2201 -0
  326. package/templates/.agents/dashboard/tests/e2e/package.json +1 -1
  327. package/templates/.agents/dashboard/tests/e2e/playwright.config.ts +1 -1
  328. package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +216 -41
  329. package/templates/.agents/dashboard/work-priority.json +34 -0
  330. package/templates/.agents/dashboard/zaip-dashboard-linux.sh +45 -0
  331. package/templates/.agents/dashboard/zaip-dashboard-mac.command +3 -0
  332. package/templates/.agents/dashboard/zaip-dashboard-windows.exe +0 -0
  333. package/templates/.agents/design/.zaip-managed.json +3 -3
  334. package/templates/.agents/design/apps/catalog/package.json +1 -1
  335. package/templates/.agents/design/packages/local-runtime/package.json +1 -1
  336. package/templates/.agents/growth/estrategia-bootstrap-90-dias.md +169 -0
  337. package/templates/.agents/growth/strategy.md +27 -0
  338. package/templates/.agents/marketing/estrategia-marketing-zaip-90-dias.md +197 -0
  339. package/templates/.agents/marketing/strategy.md +29 -0
  340. package/templates/.agents/requirements/autonomous-assurance.md +1 -1
  341. package/templates/.agents/requirements/comparative-audit.md +1 -1
  342. package/templates/.agents/requirements/context-engine.md +1 -1
  343. package/templates/.agents/requirements/cost-observability.md +1 -1
  344. package/templates/.agents/requirements/dashboard-launch.md +24 -0
  345. package/templates/.agents/requirements/data-export.md +1 -1
  346. package/templates/.agents/requirements/design-system.md +1 -1
  347. package/templates/.agents/requirements/execution-engine.md +1 -1
  348. package/templates/.agents/requirements/kanban-dashboard.md +4 -3
  349. package/templates/.agents/requirements/model-orchestration.md +1 -1
  350. package/templates/.agents/requirements/public-distribution.md +9 -6
  351. package/templates/.agents/requirements/software-architecture.md +1 -1
  352. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  353. package/templates/.agents/requirements/work-lifecycle.md +9 -3
  354. package/templates/.agents/requirements/zaip-strategic-95.md +4 -4
  355. package/templates/.agents/rules/autonomy-decision.md +15 -0
  356. package/templates/.agents/rules/core.md +1 -1
  357. package/templates/.agents/rules/run-lifecycle-closure.md +1 -1
  358. package/templates/.agents/rules/specialist-routing.md +19 -0
  359. package/templates/.agents/rules/workflow.md +1 -1
  360. package/templates/.agents/runtime/VERSION +1 -1
  361. package/templates/.agents/runtime/bin/zaip.mjs +118 -67
  362. package/templates/.agents/runtime/checksums.sha256 +6 -4
  363. package/templates/.agents/runtime/manifest.json +3 -1
  364. package/templates/.agents/runtime/pnpm-lock.sha256 +1 -0
  365. package/templates/.agents/runtime/provenance.json +11 -0
  366. package/templates/.agents/runtime/sbom.cdx.json +1929 -39
  367. package/templates/.agents/sales/playbook.md +32 -0
  368. package/templates/.agents/sales/roteiro-comercial-zaip.md +252 -0
  369. package/templates/.agents/skills/zaip-aios/SKILL.md +1 -1
  370. package/templates/.agents/skills/zaip-skill-brand-governance/SKILL.md +22 -0
  371. package/templates/.agents/skills/zaip-skill-brand-governance/agents/openai.yaml +4 -0
  372. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-framework.md +39 -0
  373. package/templates/.agents/skills/zaip-skill-brand-governance/references/brandbook-standards.md +30 -0
  374. package/templates/.agents/skills/zaip-skill-dashboard-sqlite/SKILL.md +25 -0
  375. package/templates/.agents/skills/zaip-skill-dashboard-sqlite/agents/openai.yaml +4 -0
  376. package/templates/.agents/skills/zaip-skill-enforce-architecture/SKILL.md +2 -0
  377. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/SKILL.md +18 -0
  378. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/agents/openai.yaml +6 -0
  379. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/scripts/ready-queue-fence.mjs +71 -0
  380. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/scripts/ready-queue-fence.test.mjs +60 -0
  381. package/templates/.agents/skills/zaip-skill-executar-fila-pronta/scripts/scheduled-ready-queue-contract.test.mjs +111 -0
  382. package/templates/.agents/skills/zaip-skill-execution/SKILL.md +2 -2
  383. package/templates/.agents/skills/zaip-skill-frontend-launcher/references/launcher-contract.md +3 -1
  384. package/templates/.agents/skills/zaip-skill-general-operations/SKILL.md +12 -0
  385. package/templates/.agents/skills/zaip-skill-general-operations/agents/openai.yaml +4 -0
  386. package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +9 -7
  387. package/templates/.agents/skills/zaip-skill-github-deploy/agents/openai.yaml +2 -2
  388. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/github-deploy-contract.test.mjs +11 -38
  389. package/templates/.agents/skills/zaip-skill-governance-compliance/SKILL.md +12 -0
  390. package/templates/.agents/skills/zaip-skill-governance-compliance/agents/openai.yaml +4 -0
  391. package/templates/.agents/skills/zaip-skill-growth-strategy-revops/SKILL.md +21 -0
  392. package/templates/.agents/skills/zaip-skill-growth-strategy-revops/agents/openai.yaml +4 -0
  393. package/templates/.agents/skills/zaip-skill-growth-strategy-revops/references/growth-revops-framework.md +25 -0
  394. package/templates/.agents/skills/zaip-skill-instalar-agendamento/SKILL.md +17 -0
  395. package/templates/.agents/skills/zaip-skill-instalar-agendamento/agents/openai.yaml +6 -0
  396. package/templates/.agents/skills/zaip-skill-marketing/SKILL.md +9 -3
  397. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-local-telemetry.mjs +99 -93
  398. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.mjs +132 -23
  399. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.test.mjs +149 -1
  400. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.mjs +76 -19
  401. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.test.mjs +217 -7
  402. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/source-size-telemetry/model-identity-reconciliation.mjs +77 -0
  403. package/templates/.agents/skills/zaip-skill-sales-methodology/SKILL.md +20 -0
  404. package/templates/.agents/skills/zaip-skill-sales-methodology/agents/openai.yaml +4 -0
  405. package/templates/.agents/skills/zaip-skill-sales-methodology/references/sales-process-framework.md +77 -0
  406. package/templates/.agents/skills/zaip-skill-setup/SKILL.md +5 -0
  407. package/templates/.agents/skills/zaip-skill-sqlite-read-model/SKILL.md +96 -0
  408. package/templates/.agents/skills/zaip-skill-sqlite-read-model/agents/openai.yaml +4 -0
  409. package/templates/.agents/skills/zaip-skill-sqlite-read-model/scripts/ensure-node.mjs +41 -0
  410. package/templates/.agents/zaip-aios/INDEX.md +11 -0
  411. package/templates/.agents/zaip-aios/README.md +38 -8
  412. package/templates/.agents/zaip-aios/VERSION +1 -1
  413. package/templates/.agents/zaip-aios/guides/09-agentes-e-squads.md +43 -6
  414. package/templates/.agents/zaip-aios/guides/10-seguranca.md +21 -7
  415. package/templates/.agents/zaip-aios/guides/17-como-o-custo-e-calculado.md +32 -0
  416. package/templates/.agents/zaip-aios/guides/18-catalogo-de-agentes.md +76 -0
  417. package/templates/.agents/zaip-aios/guides/19-assurance-e-autonomia.md +72 -0
  418. package/templates/.agents/zaip-aios/guides/20-instalacao-detalhada.md +330 -0
  419. package/templates/.agents/zaip-aios/guides/21-telemetria-monitor-custos.md +52 -0
  420. package/templates/.agents/zaip-aios/guides/22-fluxos-e-roteamento.md +69 -0
  421. package/templates/.agents/zaip-aios/guides/23-orquestracao-de-modelos.md +483 -0
  422. package/templates/.agents/zaip-aios/guides/24-calculo-de-custos-openai.md +85 -0
  423. package/templates/.agents/zaip-aios/guides/25-fontes-de-precos-openai.md +18 -0
  424. package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +44 -0
  425. package/templates/.agents/zaip-aios/guides/27-telemetria-git-native.md +167 -0
  426. package/templates/.agents/zaip-aios/guides/28-lifecycle-completo.md +144 -0
  427. package/templates/.agents/zaip-aios/guides/assets/diagrams/lifecycles-task-spec-prd.png +0 -0
  428. package/templates/.agents/zaip-aios/guides/assets/diagrams/prd-lifecycle.png +0 -0
  429. package/templates/.agents/zaip-aios/guides/assets/diagrams/spec-lifecycle.png +0 -0
  430. package/templates/.agents/zaip-aios/guides/assets/diagrams/task-lifecycle.png +0 -0
  431. package/templates/template-manifest.json +1082 -215
  432. package/templates/.agents/dashboard/apps/frontend/dist/assets/AgentCatalogSettingsPage-CLYIi7Vo.js +0 -1
  433. package/templates/.agents/dashboard/apps/frontend/dist/assets/AgentCatalogSettingsPage-DTDNVQGX.css +0 -1
  434. package/templates/.agents/dashboard/apps/frontend/dist/assets/DashboardHelpDialog-Cv-JcoxP.js +0 -1320
  435. package/templates/.agents/dashboard/apps/frontend/dist/assets/PricingSettingsDialog-CzEggcRb.js +0 -1
  436. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-05YKn6Z4.js +0 -3
  437. package/templates/.agents/dashboard/apps/frontend/dist/assets/ProjectSettingsPage-YO3yqtgV.css +0 -1
  438. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-8AzCnl3a.js +0 -77
  439. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-DdBYQ_8D.css +0 -1
  440. package/templates/.agents/dashboard/apps/frontend/src/AgentCatalogSettingsPage.tsx +0 -617
  441. package/templates/.agents/dashboard/apps/frontend/src/agent-catalog-settings-page.css +0 -660
  442. package/templates/.agents/dashboard/apps/frontend/src/agent-catalog-settings.test.ts +0 -249
  443. package/templates/.agents/dashboard/dashboard.config.json +0 -4
  444. package/templates/.agents/dashboard/zaip-dashboard.exe +0 -0
@@ -6,29 +6,36 @@ import {
6
6
  formatPercent,
7
7
  } from "@zaip-ai/dashboard-ui";
8
8
  import { type ReactNode, useState } from "react";
9
- import type { MacroProjection, OperationsProjection } from "../contracts.js";
10
- import {
11
- allPhaseMetricTotals,
12
- effortMetricTotals,
13
- executionMetricTotals,
14
- globalLegacyEffortMetricDuration,
15
- } from "./execution-metrics.js";
9
+ import type {
10
+ DashboardTaskProjection,
11
+ MacroProjection,
12
+ OperationsProjection,
13
+ } from "../contracts.js";
14
+ import { allPhaseMetricTotals, executionMetricTotals } from "./execution-metrics.js";
15
+ import type { EntityMetric } from "./OperationsOverview.helpers.js";
16
16
  import {
17
17
  aggregateMacroMetrics,
18
18
  aggregateTaskMetrics,
19
- averageObserved,
20
19
  countExecutionDays,
21
20
  findLatestCreatedAt,
22
21
  formatExecutionAverageHint,
23
22
  Kpi,
24
23
  macroClassificationStats,
25
24
  overviewCopy,
25
+ overviewMetricAverages,
26
26
  overviewText,
27
27
  riskColors,
28
+ selectTopEntities,
28
29
  statusColors,
29
30
  summarizeTopEntityMetrics,
30
31
  } from "./OperationsOverview.helpers.js";
31
- import type { OperationDatePreset } from "./operation-date-range.js";
32
+ import type { OperationDatePreset, OperationDateRange } from "./operation-date-range.js";
33
+ import type {
34
+ OverviewClassification,
35
+ OverviewClassificationPage,
36
+ OverviewStatusKind,
37
+ OverviewStatusPage,
38
+ } from "./operations-page-contract.js";
32
39
  import { type OperationsLocale, riskLabel } from "./status-labels.js";
33
40
  import {
34
41
  macroWorkStage,
@@ -42,14 +49,15 @@ import "./operations-overview.css";
42
49
  export {
43
50
  aggregateMacroMetrics,
44
51
  aggregateTaskMetrics,
45
- averageObserved,
46
52
  countExecutionDays,
47
53
  findLatestCreatedAt,
48
54
  findLatestTaskCreatedAt,
49
55
  formatExecutionAverageHint,
50
56
  formatTokenThroughputHint,
51
57
  macroClassificationStats,
58
+ overviewMetricAverages,
52
59
  overviewText,
60
+ selectTopEntities,
53
61
  statusColors,
54
62
  summarizeTopEntityMetrics,
55
63
  summarizeTopTaskMetrics,
@@ -57,15 +65,78 @@ export {
57
65
 
58
66
  type OverviewView = "tasks" | "specs" | "prds";
59
67
 
60
- function formatRoundedCurrency(value: number, locale: OperationsLocale): string {
68
+ function formatRoundedCurrency(
69
+ value: number,
70
+ currency: "BRL" | "USD",
71
+ locale: OperationsLocale,
72
+ ): string {
61
73
  return new Intl.NumberFormat(locale, {
62
74
  style: "currency",
63
- currency: "BRL",
75
+ currency,
64
76
  maximumFractionDigits: 0,
65
77
  }).format(value);
66
78
  }
67
79
 
68
- function runsForOverviewView(
80
+ function formatEntityDuration(value: number | null, locale: OperationsLocale): string {
81
+ if (value !== null) return formatDurationMs(value, locale);
82
+ return locale === "pt-BR" ? "Indisponível" : "Unavailable";
83
+ }
84
+
85
+ export function taskMetricAverages(
86
+ totals: { durationMs: number | null; tokens: number | null; costBrl: number | null },
87
+ taskCount: number | null,
88
+ ) {
89
+ return overviewMetricAverages(totals, taskCount);
90
+ }
91
+
92
+ export function selectedOverviewCount(
93
+ view: OverviewView,
94
+ overviewMetrics: OperationsProjection["summary"]["overview_metrics"] | undefined,
95
+ fallback: number,
96
+ ): number | null {
97
+ if (view === "specs") return overviewMetrics?.selected_counts?.specs ?? fallback;
98
+ if (view === "prds") return overviewMetrics?.selected_counts?.prds ?? fallback;
99
+ return overviewMetrics?.task_count ?? fallback;
100
+ }
101
+
102
+ /**
103
+ * Selects a currency-specific total without ever treating Operations' BRL
104
+ * aggregate as USD. Compact Cost Monitor summaries are authoritative even
105
+ * when their intentionally bounded Run collection is empty.
106
+ */
107
+ export function resolveOverviewCostTotal({
108
+ currency,
109
+ factualSnapshotTotal,
110
+ macroCostTotal,
111
+ overviewView,
112
+ resolvedRunTotal,
113
+ operationsBrlTotal,
114
+ }: {
115
+ currency: "BRL" | "USD";
116
+ factualSnapshotTotal: number | null | undefined;
117
+ /** Macro aggregates are scoped to the selected SPEC/PRD, unlike the Task snapshot. */
118
+ macroCostTotal?: number | null | undefined;
119
+ overviewView?: OverviewView;
120
+ resolvedRunTotal: number | null;
121
+ operationsBrlTotal: number | null;
122
+ }): number | null {
123
+ if (overviewView !== undefined && overviewView !== "tasks") return macroCostTotal ?? null;
124
+ if (factualSnapshotTotal !== null && factualSnapshotTotal !== undefined)
125
+ return factualSnapshotTotal;
126
+ if (resolvedRunTotal !== null) return resolvedRunTotal;
127
+ return currency === "BRL" ? operationsBrlTotal : null;
128
+ }
129
+
130
+ /**
131
+ * KPI cards are sourced exclusively from the server-selected global aggregate.
132
+ * Entity, board, and Cost Monitor pages are bounded/detail views, so their
133
+ * collections must never replace the period-filtered big-number population.
134
+ */
135
+ export function partialOverviewMetrics(data: OperationsProjection) {
136
+ return data.summary.overview_metrics;
137
+ }
138
+
139
+ export function runsForOverviewView(
69
140
  view: OverviewView,
70
141
  tasks: OperationsProjection["tasks"],
71
142
  macros: OperationsProjection["specs"] | OperationsProjection["prds"],
@@ -87,113 +158,319 @@ function runsForOverviewView(
87
158
  }
88
159
 
89
160
  export function OperationsOverview({
161
+ currency,
90
162
  data,
91
163
  datePreset,
92
164
  locale,
93
165
  onDatePresetChange,
94
166
  onOpenTask,
167
+ onOpenDocument,
168
+ onLoadStatus,
169
+ statusPage,
170
+ onLoadStatusPage,
171
+ classificationPage,
172
+ onLoadClassificationPage,
95
173
  prds = data.prds,
96
174
  runs = data.runs,
97
175
  specs = data.specs,
98
176
  tasks = data.tasks,
99
- resolveRunCostBrl,
177
+ factualCostTotal,
178
+ resolveRunCost,
100
179
  includeLegacyEffort = false,
101
180
  }: {
181
+ currency: "BRL" | "USD";
102
182
  data: OperationsProjection;
183
+ /** App-controlled range paired with datePreset; retained by Page while loading. */
184
+ dateRange?: OperationDateRange;
103
185
  datePreset: OperationDatePreset;
104
186
  locale: OperationsLocale;
105
187
  onDatePresetChange: (preset: OperationDatePreset) => void;
106
188
  onOpenTask: (taskId: string) => Promise<boolean>;
189
+ onOpenDocument: (kind: "prd" | "spec", id: string) => void;
190
+ onLoadStatus?: ((stage: WorkStage, kind: OverviewStatusKind, offset: number) => void) | undefined;
191
+ statusPage?:
192
+ | ((stage: WorkStage, kind: OverviewStatusKind) => OverviewStatusPage | undefined)
193
+ | undefined;
194
+ onLoadStatusPage?:
195
+ | ((stage: WorkStage, kind: OverviewStatusKind, offset: number) => void)
196
+ | undefined;
197
+ classificationPage?:
198
+ | ((
199
+ kind: OverviewStatusKind,
200
+ classification: OverviewClassification,
201
+ ) => OverviewClassificationPage | undefined)
202
+ | undefined;
203
+ onLoadClassificationPage?:
204
+ | ((kind: OverviewStatusKind, classification: OverviewClassification, offset: number) => void)
205
+ | undefined;
107
206
  prds?: OperationsProjection["prds"];
108
207
  runs?: OperationsProjection["runs"];
109
208
  specs?: OperationsProjection["specs"];
110
209
  tasks?: OperationsProjection["tasks"];
111
- resolveRunCostBrl?: ((runId: string) => number | null) | undefined;
210
+ /** Factual Cost Monitor snapshot total for the selected locale/currency. */
211
+ factualCostTotal?: number | null | undefined;
212
+ resolveRunCost?: ((runId: string) => number | null) | undefined;
112
213
  includeLegacyEffort?: boolean;
113
214
  }) {
114
215
  const [overviewView, setOverviewView] = useState<OverviewView>("tasks");
115
216
  const [selectedStatus, setSelectedStatus] = useState<WorkStage | null>(null);
116
- const [selectedRisk, setSelectedRisk] = useState<
117
- OperationsProjection["tasks"][number]["risk"] | null
118
- >(null);
217
+ const [selectedClassification, setSelectedClassification] =
218
+ useState<OverviewClassification | null>(null);
119
219
  const completed = tasks.filter((task) => task.status === "completed").length;
120
220
  const completedSpecs = specs.filter((spec) => spec.work_status === "completed").length;
121
221
  const completedPrds = prds.filter((prd) => prd.work_status === "completed").length;
122
- const taskCompletion = tasks.length === 0 ? 0 : completed / tasks.length;
123
- const specCompletion = specs.length === 0 ? 0 : completedSpecs / specs.length;
124
- const prdCompletion = prds.length === 0 ? 0 : completedPrds / prds.length;
125
222
  const copy = overviewCopy(overviewView, locale);
126
223
  const text = overviewText(locale);
127
224
  const selectedMacros = overviewView === "specs" ? specs : prds;
128
- const selectedCount = overviewView === "tasks" ? tasks.length : selectedMacros.length;
225
+ const overviewMetrics = partialOverviewMetrics(data);
226
+ const selectedMacroAggregate =
227
+ overviewView === "tasks" ? undefined : overviewMetrics?.macro_metrics?.[overviewView];
228
+ const selectedCount = selectedOverviewCount(
229
+ overviewView,
230
+ overviewMetrics,
231
+ overviewView === "tasks" ? tasks.length : selectedMacros.length,
232
+ );
129
233
  const selectedMetricRuns = runsForOverviewView(overviewView, tasks, selectedMacros, runs);
130
- const entityMetrics =
234
+ const entityMetrics: EntityMetric[] =
131
235
  overviewView === "tasks"
132
- ? aggregateTaskMetrics(tasks, selectedMetricRuns, resolveRunCostBrl)
133
- : aggregateMacroMetrics(selectedMacros, selectedMetricRuns, resolveRunCostBrl);
134
- const averageDuration = averageObserved(entityMetrics.map((entity) => entity.durationMs));
135
- const taskStatusStats = taskStatusDistribution(tasks, locale);
136
- const macroStatusStats = macroStatusDistribution(selectedMacros, tasks, locale);
236
+ ? aggregateTaskMetrics(tasks, selectedMetricRuns, resolveRunCost)
237
+ : aggregateMacroMetrics(selectedMacros, selectedMetricRuns, resolveRunCost);
238
+ const aggregateTaskStatusCounts = overviewMetrics?.task_status_counts;
239
+ const taskStatusStats = aggregateTaskStatusCounts
240
+ ? [
241
+ ...workStages.map((stage) => ({
242
+ key: stage,
243
+ label: workStageLabel(stage, locale),
244
+ color: statusColors[stage],
245
+ count:
246
+ stage === "definition"
247
+ ? (aggregateTaskStatusCounts.draft ?? 0) + (aggregateTaskStatusCounts.definition ?? 0)
248
+ : stage === "ready"
249
+ ? (aggregateTaskStatusCounts.planned ?? 0) + (aggregateTaskStatusCounts.ready ?? 0)
250
+ : (aggregateTaskStatusCounts[stage] ?? 0),
251
+ })),
252
+ ...(aggregateTaskStatusCounts.unclassified
253
+ ? [
254
+ {
255
+ key: "unclassified",
256
+ label: locale === "pt-BR" ? "Não classificado" : "Unclassified",
257
+ color: "var(--color-neutral-500)",
258
+ count: aggregateTaskStatusCounts.unclassified,
259
+ },
260
+ ]
261
+ : []),
262
+ ]
263
+ : taskStatusDistribution(tasks, locale);
264
+ const macroStatusStats = selectedMacroAggregate
265
+ ? workStages.map((stage) => ({
266
+ key: stage,
267
+ label: workStageLabel(stage, locale),
268
+ color: statusColors[stage],
269
+ count: selectedMacroAggregate.status_counts[stage] ?? 0,
270
+ }))
271
+ : macroStatusDistribution(selectedMacros, tasks, locale);
137
272
  const statusStats = overviewView === "tasks" ? taskStatusStats : macroStatusStats;
138
273
  const statusTotal = statusStats.reduce((total, item) => total + item.count, 0);
139
- const taskRiskStats = Object.entries(riskColors).map(([risk, color]) => ({
140
- key: risk,
141
- label: riskLabel(risk as OperationsProjection["tasks"][number]["risk"], locale),
142
- color,
143
- count: tasks.filter((task) => task.risk === risk).length,
144
- }));
274
+ const aggregateTaskRiskCounts = overviewMetrics?.task_risk_counts;
275
+ const taskRiskStats = [
276
+ ...Object.entries(riskColors).map(([risk, color]) => ({
277
+ key: risk,
278
+ label: riskLabel(risk as OperationsProjection["tasks"][number]["risk"], locale),
279
+ color,
280
+ count: aggregateTaskRiskCounts
281
+ ? (aggregateTaskRiskCounts[risk] ?? 0)
282
+ : tasks.filter((task) => task.risk === risk).length,
283
+ })),
284
+ ...((aggregateTaskRiskCounts?.unclassified ?? tasks.filter((task) => task.risk === null).length)
285
+ ? [
286
+ {
287
+ key: "unclassified",
288
+ label: locale === "pt-BR" ? "Não classificado" : "Unclassified",
289
+ color: "var(--color-neutral-500)",
290
+ count:
291
+ aggregateTaskRiskCounts?.unclassified ??
292
+ tasks.filter((task) => task.risk === null).length,
293
+ },
294
+ ]
295
+ : []),
296
+ ];
145
297
  const classificationStats =
146
- overviewView === "tasks" ? taskRiskStats : macroClassificationStats(selectedMacros, locale);
147
- // Global cards and period tables use every filtered invocation. Rankings
148
- // remain entity-attributable, so a Run without a Task/SPEC/PRD link does not
149
- // disappear from the operational total merely because it cannot be ranked.
298
+ overviewView === "tasks"
299
+ ? taskRiskStats
300
+ : selectedMacroAggregate
301
+ ? macroClassificationStats([], locale).map((row) => ({
302
+ ...row,
303
+ count: selectedMacroAggregate.classification_counts[row.key] ?? 0,
304
+ }))
305
+ : macroClassificationStats(selectedMacros, locale);
306
+ // Duration cards follow strict root execution invocations; financial usage
307
+ // remains an all-phase metric.
150
308
  const executionMetrics = executionMetricTotals(runs);
151
- const effortMetrics = effortMetricTotals(runs);
152
309
  const allPhaseMetrics = allPhaseMetricTotals(runs);
153
- const overviewMetrics = runs.length === 0 ? data.summary.overview_metrics : undefined;
310
+ // The aggregate is canonical for big numbers even after a detail, board, or
311
+ // Cost Monitor navigation populates bounded local collections.
312
+ const overviewIsPartial = overviewMetrics?.partial === true;
313
+ const taskCount = overviewMetrics?.task_count ?? tasks.length;
314
+ const specCount = overviewMetrics?.spec_count ?? specs.length;
315
+ const prdCount = overviewMetrics?.prd_count ?? prds.length;
316
+ const completedTaskCount = overviewMetrics?.completed_task_count ?? completed;
317
+ const completedSpecCount = overviewMetrics?.completed_spec_count ?? completedSpecs;
318
+ const completedPrdCount = overviewMetrics?.completed_prd_count ?? completedPrds;
319
+ const taskCompletion = taskCount === null || taskCount === 0 ? 0 : completedTaskCount / taskCount;
320
+ const specCompletion = specCount === null || specCount === 0 ? 0 : completedSpecCount / specCount;
321
+ const prdCompletion = prdCount === null || prdCount === 0 ? 0 : completedPrdCount / prdCount;
154
322
  const executionRuns = executionMetrics.runs;
155
- const totalDuration =
156
- overviewMetrics?.duration_ms ??
157
- (includeLegacyEffort ? globalLegacyEffortMetricDuration(runs) : effortMetrics.durationMs);
323
+ const totalDurationMetric = overviewMetrics?.duration_ms ?? executionMetrics.durationMs;
158
324
  const totalExecutionDays =
159
325
  overviewMetrics?.execution_day_count ?? countExecutionDays(executionRuns);
160
- const totalTokens = overviewMetrics?.tokens ?? allPhaseMetrics.tokens;
161
- const totalCost = resolveRunCostBrl
162
- ? allPhaseMetrics.runs.reduce<number | null>((total, run) => {
163
- const value = resolveRunCostBrl(run.id);
164
- return value === null ? total : (total ?? 0) + value;
165
- }, null)
166
- : (overviewMetrics?.cost_brl ?? allPhaseMetrics.costBrl);
326
+ const totalTokensMetric = overviewMetrics?.tokens ?? allPhaseMetrics.tokens;
327
+ const selectedCurrencyCost =
328
+ resolveRunCost === undefined
329
+ ? null
330
+ : allPhaseMetrics.runs.reduce<number | null>((total, run) => {
331
+ const value = resolveRunCost(run.id);
332
+ return value === null ? total : (total ?? 0) + value;
333
+ }, null);
334
+ const selectedMacroCost =
335
+ overviewView === "tasks"
336
+ ? undefined
337
+ : currency === "USD"
338
+ ? selectedMacroAggregate?.total_cost_usd
339
+ : selectedMacroAggregate?.total_cost_brl;
340
+ // Operations aggregates are BRL-only. USD is valid only when it is resolved
341
+ // from Cost Monitor's factual per-Run estimates; never relabel BRL as USD.
342
+ const totalCostMetric = resolveOverviewCostTotal({
343
+ currency,
344
+ factualSnapshotTotal: factualCostTotal,
345
+ macroCostTotal: selectedMacroCost,
346
+ overviewView,
347
+ resolvedRunTotal: selectedCurrencyCost,
348
+ operationsBrlTotal: overviewMetrics?.cost_brl ?? allPhaseMetrics.costBrl,
349
+ });
167
350
  const hourlyCost =
168
- totalDuration === null || totalDuration <= 0 || totalCost === null
351
+ totalDurationMetric === null || totalDurationMetric <= 0 || totalCostMetric === null
352
+ ? null
353
+ : (totalCostMetric * 3_600_000) / totalDurationMetric;
354
+ const costPerTask =
355
+ totalCostMetric === null || taskCount === null || taskCount === 0
169
356
  ? null
170
- : (totalCost * 3_600_000) / totalDuration;
171
- const costPerTask = totalCost === null || tasks.length === 0 ? null : totalCost / tasks.length;
357
+ : totalCostMetric / taskCount;
172
358
  const tokensPerTask =
173
- totalTokens === null || tasks.length === 0 ? null : totalTokens / tasks.length;
359
+ totalTokensMetric === null || taskCount === null || taskCount === 0
360
+ ? null
361
+ : totalTokensMetric / taskCount;
174
362
  const tokensPerHour =
175
- totalDuration === null || totalDuration <= 0 || totalTokens === null
363
+ totalDurationMetric === null || totalDurationMetric <= 0 || totalTokensMetric === null
176
364
  ? null
177
- : (totalTokens * 3_600_000) / totalDuration;
178
- const averageTokens = averageObserved(entityMetrics.map((entity) => entity.tokens));
179
- const averageCost = averageObserved(entityMetrics.map((entity) => entity.costBrl));
180
- const latestCreatedAt = findLatestCreatedAt(overviewView === "tasks" ? tasks : selectedMacros);
365
+ : (totalTokensMetric * 3_600_000) / totalDurationMetric;
366
+ const aggregateAverages = overviewMetricAverages(
367
+ { durationMs: totalDurationMetric, tokens: totalTokensMetric, costBrl: totalCostMetric },
368
+ selectedCount,
369
+ );
370
+ const averageDuration =
371
+ selectedMacroAggregate?.average_duration_ms ?? aggregateAverages.durationMs;
372
+ const averageTokens = selectedMacroAggregate?.average_tokens ?? aggregateAverages.tokens;
373
+ const averageCost =
374
+ overviewView !== "tasks"
375
+ ? ((currency === "USD"
376
+ ? selectedMacroAggregate?.average_cost_usd
377
+ : selectedMacroAggregate?.average_cost_brl) ?? null)
378
+ : aggregateAverages.costBrl;
379
+ const latestCreatedAt =
380
+ overviewView === "tasks"
381
+ ? tasks.length === 0
382
+ ? (overviewMetrics?.latest_task_created_at ?? null)
383
+ : findLatestCreatedAt(tasks)
384
+ : (selectedMacroAggregate?.latest_created_at ?? findLatestCreatedAt(selectedMacros));
181
385
  const metricStats = [
182
- [text.averageTime, averageDuration === null ? "—" : formatDurationMs(averageDuration, locale)],
183
- [text.averageTokens, formatCompactNumber(averageTokens, locale)],
184
- [text.averageCost, formatCurrency(averageCost, "BRL", locale)],
386
+ [text.averageTime, formatEntityDuration(averageDuration, locale)],
387
+ [
388
+ text.averageTokens,
389
+ averageTokens === null
390
+ ? formatEntityDuration(null, locale)
391
+ : formatCompactNumber(averageTokens, locale),
392
+ ],
393
+ [
394
+ text.averageCost,
395
+ averageCost === null
396
+ ? formatEntityDuration(null, locale)
397
+ : formatCurrency(averageCost, currency, locale),
398
+ ],
185
399
  ] as const;
186
- const topEntities = entityMetrics
187
- .filter((entity) => entity.durationMs !== null)
188
- .sort((a, b) => (b.durationMs ?? 0) - (a.durationMs ?? 0))
189
- .slice(0, 5);
400
+ const aggregateTopTasks: EntityMetric[] = (overviewMetrics?.top_tasks ?? []).map((metric) => ({
401
+ id: metric.task_id,
402
+ title: tasks.find((task) => task.id === metric.task_id)?.title ?? metric.task_id,
403
+ durationMs: metric.duration_ms,
404
+ durationProvenance: metric.duration_ms === null ? "unavailable" : "observed",
405
+ tokens: metric.total_tokens,
406
+ // EntityMetric predates the global locale selector. It is a selected
407
+ // display-currency bucket here, never a BRL value relabelled as USD.
408
+ costBrl: currency === "USD" ? (metric.total_cost_usd ?? null) : metric.cost_brl,
409
+ runCount: metric.run_count,
410
+ }));
411
+ const aggregateTopMacros: EntityMetric[] = (selectedMacroAggregate?.top_entities ?? []).map(
412
+ (metric) => ({
413
+ id: metric.entity_id,
414
+ title: metric.title,
415
+ durationMs: metric.duration_ms,
416
+ durationProvenance: metric.duration_ms === null ? "unavailable" : "observed",
417
+ tokens: metric.total_tokens,
418
+ costBrl: currency === "USD" ? (metric.total_cost_usd ?? null) : metric.cost_brl,
419
+ runCount: metric.run_count,
420
+ }),
421
+ );
422
+ const topEntities =
423
+ overviewView === "tasks" && runs.length === 0 && aggregateTopTasks.length > 0
424
+ ? aggregateTopTasks
425
+ : overviewView !== "tasks" && runs.length === 0 && aggregateTopMacros.length > 0
426
+ ? aggregateTopMacros
427
+ : selectTopEntities(entityMetrics);
190
428
  const maxDuration = Math.max(1, ...topEntities.map((entity) => entity.durationMs ?? 0));
191
- const topEntitiesSummary = summarizeTopEntityMetrics(topEntities, entityMetrics);
429
+ const topEntitiesSummary =
430
+ overviewView === "tasks" && runs.length === 0 && aggregateTopTasks.length > 0
431
+ ? {
432
+ ...summarizeTopEntityMetrics(topEntities, topEntities),
433
+ durationShare:
434
+ totalDurationMetric === null || totalDurationMetric === 0
435
+ ? 0
436
+ : topEntities.reduce((sum, entity) => sum + (entity.durationMs ?? 0), 0) /
437
+ totalDurationMetric,
438
+ }
439
+ : overviewView !== "tasks" && runs.length === 0 && aggregateTopMacros.length > 0
440
+ ? {
441
+ ...summarizeTopEntityMetrics(topEntities, topEntities),
442
+ durationShare:
443
+ selectedMacroAggregate?.total_duration_ms === null ||
444
+ selectedMacroAggregate?.total_duration_ms === undefined ||
445
+ selectedMacroAggregate.total_duration_ms === 0
446
+ ? 0
447
+ : topEntities.reduce((sum, entity) => sum + (entity.durationMs ?? 0), 0) /
448
+ selectedMacroAggregate.total_duration_ms,
449
+ }
450
+ : summarizeTopEntityMetrics(topEntities, entityMetrics);
451
+ // Aggregate top-entity snapshots in Operations are BRL-only. A selected USD
452
+ // value is displayable only when the visible entities were derived from the
453
+ // factual per-Run Cost Monitor resolver.
454
+ const usesAggregateTopEntities =
455
+ (overviewView === "tasks" && runs.length === 0 && aggregateTopTasks.length > 0) ||
456
+ (overviewView !== "tasks" && runs.length === 0 && aggregateTopMacros.length > 0);
457
+ const topEntitiesCost =
458
+ currency === "USD" &&
459
+ !usesAggregateTopEntities &&
460
+ (runs.length === 0 || resolveRunCost === undefined)
461
+ ? null
462
+ : topEntitiesSummary.costBrl;
463
+ const topHint =
464
+ overviewView === "tasks"
465
+ ? text.topHint
466
+ : locale === "pt-BR"
467
+ ? "Tempo de ciclo de vida (Runs-raiz, todas as fases)."
468
+ : "Lifecycle time (root Runs, all phases).";
192
469
 
193
470
  const selectOverviewView = (view: OverviewView) => {
194
471
  setOverviewView(view);
195
472
  setSelectedStatus(null);
196
- setSelectedRisk(null);
473
+ setSelectedClassification(null);
197
474
  };
198
475
 
199
476
  return (
@@ -230,34 +507,52 @@ export function OperationsOverview({
230
507
  <div className="operations-kpi-grid">
231
508
  <Kpi
232
509
  featured={overviewView === "tasks"}
233
- hint={`${completed} ${text.completedTasks} (${formatPercent(taskCompletion, locale)})`}
510
+ hint={
511
+ overviewIsPartial && completedTaskCount === null
512
+ ? locale === "pt-BR"
513
+ ? "Resumo detalhado carregando…"
514
+ : "Detailed summary loading…"
515
+ : `${completedTaskCount} ${text.completedTasks} (${formatPercent(taskCompletion, locale)})`
516
+ }
234
517
  label={text.tasks}
235
518
  onSelect={() => selectOverviewView("tasks")}
236
- value={String(tasks.length)}
519
+ value={taskCount === null ? "—" : String(taskCount)}
237
520
  />
238
521
  <Kpi
239
522
  featured={overviewView === "specs"}
240
- hint={`${completedSpecs} ${text.completedSpecs} (${formatPercent(specCompletion, locale)})`}
523
+ hint={
524
+ overviewIsPartial && completedSpecCount === null
525
+ ? locale === "pt-BR"
526
+ ? "Resumo detalhado carregando…"
527
+ : "Detailed summary loading…"
528
+ : `${completedSpecCount} ${text.completedSpecs} (${formatPercent(specCompletion, locale)})`
529
+ }
241
530
  label="SPECs"
242
531
  onSelect={() => selectOverviewView("specs")}
243
- value={String(specs.length)}
532
+ value={specCount === null ? "—" : String(specCount)}
244
533
  />
245
534
  <Kpi
246
535
  featured={overviewView === "prds"}
247
- hint={`${completedPrds} ${text.completedPrds} (${formatPercent(prdCompletion, locale)})`}
536
+ hint={
537
+ overviewIsPartial && completedPrdCount === null
538
+ ? locale === "pt-BR"
539
+ ? "Resumo detalhado carregando…"
540
+ : "Detailed summary loading…"
541
+ : `${completedPrdCount} ${text.completedPrds} (${formatPercent(prdCompletion, locale)})`
542
+ }
248
543
  label="PRDs"
249
544
  onSelect={() => selectOverviewView("prds")}
250
- value={String(prds.length)}
545
+ value={prdCount === null ? "—" : String(prdCount)}
251
546
  />
252
547
  <Kpi
253
548
  hint={formatExecutionAverageHint(
254
- totalDuration ?? 0,
255
- tasks.length,
549
+ totalDurationMetric ?? 0,
550
+ taskCount ?? 0,
256
551
  totalExecutionDays,
257
552
  locale,
258
553
  )}
259
554
  label={text.totalExecutionTime}
260
- value={totalDuration === null ? "—" : formatDurationMs(totalDuration, locale)}
555
+ value={formatEntityDuration(totalDurationMetric, locale)}
261
556
  />
262
557
  <Kpi
263
558
  hint={
@@ -270,7 +565,11 @@ export function OperationsOverview({
270
565
  : `${formatCompactNumber(tokensPerTask, locale)} / task (${formatCompactNumber(tokensPerHour, locale, 0)} / hour)`
271
566
  }
272
567
  label={locale === "pt-BR" ? "Tokens gastos" : "Spent tokens"}
273
- value={totalTokens === null ? "—" : formatCompactNumber(totalTokens, locale)}
568
+ value={
569
+ totalTokensMetric === null
570
+ ? formatEntityDuration(null, locale)
571
+ : formatCompactNumber(totalTokensMetric, locale)
572
+ }
274
573
  />
275
574
  <Kpi
276
575
  hint={
@@ -279,11 +578,15 @@ export function OperationsOverview({
279
578
  ? "Custo por tarefa e por hora indispon\u00edvel"
280
579
  : "Cost per task and hour unavailable"
281
580
  : locale === "pt-BR"
282
- ? `${formatRoundedCurrency(costPerTask, locale)} / tarefa (${formatRoundedCurrency(hourlyCost, locale)} / hora)`
283
- : `${formatRoundedCurrency(costPerTask, locale)} / task (${formatRoundedCurrency(hourlyCost, locale)} / hour)`
581
+ ? `${formatRoundedCurrency(costPerTask, currency, locale)} / tarefa (${formatRoundedCurrency(hourlyCost, currency, locale)} / hora)`
582
+ : `${formatRoundedCurrency(costPerTask, currency, locale)} / task (${formatRoundedCurrency(hourlyCost, currency, locale)} / hour)`
284
583
  }
285
584
  label={locale === "pt-BR" ? "Custo Estimado" : "Estimated cost"}
286
- value={totalCost === null ? "—" : formatRoundedCurrency(totalCost, locale)}
585
+ value={
586
+ totalCostMetric === null
587
+ ? formatEntityDuration(null, locale)
588
+ : formatRoundedCurrency(totalCostMetric, currency, locale)
589
+ }
287
590
  />
288
591
  </div>
289
592
 
@@ -316,12 +619,20 @@ export function OperationsOverview({
316
619
  </span>
317
620
  </>
318
621
  );
319
- return overviewView === "tasks" ? (
622
+ return item.key !== "unclassified" ? (
320
623
  <button
321
624
  aria-label={`${item.label}: ${item.count}, ${formatPercent(statusTotal === 0 ? 0 : item.count / statusTotal, locale)}`}
322
625
  className="operations-legend-row operations-status-filter"
323
626
  key={item.key}
324
- onClick={() => setSelectedStatus(item.key as WorkStage)}
627
+ onClick={() => {
628
+ const stage = item.key as WorkStage;
629
+ setSelectedStatus(stage);
630
+ onLoadStatus?.(
631
+ stage,
632
+ overviewView === "tasks" ? "task" : overviewView === "specs" ? "spec" : "prd",
633
+ 0,
634
+ );
635
+ }}
325
636
  type="button"
326
637
  >
327
638
  {content}
@@ -341,10 +652,12 @@ export function OperationsOverview({
341
652
  <div
342
653
  aria-hidden="true"
343
654
  className="operations-donut"
344
- style={{ background: donutGradient(classificationStats, Math.max(selectedCount, 1)) }}
655
+ style={{
656
+ background: donutGradient(classificationStats, Math.max(selectedCount ?? 0, 1)),
657
+ }}
345
658
  />
346
659
  <div>
347
- <strong>{selectedCount}</strong>
660
+ <strong>{selectedCount ?? "—"}</strong>
348
661
  <span>{copy.shortPlural}</span>
349
662
  </div>
350
663
  </div>
@@ -357,19 +670,30 @@ export function OperationsOverview({
357
670
  <span className="operations-legend-value">
358
671
  <strong>{item.count}</strong>
359
672
  <small>
360
- {formatPercent(selectedCount === 0 ? 0 : item.count / selectedCount, locale)}
673
+ {formatPercent(
674
+ selectedCount === null || selectedCount === 0
675
+ ? 0
676
+ : item.count / selectedCount,
677
+ locale,
678
+ )}
361
679
  </small>
362
680
  </span>
363
681
  </>
364
682
  );
365
- return overviewView === "tasks" ? (
683
+ return item.key !== "unclassified" ? (
366
684
  <button
367
- aria-label={`${item.label}: ${item.count}, ${formatPercent(selectedCount === 0 ? 0 : item.count / selectedCount, locale)}`}
685
+ aria-label={`${item.label}: ${item.count}, ${formatPercent(selectedCount === null || selectedCount === 0 ? 0 : item.count / selectedCount, locale)}`}
368
686
  className="operations-legend-row operations-status-filter"
369
687
  key={item.key}
370
- onClick={() =>
371
- setSelectedRisk(item.key as OperationsProjection["tasks"][number]["risk"])
372
- }
688
+ onClick={() => {
689
+ const classification = item.key as OverviewClassification;
690
+ setSelectedClassification(classification);
691
+ onLoadClassificationPage?.(
692
+ overviewView === "tasks" ? "task" : overviewView === "specs" ? "spec" : "prd",
693
+ classification,
694
+ 0,
695
+ );
696
+ }}
373
697
  type="button"
374
698
  >
375
699
  {content}
@@ -404,7 +728,7 @@ export function OperationsOverview({
404
728
  </article>
405
729
 
406
730
  <article className="operations-panel">
407
- <PanelHeader hint={text.topHint} title={copy.topTitle} />
731
+ <PanelHeader hint={topHint} title={copy.topTitle} />
408
732
  {topEntities.length > 0 ? (
409
733
  <>
410
734
  <div className="operations-run-bars">
@@ -419,14 +743,10 @@ export function OperationsOverview({
419
743
  disabled={overviewView !== "tasks"}
420
744
  key={entity.id}
421
745
  onClick={() => void onOpenTask(entity.id)}
422
- title={`${entity.title}: ${formatDurationMs(entity.durationMs, locale)}`}
746
+ title={`${entity.title}: ${formatEntityDuration(entity.durationMs, locale)}`}
423
747
  type="button"
424
748
  >
425
- <strong>
426
- {entity.durationMs == null
427
- ? "—"
428
- : formatDurationMs(entity.durationMs, locale)}
429
- </strong>
749
+ <strong>{formatEntityDuration(entity.durationMs, locale)}</strong>
430
750
  <i
431
751
  style={{
432
752
  height: `${Math.max(8, ((entity.durationMs ?? 0) / maxDuration) * 100)}%`,
@@ -439,7 +759,7 @@ export function OperationsOverview({
439
759
  <div className="operations-top-task-metrics">
440
760
  <div>
441
761
  <span>{text.accumulatedTime}</span>
442
- <strong>{formatDurationMs(topEntitiesSummary.durationMs, locale)}</strong>
762
+ <strong>{formatEntityDuration(topEntitiesSummary.durationMs, locale)}</strong>
443
763
  <small>{`${topEntitiesSummary.runCount} ${text.associatedRuns}`}</small>
444
764
  </div>
445
765
  <div>
@@ -449,7 +769,11 @@ export function OperationsOverview({
449
769
  </div>
450
770
  <div>
451
771
  <span>{text.topFiveCost}</span>
452
- <strong>{formatCurrency(topEntitiesSummary.costBrl, "BRL", locale)}</strong>
772
+ <strong>
773
+ {topEntitiesCost === null
774
+ ? formatEntityDuration(null, locale)
775
+ : formatCurrency(topEntitiesCost, currency, locale)}
776
+ </strong>
453
777
  <small>
454
778
  {topEntitiesSummary.tokens === null
455
779
  ? text.tokensUnavailable
@@ -464,8 +788,16 @@ export function OperationsOverview({
464
788
  </article>
465
789
  </div>
466
790
  {selectedStatus === null ? null : (
467
- <FilteredTasksDialog
468
- eyebrow={locale === "pt-BR" ? "Status das tarefas" : "Task status"}
791
+ <FilteredStatusDialog
792
+ eyebrow={
793
+ locale === "pt-BR"
794
+ ? overviewView === "tasks"
795
+ ? "Status das tarefas"
796
+ : overviewView === "specs"
797
+ ? "Status das SPECs"
798
+ : "Status dos PRDs"
799
+ : `${copy.shortPlural} status`
800
+ }
469
801
  label={workStageLabel(selectedStatus, locale)}
470
802
  locale={locale}
471
803
  onClose={() => setSelectedStatus(null)}
@@ -473,49 +805,103 @@ export function OperationsOverview({
473
805
  setSelectedStatus(null);
474
806
  void onOpenTask(taskId);
475
807
  }}
476
- tasks={tasks.filter((task) => taskWorkStage(task) === selectedStatus)}
808
+ onOpenDocument={(kind, id) => {
809
+ setSelectedStatus(null);
810
+ onOpenDocument(kind, id);
811
+ }}
812
+ items={
813
+ statusPage?.(
814
+ selectedStatus,
815
+ overviewView === "tasks" ? "task" : overviewView === "specs" ? "spec" : "prd",
816
+ )?.items ?? []
817
+ }
818
+ kind={overviewView === "tasks" ? "task" : overviewView === "specs" ? "spec" : "prd"}
819
+ onLoadMore={(offset) =>
820
+ onLoadStatusPage?.(
821
+ selectedStatus,
822
+ overviewView === "tasks" ? "task" : overviewView === "specs" ? "spec" : "prd",
823
+ offset,
824
+ )
825
+ }
826
+ page={statusPage?.(
827
+ selectedStatus,
828
+ overviewView === "tasks" ? "task" : overviewView === "specs" ? "spec" : "prd",
829
+ )}
477
830
  />
478
831
  )}
479
- {selectedRisk === null ? null : (
480
- <FilteredTasksDialog
481
- eyebrow={locale === "pt-BR" ? "Risco das tarefas" : "Task risk"}
482
- label={riskLabel(selectedRisk, locale)}
832
+ {selectedClassification === null ? null : (
833
+ <FilteredStatusDialog
834
+ eyebrow={locale === "pt-BR" ? copy.classificationTitle : "Classification"}
835
+ label={
836
+ overviewView === "tasks"
837
+ ? riskLabel(
838
+ selectedClassification as OperationsProjection["tasks"][number]["risk"],
839
+ locale,
840
+ )
841
+ : (classificationStats.find((item) => item.key === selectedClassification)?.label ??
842
+ selectedClassification)
843
+ }
483
844
  locale={locale}
484
- onClose={() => setSelectedRisk(null)}
845
+ onClose={() => setSelectedClassification(null)}
485
846
  onOpenTask={(taskId) => {
486
- setSelectedRisk(null);
847
+ setSelectedClassification(null);
487
848
  void onOpenTask(taskId);
488
849
  }}
489
- tasks={tasks.filter((task) => task.risk === selectedRisk)}
850
+ onOpenDocument={(kind, id) => {
851
+ setSelectedClassification(null);
852
+ onOpenDocument(kind, id);
853
+ }}
854
+ items={
855
+ classificationPage?.(
856
+ overviewView === "tasks" ? "task" : overviewView === "specs" ? "spec" : "prd",
857
+ selectedClassification,
858
+ )?.items ?? []
859
+ }
860
+ kind={overviewView === "tasks" ? "task" : overviewView === "specs" ? "spec" : "prd"}
861
+ onLoadMore={(offset) =>
862
+ onLoadClassificationPage?.(
863
+ overviewView === "tasks" ? "task" : overviewView === "specs" ? "spec" : "prd",
864
+ selectedClassification,
865
+ offset,
866
+ )
867
+ }
868
+ page={classificationPage?.(
869
+ overviewView === "tasks" ? "task" : overviewView === "specs" ? "spec" : "prd",
870
+ selectedClassification,
871
+ )}
490
872
  />
491
873
  )}
492
874
  </section>
493
875
  );
494
876
  }
495
877
 
496
- function FilteredTasksDialog({
878
+ function FilteredStatusDialog({
497
879
  eyebrow,
498
880
  label,
499
881
  locale,
500
882
  onClose,
501
883
  onOpenTask,
502
- tasks,
884
+ onOpenDocument,
885
+ items,
886
+ state,
887
+ kind = "task",
888
+ onLoadMore,
889
+ page,
503
890
  }: {
504
891
  eyebrow: string;
505
892
  label: string;
506
893
  locale: OperationsLocale;
507
894
  onClose: () => void;
508
895
  onOpenTask: (taskId: string) => void;
509
- tasks: OperationsProjection["tasks"];
896
+ onOpenDocument: (kind: "prd" | "spec", id: string) => void;
897
+ items: readonly (DashboardTaskProjection | MacroProjection)[];
898
+ state?: { loading: boolean; error: string | null } | undefined;
899
+ kind?: OverviewStatusKind;
900
+ onLoadMore?: ((offset: number) => void) | undefined;
901
+ page?: OverviewStatusPage | undefined;
510
902
  }) {
511
- const taskLabel =
512
- locale === "pt-BR"
513
- ? tasks.length === 1
514
- ? "tarefa"
515
- : "tarefas"
516
- : tasks.length === 1
517
- ? "task"
518
- : "tasks";
903
+ const total = page?.total ?? items.length;
904
+ const taskLabel = statusTypeLabel(kind, total, locale);
519
905
  return (
520
906
  <dialog
521
907
  className="operations-status-dialog-backdrop"
@@ -525,7 +911,7 @@ function FilteredTasksDialog({
525
911
  open
526
912
  >
527
913
  <section
528
- aria-label={`${label} — ${tasks.length} ${taskLabel}`}
914
+ aria-label={`${label} — ${total} ${taskLabel}`}
529
915
  aria-modal="true"
530
916
  className="operations-status-dialog"
531
917
  role="dialog"
@@ -535,7 +921,7 @@ function FilteredTasksDialog({
535
921
  <span>{eyebrow}</span>
536
922
  <h2>{label}</h2>
537
923
  <p>
538
- {tasks.length} {taskLabel}
924
+ {total} {taskLabel}
539
925
  </p>
540
926
  </div>
541
927
  <button
@@ -547,31 +933,76 @@ function FilteredTasksDialog({
547
933
  </button>
548
934
  </header>
549
935
  <div className="operations-status-task-list">
550
- {tasks.map((task) => (
551
- <button key={task.id} onClick={() => onOpenTask(task.id)} type="button">
936
+ {(page?.loading ?? state?.loading) && items.length === 0 ? (
937
+ <p aria-busy="true" aria-live="polite" className="operations-empty" role="status">
938
+ {locale === "pt-BR" ? "Carregando itens\u2026" : "Loading items\u2026"}
939
+ </p>
940
+ ) : null}
941
+ {(page?.error ?? state?.error) ? (
942
+ <p aria-live="assertive" className="operations-empty" role="alert">
943
+ {page?.error ?? state?.error}
944
+ </p>
945
+ ) : null}
946
+ {items.map((item) => (
947
+ <button
948
+ key={item.id}
949
+ onClick={() =>
950
+ "change_id" in item ? onOpenTask(item.id) : onOpenDocument(item.kind, item.id)
951
+ }
952
+ type="button"
953
+ >
552
954
  <span className="operations-status-task-main">
553
- <strong>{task.title}</strong>
554
- <small>{task.id}</small>
955
+ <strong>{item.title}</strong>
956
+ <small>{item.id}</small>
555
957
  </span>
556
- <span className={`operations-risk is-${task.risk}`}>
557
- {riskLabel(task.risk, locale)}
958
+ <span
959
+ className={
960
+ "change_id" in item
961
+ ? `operations-risk is-${item.risk}`
962
+ : "operations-work-kind is-macro"
963
+ }
964
+ >
965
+ {"change_id" in item ? riskLabel(item.risk, locale) : item.kind.toUpperCase()}
558
966
  </span>
559
967
  <span className="operations-status-task-action">
560
968
  {locale === "pt-BR" ? "Ver detalhes" : "View details"} →
561
969
  </span>
562
970
  </button>
563
971
  ))}
564
- {tasks.length === 0 ? (
972
+ {items.length === 0 && !(page?.loading ?? state?.loading) ? (
565
973
  <p className="operations-empty">
566
- {locale === "pt-BR" ? "Nenhuma tarefa neste status." : "No tasks in this status."}
974
+ {locale === "pt-BR"
975
+ ? `Nenhum${kind === "task" ? "a" : ""} ${statusTypeLabel(kind, 1, locale)} neste status.`
976
+ : `No ${statusTypeLabel(kind, 2, locale)} in this status.`}
567
977
  </p>
568
978
  ) : null}
979
+ {onLoadMore && items.length < (page?.total ?? 0) ? (
980
+ <button disabled={page?.loading} onClick={() => onLoadMore(items.length)} type="button">
981
+ {page?.loading
982
+ ? locale === "pt-BR"
983
+ ? "Carregando…"
984
+ : "Loading…"
985
+ : locale === "pt-BR"
986
+ ? "Carregar mais"
987
+ : "Load more"}
988
+ </button>
989
+ ) : null}
569
990
  </div>
570
991
  </section>
571
992
  </dialog>
572
993
  );
573
994
  }
574
995
 
996
+ function statusTypeLabel(
997
+ kind: OverviewStatusKind,
998
+ count: number,
999
+ locale: OperationsLocale,
1000
+ ): string {
1001
+ if (kind === "prd") return count === 1 ? "PRD" : "PRDs";
1002
+ if (kind === "spec") return count === 1 ? "SPEC" : "SPECs";
1003
+ return locale === "pt-BR" ? (count === 1 ? "tarefa" : "tarefas") : count === 1 ? "task" : "tasks";
1004
+ }
1005
+
575
1006
  function PanelHeader({ title, hint, action }: { title: string; hint: string; action?: ReactNode }) {
576
1007
  return (
577
1008
  <header className="operations-panel-header">