@zaip-ai/cli 1.0.7 → 1.0.8

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